blob: 97dec81950e5dead2f5eef76a514f7beb18f7ee0 [file] [log] [blame]
Quentin Monnet00b8a5f2021-11-12 00:17:34 +00001// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/* Copyright (C) 2020 Facebook */
3
4#include <errno.h>
Mauricio Vásquez8ce7ed02022-01-07 10:26:20 -05005#include <linux/err.h>
Quentin Monnet00b8a5f2021-11-12 00:17:34 +00006#include <net/if.h>
7#include <stdio.h>
8#include <unistd.h>
9
10#include <bpf/bpf.h>
11#include <bpf/hashmap.h>
12
13#include "json_writer.h"
14#include "main.h"
15
16static const char * const link_type_name[] = {
17 [BPF_LINK_TYPE_UNSPEC] = "unspec",
18 [BPF_LINK_TYPE_RAW_TRACEPOINT] = "raw_tracepoint",
19 [BPF_LINK_TYPE_TRACING] = "tracing",
20 [BPF_LINK_TYPE_CGROUP] = "cgroup",
21 [BPF_LINK_TYPE_ITER] = "iter",
22 [BPF_LINK_TYPE_NETNS] = "netns",
23};
24
25static struct hashmap *link_table;
26
27static int link_parse_fd(int *argc, char ***argv)
28{
29 int fd;
30
31 if (is_prefix(**argv, "id")) {
32 unsigned int id;
33 char *endptr;
34
35 NEXT_ARGP();
36
37 id = strtoul(**argv, &endptr, 0);
38 if (*endptr) {
39 p_err("can't parse %s as ID", **argv);
40 return -1;
41 }
42 NEXT_ARGP();
43
44 fd = bpf_link_get_fd_by_id(id);
45 if (fd < 0)
46 p_err("failed to get link with ID %d: %s", id, strerror(errno));
47 return fd;
48 } else if (is_prefix(**argv, "pinned")) {
49 char *path;
50
51 NEXT_ARGP();
52
53 path = **argv;
54 NEXT_ARGP();
55
56 return open_obj_pinned_any(path, BPF_OBJ_LINK);
57 }
58
59 p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
60 return -1;
61}
62
63static void
64show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)
65{
66 jsonw_uint_field(wtr, "id", info->id);
67 if (info->type < ARRAY_SIZE(link_type_name))
68 jsonw_string_field(wtr, "type", link_type_name[info->type]);
69 else
70 jsonw_uint_field(wtr, "type", info->type);
71
72 jsonw_uint_field(json_wtr, "prog_id", info->prog_id);
73}
74
75static void show_link_attach_type_json(__u32 attach_type, json_writer_t *wtr)
76{
77 if (attach_type < ARRAY_SIZE(attach_type_name))
78 jsonw_string_field(wtr, "attach_type",
79 attach_type_name[attach_type]);
80 else
81 jsonw_uint_field(wtr, "attach_type", attach_type);
82}
83
84static bool is_iter_map_target(const char *target_name)
85{
86 return strcmp(target_name, "bpf_map_elem") == 0 ||
87 strcmp(target_name, "bpf_sk_storage_map") == 0;
88}
89
90static void show_iter_json(struct bpf_link_info *info, json_writer_t *wtr)
91{
92 const char *target_name = u64_to_ptr(info->iter.target_name);
93
94 jsonw_string_field(wtr, "target_name", target_name);
95
96 if (is_iter_map_target(target_name))
97 jsonw_uint_field(wtr, "map_id", info->iter.map.map_id);
98}
99
100static int get_prog_info(int prog_id, struct bpf_prog_info *info)
101{
102 __u32 len = sizeof(*info);
103 int err, prog_fd;
104
105 prog_fd = bpf_prog_get_fd_by_id(prog_id);
106 if (prog_fd < 0)
107 return prog_fd;
108
109 memset(info, 0, sizeof(*info));
110 err = bpf_obj_get_info_by_fd(prog_fd, info, &len);
111 if (err)
112 p_err("can't get prog info: %s", strerror(errno));
113 close(prog_fd);
114 return err;
115}
116
117static int show_link_close_json(int fd, struct bpf_link_info *info)
118{
119 struct bpf_prog_info prog_info;
120 int err;
121
122 jsonw_start_object(json_wtr);
123
124 show_link_header_json(info, json_wtr);
125
126 switch (info->type) {
127 case BPF_LINK_TYPE_RAW_TRACEPOINT:
128 jsonw_string_field(json_wtr, "tp_name",
129 u64_to_ptr(info->raw_tracepoint.tp_name));
130 break;
131 case BPF_LINK_TYPE_TRACING:
132 err = get_prog_info(info->prog_id, &prog_info);
133 if (err)
134 return err;
135
136 if (prog_info.type < prog_type_name_size)
137 jsonw_string_field(json_wtr, "prog_type",
138 prog_type_name[prog_info.type]);
139 else
140 jsonw_uint_field(json_wtr, "prog_type",
141 prog_info.type);
142
143 show_link_attach_type_json(info->tracing.attach_type,
144 json_wtr);
145 break;
146 case BPF_LINK_TYPE_CGROUP:
147 jsonw_lluint_field(json_wtr, "cgroup_id",
148 info->cgroup.cgroup_id);
149 show_link_attach_type_json(info->cgroup.attach_type, json_wtr);
150 break;
151 case BPF_LINK_TYPE_ITER:
152 show_iter_json(info, json_wtr);
153 break;
154 case BPF_LINK_TYPE_NETNS:
155 jsonw_uint_field(json_wtr, "netns_ino",
156 info->netns.netns_ino);
157 show_link_attach_type_json(info->netns.attach_type, json_wtr);
158 break;
159 default:
160 break;
161 }
162
163 if (!hashmap__empty(link_table)) {
164 struct hashmap_entry *entry;
165
166 jsonw_name(json_wtr, "pinned");
167 jsonw_start_array(json_wtr);
168 hashmap__for_each_key_entry(link_table, entry,
169 u32_as_hash_field(info->id))
170 jsonw_string(json_wtr, entry->value);
171 jsonw_end_array(json_wtr);
172 }
173
174 emit_obj_refs_json(refs_table, info->id, json_wtr);
175
176 jsonw_end_object(json_wtr);
177
178 return 0;
179}
180
181static void show_link_header_plain(struct bpf_link_info *info)
182{
183 printf("%u: ", info->id);
184 if (info->type < ARRAY_SIZE(link_type_name))
185 printf("%s ", link_type_name[info->type]);
186 else
187 printf("type %u ", info->type);
188
189 printf("prog %u ", info->prog_id);
190}
191
192static void show_link_attach_type_plain(__u32 attach_type)
193{
194 if (attach_type < ARRAY_SIZE(attach_type_name))
195 printf("attach_type %s ", attach_type_name[attach_type]);
196 else
197 printf("attach_type %u ", attach_type);
198}
199
200static void show_iter_plain(struct bpf_link_info *info)
201{
202 const char *target_name = u64_to_ptr(info->iter.target_name);
203
204 printf("target_name %s ", target_name);
205
206 if (is_iter_map_target(target_name))
207 printf("map_id %u ", info->iter.map.map_id);
208}
209
210static int show_link_close_plain(int fd, struct bpf_link_info *info)
211{
212 struct bpf_prog_info prog_info;
213 int err;
214
215 show_link_header_plain(info);
216
217 switch (info->type) {
218 case BPF_LINK_TYPE_RAW_TRACEPOINT:
219 printf("\n\ttp '%s' ",
220 (const char *)u64_to_ptr(info->raw_tracepoint.tp_name));
221 break;
222 case BPF_LINK_TYPE_TRACING:
223 err = get_prog_info(info->prog_id, &prog_info);
224 if (err)
225 return err;
226
227 if (prog_info.type < prog_type_name_size)
228 printf("\n\tprog_type %s ",
229 prog_type_name[prog_info.type]);
230 else
231 printf("\n\tprog_type %u ", prog_info.type);
232
233 show_link_attach_type_plain(info->tracing.attach_type);
234 break;
235 case BPF_LINK_TYPE_CGROUP:
236 printf("\n\tcgroup_id %zu ", (size_t)info->cgroup.cgroup_id);
237 show_link_attach_type_plain(info->cgroup.attach_type);
238 break;
239 case BPF_LINK_TYPE_ITER:
240 show_iter_plain(info);
241 break;
242 case BPF_LINK_TYPE_NETNS:
243 printf("\n\tnetns_ino %u ", info->netns.netns_ino);
244 show_link_attach_type_plain(info->netns.attach_type);
245 break;
246 default:
247 break;
248 }
249
250 if (!hashmap__empty(link_table)) {
251 struct hashmap_entry *entry;
252
253 hashmap__for_each_key_entry(link_table, entry,
254 u32_as_hash_field(info->id))
255 printf("\n\tpinned %s", (char *)entry->value);
256 }
257 emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
258
259 printf("\n");
260
261 return 0;
262}
263
264static int do_show_link(int fd)
265{
266 struct bpf_link_info info;
267 __u32 len = sizeof(info);
268 char buf[256];
269 int err;
270
271 memset(&info, 0, sizeof(info));
272again:
273 err = bpf_obj_get_info_by_fd(fd, &info, &len);
274 if (err) {
275 p_err("can't get link info: %s",
276 strerror(errno));
277 close(fd);
278 return err;
279 }
280 if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&
281 !info.raw_tracepoint.tp_name) {
282 info.raw_tracepoint.tp_name = (unsigned long)&buf;
283 info.raw_tracepoint.tp_name_len = sizeof(buf);
284 goto again;
285 }
286 if (info.type == BPF_LINK_TYPE_ITER &&
287 !info.iter.target_name) {
288 info.iter.target_name = (unsigned long)&buf;
289 info.iter.target_name_len = sizeof(buf);
290 goto again;
291 }
292
293 if (json_output)
294 show_link_close_json(fd, &info);
295 else
296 show_link_close_plain(fd, &info);
297
298 close(fd);
299 return 0;
300}
301
302static int do_show(int argc, char **argv)
303{
304 __u32 id = 0;
305 int err, fd;
306
307 if (show_pinned) {
308 link_table = hashmap__new(hash_fn_for_key_as_id,
309 equal_fn_for_key_as_id, NULL);
Mauricio Vásquez8ce7ed02022-01-07 10:26:20 -0500310 if (IS_ERR(link_table)) {
Quentin Monnet00b8a5f2021-11-12 00:17:34 +0000311 p_err("failed to create hashmap for pinned paths");
312 return -1;
313 }
314 build_pinned_obj_table(link_table, BPF_OBJ_LINK);
315 }
316 build_obj_refs_table(&refs_table, BPF_OBJ_LINK);
317
318 if (argc == 2) {
319 fd = link_parse_fd(&argc, &argv);
320 if (fd < 0)
321 return fd;
322 return do_show_link(fd);
323 }
324
325 if (argc)
326 return BAD_ARG();
327
328 if (json_output)
329 jsonw_start_array(json_wtr);
330 while (true) {
331 err = bpf_link_get_next_id(id, &id);
332 if (err) {
333 if (errno == ENOENT)
334 break;
335 p_err("can't get next link: %s%s", strerror(errno),
336 errno == EINVAL ? " -- kernel too old?" : "");
337 break;
338 }
339
340 fd = bpf_link_get_fd_by_id(id);
341 if (fd < 0) {
342 if (errno == ENOENT)
343 continue;
344 p_err("can't get link by id (%u): %s",
345 id, strerror(errno));
346 break;
347 }
348
349 err = do_show_link(fd);
350 if (err)
351 break;
352 }
353 if (json_output)
354 jsonw_end_array(json_wtr);
355
356 delete_obj_refs_table(refs_table);
357
358 if (show_pinned)
359 delete_pinned_obj_table(link_table);
360
361 return errno == ENOENT ? 0 : -1;
362}
363
364static int do_pin(int argc, char **argv)
365{
366 int err;
367
368 err = do_pin_any(argc, argv, link_parse_fd);
369 if (!err && json_output)
370 jsonw_null(json_wtr);
371 return err;
372}
373
374static int do_detach(int argc, char **argv)
375{
376 int err, fd;
377
378 if (argc != 2) {
379 p_err("link specifier is invalid or missing\n");
380 return 1;
381 }
382
383 fd = link_parse_fd(&argc, &argv);
384 if (fd < 0)
385 return 1;
386
387 err = bpf_link_detach(fd);
388 if (err)
389 err = -errno;
390 close(fd);
391 if (err) {
392 p_err("failed link detach: %s", strerror(-err));
393 return 1;
394 }
395
396 if (json_output)
397 jsonw_null(json_wtr);
398
399 return 0;
400}
401
402static int do_help(int argc, char **argv)
403{
404 if (json_output) {
405 jsonw_null(json_wtr);
406 return 0;
407 }
408
409 fprintf(stderr,
410 "Usage: %1$s %2$s { show | list } [LINK]\n"
411 " %1$s %2$s pin LINK FILE\n"
412 " %1$s %2$s detach LINK\n"
413 " %1$s %2$s help\n"
414 "\n"
415 " " HELP_SPEC_LINK "\n"
416 " " HELP_SPEC_OPTIONS " |\n"
417 " {-f|--bpffs} | {-n|--nomount} }\n"
418 "",
419 bin_name, argv[-2]);
420
421 return 0;
422}
423
424static const struct cmd cmds[] = {
425 { "show", do_show },
426 { "list", do_show },
427 { "help", do_help },
428 { "pin", do_pin },
429 { "detach", do_detach },
430 { 0 }
431};
432
433int do_link(int argc, char **argv)
434{
435 return cmd_select(cmds, argc, argv, do_help);
436}