0001
0002
0003
0004 #include <errno.h>
0005 #include <linux/err.h>
0006 #include <net/if.h>
0007 #include <stdio.h>
0008 #include <unistd.h>
0009
0010 #include <bpf/bpf.h>
0011 #include <bpf/hashmap.h>
0012
0013 #include "json_writer.h"
0014 #include "main.h"
0015
0016 static struct hashmap *link_table;
0017
0018 static int link_parse_fd(int *argc, char ***argv)
0019 {
0020 int fd;
0021
0022 if (is_prefix(**argv, "id")) {
0023 unsigned int id;
0024 char *endptr;
0025
0026 NEXT_ARGP();
0027
0028 id = strtoul(**argv, &endptr, 0);
0029 if (*endptr) {
0030 p_err("can't parse %s as ID", **argv);
0031 return -1;
0032 }
0033 NEXT_ARGP();
0034
0035 fd = bpf_link_get_fd_by_id(id);
0036 if (fd < 0)
0037 p_err("failed to get link with ID %d: %s", id, strerror(errno));
0038 return fd;
0039 } else if (is_prefix(**argv, "pinned")) {
0040 char *path;
0041
0042 NEXT_ARGP();
0043
0044 path = **argv;
0045 NEXT_ARGP();
0046
0047 return open_obj_pinned_any(path, BPF_OBJ_LINK);
0048 }
0049
0050 p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
0051 return -1;
0052 }
0053
0054 static void
0055 show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)
0056 {
0057 const char *link_type_str;
0058
0059 jsonw_uint_field(wtr, "id", info->id);
0060 link_type_str = libbpf_bpf_link_type_str(info->type);
0061 if (link_type_str)
0062 jsonw_string_field(wtr, "type", link_type_str);
0063 else
0064 jsonw_uint_field(wtr, "type", info->type);
0065
0066 jsonw_uint_field(json_wtr, "prog_id", info->prog_id);
0067 }
0068
0069 static void show_link_attach_type_json(__u32 attach_type, json_writer_t *wtr)
0070 {
0071 const char *attach_type_str;
0072
0073 attach_type_str = libbpf_bpf_attach_type_str(attach_type);
0074 if (attach_type_str)
0075 jsonw_string_field(wtr, "attach_type", attach_type_str);
0076 else
0077 jsonw_uint_field(wtr, "attach_type", attach_type);
0078 }
0079
0080 static bool is_iter_map_target(const char *target_name)
0081 {
0082 return strcmp(target_name, "bpf_map_elem") == 0 ||
0083 strcmp(target_name, "bpf_sk_storage_map") == 0;
0084 }
0085
0086 static void show_iter_json(struct bpf_link_info *info, json_writer_t *wtr)
0087 {
0088 const char *target_name = u64_to_ptr(info->iter.target_name);
0089
0090 jsonw_string_field(wtr, "target_name", target_name);
0091
0092 if (is_iter_map_target(target_name))
0093 jsonw_uint_field(wtr, "map_id", info->iter.map.map_id);
0094 }
0095
0096 static int get_prog_info(int prog_id, struct bpf_prog_info *info)
0097 {
0098 __u32 len = sizeof(*info);
0099 int err, prog_fd;
0100
0101 prog_fd = bpf_prog_get_fd_by_id(prog_id);
0102 if (prog_fd < 0)
0103 return prog_fd;
0104
0105 memset(info, 0, sizeof(*info));
0106 err = bpf_obj_get_info_by_fd(prog_fd, info, &len);
0107 if (err)
0108 p_err("can't get prog info: %s", strerror(errno));
0109 close(prog_fd);
0110 return err;
0111 }
0112
0113 static int show_link_close_json(int fd, struct bpf_link_info *info)
0114 {
0115 struct bpf_prog_info prog_info;
0116 const char *prog_type_str;
0117 int err;
0118
0119 jsonw_start_object(json_wtr);
0120
0121 show_link_header_json(info, json_wtr);
0122
0123 switch (info->type) {
0124 case BPF_LINK_TYPE_RAW_TRACEPOINT:
0125 jsonw_string_field(json_wtr, "tp_name",
0126 u64_to_ptr(info->raw_tracepoint.tp_name));
0127 break;
0128 case BPF_LINK_TYPE_TRACING:
0129 err = get_prog_info(info->prog_id, &prog_info);
0130 if (err)
0131 return err;
0132
0133 prog_type_str = libbpf_bpf_prog_type_str(prog_info.type);
0134
0135 if (prog_type_str)
0136 jsonw_string_field(json_wtr, "prog_type", prog_type_str);
0137 else
0138 jsonw_uint_field(json_wtr, "prog_type", prog_info.type);
0139
0140 show_link_attach_type_json(info->tracing.attach_type,
0141 json_wtr);
0142 break;
0143 case BPF_LINK_TYPE_CGROUP:
0144 jsonw_lluint_field(json_wtr, "cgroup_id",
0145 info->cgroup.cgroup_id);
0146 show_link_attach_type_json(info->cgroup.attach_type, json_wtr);
0147 break;
0148 case BPF_LINK_TYPE_ITER:
0149 show_iter_json(info, json_wtr);
0150 break;
0151 case BPF_LINK_TYPE_NETNS:
0152 jsonw_uint_field(json_wtr, "netns_ino",
0153 info->netns.netns_ino);
0154 show_link_attach_type_json(info->netns.attach_type, json_wtr);
0155 break;
0156 default:
0157 break;
0158 }
0159
0160 if (!hashmap__empty(link_table)) {
0161 struct hashmap_entry *entry;
0162
0163 jsonw_name(json_wtr, "pinned");
0164 jsonw_start_array(json_wtr);
0165 hashmap__for_each_key_entry(link_table, entry,
0166 u32_as_hash_field(info->id))
0167 jsonw_string(json_wtr, entry->value);
0168 jsonw_end_array(json_wtr);
0169 }
0170
0171 emit_obj_refs_json(refs_table, info->id, json_wtr);
0172
0173 jsonw_end_object(json_wtr);
0174
0175 return 0;
0176 }
0177
0178 static void show_link_header_plain(struct bpf_link_info *info)
0179 {
0180 const char *link_type_str;
0181
0182 printf("%u: ", info->id);
0183 link_type_str = libbpf_bpf_link_type_str(info->type);
0184 if (link_type_str)
0185 printf("%s ", link_type_str);
0186 else
0187 printf("type %u ", info->type);
0188
0189 printf("prog %u ", info->prog_id);
0190 }
0191
0192 static void show_link_attach_type_plain(__u32 attach_type)
0193 {
0194 const char *attach_type_str;
0195
0196 attach_type_str = libbpf_bpf_attach_type_str(attach_type);
0197 if (attach_type_str)
0198 printf("attach_type %s ", attach_type_str);
0199 else
0200 printf("attach_type %u ", attach_type);
0201 }
0202
0203 static void show_iter_plain(struct bpf_link_info *info)
0204 {
0205 const char *target_name = u64_to_ptr(info->iter.target_name);
0206
0207 printf("target_name %s ", target_name);
0208
0209 if (is_iter_map_target(target_name))
0210 printf("map_id %u ", info->iter.map.map_id);
0211 }
0212
0213 static int show_link_close_plain(int fd, struct bpf_link_info *info)
0214 {
0215 struct bpf_prog_info prog_info;
0216 const char *prog_type_str;
0217 int err;
0218
0219 show_link_header_plain(info);
0220
0221 switch (info->type) {
0222 case BPF_LINK_TYPE_RAW_TRACEPOINT:
0223 printf("\n\ttp '%s' ",
0224 (const char *)u64_to_ptr(info->raw_tracepoint.tp_name));
0225 break;
0226 case BPF_LINK_TYPE_TRACING:
0227 err = get_prog_info(info->prog_id, &prog_info);
0228 if (err)
0229 return err;
0230
0231 prog_type_str = libbpf_bpf_prog_type_str(prog_info.type);
0232
0233 if (prog_type_str)
0234 printf("\n\tprog_type %s ", prog_type_str);
0235 else
0236 printf("\n\tprog_type %u ", prog_info.type);
0237
0238 show_link_attach_type_plain(info->tracing.attach_type);
0239 break;
0240 case BPF_LINK_TYPE_CGROUP:
0241 printf("\n\tcgroup_id %zu ", (size_t)info->cgroup.cgroup_id);
0242 show_link_attach_type_plain(info->cgroup.attach_type);
0243 break;
0244 case BPF_LINK_TYPE_ITER:
0245 show_iter_plain(info);
0246 break;
0247 case BPF_LINK_TYPE_NETNS:
0248 printf("\n\tnetns_ino %u ", info->netns.netns_ino);
0249 show_link_attach_type_plain(info->netns.attach_type);
0250 break;
0251 default:
0252 break;
0253 }
0254
0255 if (!hashmap__empty(link_table)) {
0256 struct hashmap_entry *entry;
0257
0258 hashmap__for_each_key_entry(link_table, entry,
0259 u32_as_hash_field(info->id))
0260 printf("\n\tpinned %s", (char *)entry->value);
0261 }
0262 emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
0263
0264 printf("\n");
0265
0266 return 0;
0267 }
0268
0269 static int do_show_link(int fd)
0270 {
0271 struct bpf_link_info info;
0272 __u32 len = sizeof(info);
0273 char buf[256];
0274 int err;
0275
0276 memset(&info, 0, sizeof(info));
0277 again:
0278 err = bpf_obj_get_info_by_fd(fd, &info, &len);
0279 if (err) {
0280 p_err("can't get link info: %s",
0281 strerror(errno));
0282 close(fd);
0283 return err;
0284 }
0285 if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&
0286 !info.raw_tracepoint.tp_name) {
0287 info.raw_tracepoint.tp_name = (unsigned long)&buf;
0288 info.raw_tracepoint.tp_name_len = sizeof(buf);
0289 goto again;
0290 }
0291 if (info.type == BPF_LINK_TYPE_ITER &&
0292 !info.iter.target_name) {
0293 info.iter.target_name = (unsigned long)&buf;
0294 info.iter.target_name_len = sizeof(buf);
0295 goto again;
0296 }
0297
0298 if (json_output)
0299 show_link_close_json(fd, &info);
0300 else
0301 show_link_close_plain(fd, &info);
0302
0303 close(fd);
0304 return 0;
0305 }
0306
0307 static int do_show(int argc, char **argv)
0308 {
0309 __u32 id = 0;
0310 int err, fd;
0311
0312 if (show_pinned) {
0313 link_table = hashmap__new(hash_fn_for_key_as_id,
0314 equal_fn_for_key_as_id, NULL);
0315 if (IS_ERR(link_table)) {
0316 p_err("failed to create hashmap for pinned paths");
0317 return -1;
0318 }
0319 build_pinned_obj_table(link_table, BPF_OBJ_LINK);
0320 }
0321 build_obj_refs_table(&refs_table, BPF_OBJ_LINK);
0322
0323 if (argc == 2) {
0324 fd = link_parse_fd(&argc, &argv);
0325 if (fd < 0)
0326 return fd;
0327 return do_show_link(fd);
0328 }
0329
0330 if (argc)
0331 return BAD_ARG();
0332
0333 if (json_output)
0334 jsonw_start_array(json_wtr);
0335 while (true) {
0336 err = bpf_link_get_next_id(id, &id);
0337 if (err) {
0338 if (errno == ENOENT)
0339 break;
0340 p_err("can't get next link: %s%s", strerror(errno),
0341 errno == EINVAL ? " -- kernel too old?" : "");
0342 break;
0343 }
0344
0345 fd = bpf_link_get_fd_by_id(id);
0346 if (fd < 0) {
0347 if (errno == ENOENT)
0348 continue;
0349 p_err("can't get link by id (%u): %s",
0350 id, strerror(errno));
0351 break;
0352 }
0353
0354 err = do_show_link(fd);
0355 if (err)
0356 break;
0357 }
0358 if (json_output)
0359 jsonw_end_array(json_wtr);
0360
0361 delete_obj_refs_table(refs_table);
0362
0363 if (show_pinned)
0364 delete_pinned_obj_table(link_table);
0365
0366 return errno == ENOENT ? 0 : -1;
0367 }
0368
0369 static int do_pin(int argc, char **argv)
0370 {
0371 int err;
0372
0373 err = do_pin_any(argc, argv, link_parse_fd);
0374 if (!err && json_output)
0375 jsonw_null(json_wtr);
0376 return err;
0377 }
0378
0379 static int do_detach(int argc, char **argv)
0380 {
0381 int err, fd;
0382
0383 if (argc != 2) {
0384 p_err("link specifier is invalid or missing\n");
0385 return 1;
0386 }
0387
0388 fd = link_parse_fd(&argc, &argv);
0389 if (fd < 0)
0390 return 1;
0391
0392 err = bpf_link_detach(fd);
0393 if (err)
0394 err = -errno;
0395 close(fd);
0396 if (err) {
0397 p_err("failed link detach: %s", strerror(-err));
0398 return 1;
0399 }
0400
0401 if (json_output)
0402 jsonw_null(json_wtr);
0403
0404 return 0;
0405 }
0406
0407 static int do_help(int argc, char **argv)
0408 {
0409 if (json_output) {
0410 jsonw_null(json_wtr);
0411 return 0;
0412 }
0413
0414 fprintf(stderr,
0415 "Usage: %1$s %2$s { show | list } [LINK]\n"
0416 " %1$s %2$s pin LINK FILE\n"
0417 " %1$s %2$s detach LINK\n"
0418 " %1$s %2$s help\n"
0419 "\n"
0420 " " HELP_SPEC_LINK "\n"
0421 " " HELP_SPEC_OPTIONS " |\n"
0422 " {-f|--bpffs} | {-n|--nomount} }\n"
0423 "",
0424 bin_name, argv[-2]);
0425
0426 return 0;
0427 }
0428
0429 static const struct cmd cmds[] = {
0430 { "show", do_show },
0431 { "list", do_show },
0432 { "help", do_help },
0433 { "pin", do_pin },
0434 { "detach", do_detach },
0435 { 0 }
0436 };
0437
0438 int do_link(int argc, char **argv)
0439 {
0440 return cmd_select(cmds, argc, argv, do_help);
0441 }