]> Git Repo - linux.git/blob - tools/bpf/bpftool/link.c
Merge tag 'amd-drm-next-6.5-2023-06-09' of https://gitlab.freedesktop.org/agd5f/linux...
[linux.git] / tools / bpf / bpftool / link.c
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 <linux/netfilter.h>
7 #include <linux/netfilter_arp.h>
8 #include <net/if.h>
9 #include <stdio.h>
10 #include <unistd.h>
11
12 #include <bpf/bpf.h>
13 #include <bpf/hashmap.h>
14
15 #include "json_writer.h"
16 #include "main.h"
17
18 static struct hashmap *link_table;
19
20 static int link_parse_fd(int *argc, char ***argv)
21 {
22         int fd;
23
24         if (is_prefix(**argv, "id")) {
25                 unsigned int id;
26                 char *endptr;
27
28                 NEXT_ARGP();
29
30                 id = strtoul(**argv, &endptr, 0);
31                 if (*endptr) {
32                         p_err("can't parse %s as ID", **argv);
33                         return -1;
34                 }
35                 NEXT_ARGP();
36
37                 fd = bpf_link_get_fd_by_id(id);
38                 if (fd < 0)
39                         p_err("failed to get link with ID %d: %s", id, strerror(errno));
40                 return fd;
41         } else if (is_prefix(**argv, "pinned")) {
42                 char *path;
43
44                 NEXT_ARGP();
45
46                 path = **argv;
47                 NEXT_ARGP();
48
49                 return open_obj_pinned_any(path, BPF_OBJ_LINK);
50         }
51
52         p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
53         return -1;
54 }
55
56 static void
57 show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)
58 {
59         const char *link_type_str;
60
61         jsonw_uint_field(wtr, "id", info->id);
62         link_type_str = libbpf_bpf_link_type_str(info->type);
63         if (link_type_str)
64                 jsonw_string_field(wtr, "type", link_type_str);
65         else
66                 jsonw_uint_field(wtr, "type", info->type);
67
68         jsonw_uint_field(json_wtr, "prog_id", info->prog_id);
69 }
70
71 static void show_link_attach_type_json(__u32 attach_type, json_writer_t *wtr)
72 {
73         const char *attach_type_str;
74
75         attach_type_str = libbpf_bpf_attach_type_str(attach_type);
76         if (attach_type_str)
77                 jsonw_string_field(wtr, "attach_type", attach_type_str);
78         else
79                 jsonw_uint_field(wtr, "attach_type", attach_type);
80 }
81
82 static bool is_iter_map_target(const char *target_name)
83 {
84         return strcmp(target_name, "bpf_map_elem") == 0 ||
85                strcmp(target_name, "bpf_sk_storage_map") == 0;
86 }
87
88 static bool is_iter_cgroup_target(const char *target_name)
89 {
90         return strcmp(target_name, "cgroup") == 0;
91 }
92
93 static const char *cgroup_order_string(__u32 order)
94 {
95         switch (order) {
96         case BPF_CGROUP_ITER_ORDER_UNSPEC:
97                 return "order_unspec";
98         case BPF_CGROUP_ITER_SELF_ONLY:
99                 return "self_only";
100         case BPF_CGROUP_ITER_DESCENDANTS_PRE:
101                 return "descendants_pre";
102         case BPF_CGROUP_ITER_DESCENDANTS_POST:
103                 return "descendants_post";
104         case BPF_CGROUP_ITER_ANCESTORS_UP:
105                 return "ancestors_up";
106         default: /* won't happen */
107                 return "unknown";
108         }
109 }
110
111 static bool is_iter_task_target(const char *target_name)
112 {
113         return strcmp(target_name, "task") == 0 ||
114                 strcmp(target_name, "task_file") == 0 ||
115                 strcmp(target_name, "task_vma") == 0;
116 }
117
118 static void show_iter_json(struct bpf_link_info *info, json_writer_t *wtr)
119 {
120         const char *target_name = u64_to_ptr(info->iter.target_name);
121
122         jsonw_string_field(wtr, "target_name", target_name);
123
124         if (is_iter_map_target(target_name))
125                 jsonw_uint_field(wtr, "map_id", info->iter.map.map_id);
126         else if (is_iter_task_target(target_name)) {
127                 if (info->iter.task.tid)
128                         jsonw_uint_field(wtr, "tid", info->iter.task.tid);
129                 else if (info->iter.task.pid)
130                         jsonw_uint_field(wtr, "pid", info->iter.task.pid);
131         }
132
133         if (is_iter_cgroup_target(target_name)) {
134                 jsonw_lluint_field(wtr, "cgroup_id", info->iter.cgroup.cgroup_id);
135                 jsonw_string_field(wtr, "order",
136                                    cgroup_order_string(info->iter.cgroup.order));
137         }
138 }
139
140 void netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr)
141 {
142         jsonw_uint_field(json_wtr, "pf",
143                          info->netfilter.pf);
144         jsonw_uint_field(json_wtr, "hook",
145                          info->netfilter.hooknum);
146         jsonw_int_field(json_wtr, "prio",
147                          info->netfilter.priority);
148         jsonw_uint_field(json_wtr, "flags",
149                          info->netfilter.flags);
150 }
151
152 static int get_prog_info(int prog_id, struct bpf_prog_info *info)
153 {
154         __u32 len = sizeof(*info);
155         int err, prog_fd;
156
157         prog_fd = bpf_prog_get_fd_by_id(prog_id);
158         if (prog_fd < 0)
159                 return prog_fd;
160
161         memset(info, 0, sizeof(*info));
162         err = bpf_prog_get_info_by_fd(prog_fd, info, &len);
163         if (err)
164                 p_err("can't get prog info: %s", strerror(errno));
165         close(prog_fd);
166         return err;
167 }
168
169 static int show_link_close_json(int fd, struct bpf_link_info *info)
170 {
171         struct bpf_prog_info prog_info;
172         const char *prog_type_str;
173         int err;
174
175         jsonw_start_object(json_wtr);
176
177         show_link_header_json(info, json_wtr);
178
179         switch (info->type) {
180         case BPF_LINK_TYPE_RAW_TRACEPOINT:
181                 jsonw_string_field(json_wtr, "tp_name",
182                                    u64_to_ptr(info->raw_tracepoint.tp_name));
183                 break;
184         case BPF_LINK_TYPE_TRACING:
185                 err = get_prog_info(info->prog_id, &prog_info);
186                 if (err)
187                         return err;
188
189                 prog_type_str = libbpf_bpf_prog_type_str(prog_info.type);
190                 /* libbpf will return NULL for variants unknown to it. */
191                 if (prog_type_str)
192                         jsonw_string_field(json_wtr, "prog_type", prog_type_str);
193                 else
194                         jsonw_uint_field(json_wtr, "prog_type", prog_info.type);
195
196                 show_link_attach_type_json(info->tracing.attach_type,
197                                            json_wtr);
198                 break;
199         case BPF_LINK_TYPE_CGROUP:
200                 jsonw_lluint_field(json_wtr, "cgroup_id",
201                                    info->cgroup.cgroup_id);
202                 show_link_attach_type_json(info->cgroup.attach_type, json_wtr);
203                 break;
204         case BPF_LINK_TYPE_ITER:
205                 show_iter_json(info, json_wtr);
206                 break;
207         case BPF_LINK_TYPE_NETNS:
208                 jsonw_uint_field(json_wtr, "netns_ino",
209                                  info->netns.netns_ino);
210                 show_link_attach_type_json(info->netns.attach_type, json_wtr);
211                 break;
212         case BPF_LINK_TYPE_NETFILTER:
213                 netfilter_dump_json(info, json_wtr);
214                 break;
215
216         default:
217                 break;
218         }
219
220         if (!hashmap__empty(link_table)) {
221                 struct hashmap_entry *entry;
222
223                 jsonw_name(json_wtr, "pinned");
224                 jsonw_start_array(json_wtr);
225                 hashmap__for_each_key_entry(link_table, entry, info->id)
226                         jsonw_string(json_wtr, entry->pvalue);
227                 jsonw_end_array(json_wtr);
228         }
229
230         emit_obj_refs_json(refs_table, info->id, json_wtr);
231
232         jsonw_end_object(json_wtr);
233
234         return 0;
235 }
236
237 static void show_link_header_plain(struct bpf_link_info *info)
238 {
239         const char *link_type_str;
240
241         printf("%u: ", info->id);
242         link_type_str = libbpf_bpf_link_type_str(info->type);
243         if (link_type_str)
244                 printf("%s  ", link_type_str);
245         else
246                 printf("type %u  ", info->type);
247
248         printf("prog %u  ", info->prog_id);
249 }
250
251 static void show_link_attach_type_plain(__u32 attach_type)
252 {
253         const char *attach_type_str;
254
255         attach_type_str = libbpf_bpf_attach_type_str(attach_type);
256         if (attach_type_str)
257                 printf("attach_type %s  ", attach_type_str);
258         else
259                 printf("attach_type %u  ", attach_type);
260 }
261
262 static void show_iter_plain(struct bpf_link_info *info)
263 {
264         const char *target_name = u64_to_ptr(info->iter.target_name);
265
266         printf("target_name %s  ", target_name);
267
268         if (is_iter_map_target(target_name))
269                 printf("map_id %u  ", info->iter.map.map_id);
270         else if (is_iter_task_target(target_name)) {
271                 if (info->iter.task.tid)
272                         printf("tid %u ", info->iter.task.tid);
273                 else if (info->iter.task.pid)
274                         printf("pid %u ", info->iter.task.pid);
275         }
276
277         if (is_iter_cgroup_target(target_name)) {
278                 printf("cgroup_id %llu  ", info->iter.cgroup.cgroup_id);
279                 printf("order %s  ",
280                        cgroup_order_string(info->iter.cgroup.order));
281         }
282 }
283
284 static const char * const pf2name[] = {
285         [NFPROTO_INET] = "inet",
286         [NFPROTO_IPV4] = "ip",
287         [NFPROTO_ARP] = "arp",
288         [NFPROTO_NETDEV] = "netdev",
289         [NFPROTO_BRIDGE] = "bridge",
290         [NFPROTO_IPV6] = "ip6",
291 };
292
293 static const char * const inethook2name[] = {
294         [NF_INET_PRE_ROUTING] = "prerouting",
295         [NF_INET_LOCAL_IN] = "input",
296         [NF_INET_FORWARD] = "forward",
297         [NF_INET_LOCAL_OUT] = "output",
298         [NF_INET_POST_ROUTING] = "postrouting",
299 };
300
301 static const char * const arphook2name[] = {
302         [NF_ARP_IN] = "input",
303         [NF_ARP_OUT] = "output",
304 };
305
306 void netfilter_dump_plain(const struct bpf_link_info *info)
307 {
308         const char *hookname = NULL, *pfname = NULL;
309         unsigned int hook = info->netfilter.hooknum;
310         unsigned int pf = info->netfilter.pf;
311
312         if (pf < ARRAY_SIZE(pf2name))
313                 pfname = pf2name[pf];
314
315         switch (pf) {
316         case NFPROTO_BRIDGE: /* bridge shares numbers with enum nf_inet_hooks */
317         case NFPROTO_IPV4:
318         case NFPROTO_IPV6:
319         case NFPROTO_INET:
320                 if (hook < ARRAY_SIZE(inethook2name))
321                         hookname = inethook2name[hook];
322                 break;
323         case NFPROTO_ARP:
324                 if (hook < ARRAY_SIZE(arphook2name))
325                         hookname = arphook2name[hook];
326         default:
327                 break;
328         }
329
330         if (pfname)
331                 printf("\n\t%s", pfname);
332         else
333                 printf("\n\tpf: %d", pf);
334
335         if (hookname)
336                 printf(" %s", hookname);
337         else
338                 printf(", hook %u,", hook);
339
340         printf(" prio %d", info->netfilter.priority);
341
342         if (info->netfilter.flags)
343                 printf(" flags 0x%x", info->netfilter.flags);
344 }
345
346 static int show_link_close_plain(int fd, struct bpf_link_info *info)
347 {
348         struct bpf_prog_info prog_info;
349         const char *prog_type_str;
350         int err;
351
352         show_link_header_plain(info);
353
354         switch (info->type) {
355         case BPF_LINK_TYPE_RAW_TRACEPOINT:
356                 printf("\n\ttp '%s'  ",
357                        (const char *)u64_to_ptr(info->raw_tracepoint.tp_name));
358                 break;
359         case BPF_LINK_TYPE_TRACING:
360                 err = get_prog_info(info->prog_id, &prog_info);
361                 if (err)
362                         return err;
363
364                 prog_type_str = libbpf_bpf_prog_type_str(prog_info.type);
365                 /* libbpf will return NULL for variants unknown to it. */
366                 if (prog_type_str)
367                         printf("\n\tprog_type %s  ", prog_type_str);
368                 else
369                         printf("\n\tprog_type %u  ", prog_info.type);
370
371                 show_link_attach_type_plain(info->tracing.attach_type);
372                 break;
373         case BPF_LINK_TYPE_CGROUP:
374                 printf("\n\tcgroup_id %zu  ", (size_t)info->cgroup.cgroup_id);
375                 show_link_attach_type_plain(info->cgroup.attach_type);
376                 break;
377         case BPF_LINK_TYPE_ITER:
378                 show_iter_plain(info);
379                 break;
380         case BPF_LINK_TYPE_NETNS:
381                 printf("\n\tnetns_ino %u  ", info->netns.netns_ino);
382                 show_link_attach_type_plain(info->netns.attach_type);
383                 break;
384         case BPF_LINK_TYPE_NETFILTER:
385                 netfilter_dump_plain(info);
386                 break;
387         default:
388                 break;
389         }
390
391         if (!hashmap__empty(link_table)) {
392                 struct hashmap_entry *entry;
393
394                 hashmap__for_each_key_entry(link_table, entry, info->id)
395                         printf("\n\tpinned %s", (char *)entry->pvalue);
396         }
397         emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
398
399         printf("\n");
400
401         return 0;
402 }
403
404 static int do_show_link(int fd)
405 {
406         struct bpf_link_info info;
407         __u32 len = sizeof(info);
408         char buf[256];
409         int err;
410
411         memset(&info, 0, sizeof(info));
412 again:
413         err = bpf_link_get_info_by_fd(fd, &info, &len);
414         if (err) {
415                 p_err("can't get link info: %s",
416                       strerror(errno));
417                 close(fd);
418                 return err;
419         }
420         if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&
421             !info.raw_tracepoint.tp_name) {
422                 info.raw_tracepoint.tp_name = (unsigned long)&buf;
423                 info.raw_tracepoint.tp_name_len = sizeof(buf);
424                 goto again;
425         }
426         if (info.type == BPF_LINK_TYPE_ITER &&
427             !info.iter.target_name) {
428                 info.iter.target_name = (unsigned long)&buf;
429                 info.iter.target_name_len = sizeof(buf);
430                 goto again;
431         }
432
433         if (json_output)
434                 show_link_close_json(fd, &info);
435         else
436                 show_link_close_plain(fd, &info);
437
438         close(fd);
439         return 0;
440 }
441
442 static int do_show(int argc, char **argv)
443 {
444         __u32 id = 0;
445         int err, fd;
446
447         if (show_pinned) {
448                 link_table = hashmap__new(hash_fn_for_key_as_id,
449                                           equal_fn_for_key_as_id, NULL);
450                 if (IS_ERR(link_table)) {
451                         p_err("failed to create hashmap for pinned paths");
452                         return -1;
453                 }
454                 build_pinned_obj_table(link_table, BPF_OBJ_LINK);
455         }
456         build_obj_refs_table(&refs_table, BPF_OBJ_LINK);
457
458         if (argc == 2) {
459                 fd = link_parse_fd(&argc, &argv);
460                 if (fd < 0)
461                         return fd;
462                 return do_show_link(fd);
463         }
464
465         if (argc)
466                 return BAD_ARG();
467
468         if (json_output)
469                 jsonw_start_array(json_wtr);
470         while (true) {
471                 err = bpf_link_get_next_id(id, &id);
472                 if (err) {
473                         if (errno == ENOENT)
474                                 break;
475                         p_err("can't get next link: %s%s", strerror(errno),
476                               errno == EINVAL ? " -- kernel too old?" : "");
477                         break;
478                 }
479
480                 fd = bpf_link_get_fd_by_id(id);
481                 if (fd < 0) {
482                         if (errno == ENOENT)
483                                 continue;
484                         p_err("can't get link by id (%u): %s",
485                               id, strerror(errno));
486                         break;
487                 }
488
489                 err = do_show_link(fd);
490                 if (err)
491                         break;
492         }
493         if (json_output)
494                 jsonw_end_array(json_wtr);
495
496         delete_obj_refs_table(refs_table);
497
498         if (show_pinned)
499                 delete_pinned_obj_table(link_table);
500
501         return errno == ENOENT ? 0 : -1;
502 }
503
504 static int do_pin(int argc, char **argv)
505 {
506         int err;
507
508         err = do_pin_any(argc, argv, link_parse_fd);
509         if (!err && json_output)
510                 jsonw_null(json_wtr);
511         return err;
512 }
513
514 static int do_detach(int argc, char **argv)
515 {
516         int err, fd;
517
518         if (argc != 2) {
519                 p_err("link specifier is invalid or missing\n");
520                 return 1;
521         }
522
523         fd = link_parse_fd(&argc, &argv);
524         if (fd < 0)
525                 return 1;
526
527         err = bpf_link_detach(fd);
528         if (err)
529                 err = -errno;
530         close(fd);
531         if (err) {
532                 p_err("failed link detach: %s", strerror(-err));
533                 return 1;
534         }
535
536         if (json_output)
537                 jsonw_null(json_wtr);
538
539         return 0;
540 }
541
542 static int do_help(int argc, char **argv)
543 {
544         if (json_output) {
545                 jsonw_null(json_wtr);
546                 return 0;
547         }
548
549         fprintf(stderr,
550                 "Usage: %1$s %2$s { show | list }   [LINK]\n"
551                 "       %1$s %2$s pin        LINK  FILE\n"
552                 "       %1$s %2$s detach     LINK\n"
553                 "       %1$s %2$s help\n"
554                 "\n"
555                 "       " HELP_SPEC_LINK "\n"
556                 "       " HELP_SPEC_OPTIONS " |\n"
557                 "                    {-f|--bpffs} | {-n|--nomount} }\n"
558                 "",
559                 bin_name, argv[-2]);
560
561         return 0;
562 }
563
564 static const struct cmd cmds[] = {
565         { "show",       do_show },
566         { "list",       do_show },
567         { "help",       do_help },
568         { "pin",        do_pin },
569         { "detach",     do_detach },
570         { 0 }
571 };
572
573 int do_link(int argc, char **argv)
574 {
575         return cmd_select(cmds, argc, argv, do_help);
576 }
This page took 0.06644 seconds and 4 git commands to generate.