0001
0002
0003
0004 #include <errno.h>
0005 #include <stdio.h>
0006 #include <unistd.h>
0007
0008 #include <linux/err.h>
0009
0010 #include <bpf/bpf.h>
0011 #include <bpf/btf.h>
0012 #include <bpf/libbpf.h>
0013
0014 #include "json_writer.h"
0015 #include "main.h"
0016
0017 #define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_"
0018
0019 static const struct btf_type *map_info_type;
0020 static __u32 map_info_alloc_len;
0021 static struct btf *btf_vmlinux;
0022 static __s32 map_info_type_id;
0023
0024 struct res {
0025 unsigned int nr_maps;
0026 unsigned int nr_errs;
0027 };
0028
0029 static const struct btf *get_btf_vmlinux(void)
0030 {
0031 if (btf_vmlinux)
0032 return btf_vmlinux;
0033
0034 btf_vmlinux = libbpf_find_kernel_btf();
0035 if (libbpf_get_error(btf_vmlinux))
0036 p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y");
0037
0038 return btf_vmlinux;
0039 }
0040
0041 static const char *get_kern_struct_ops_name(const struct bpf_map_info *info)
0042 {
0043 const struct btf *kern_btf;
0044 const struct btf_type *t;
0045 const char *st_ops_name;
0046
0047 kern_btf = get_btf_vmlinux();
0048 if (libbpf_get_error(kern_btf))
0049 return "<btf_vmlinux_not_found>";
0050
0051 t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id);
0052 st_ops_name = btf__name_by_offset(kern_btf, t->name_off);
0053 st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX);
0054
0055 return st_ops_name;
0056 }
0057
0058 static __s32 get_map_info_type_id(void)
0059 {
0060 const struct btf *kern_btf;
0061
0062 if (map_info_type_id)
0063 return map_info_type_id;
0064
0065 kern_btf = get_btf_vmlinux();
0066 if (libbpf_get_error(kern_btf)) {
0067 map_info_type_id = PTR_ERR(kern_btf);
0068 return map_info_type_id;
0069 }
0070
0071 map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info",
0072 BTF_KIND_STRUCT);
0073 if (map_info_type_id < 0) {
0074 p_err("can't find bpf_map_info from btf_vmlinux");
0075 return map_info_type_id;
0076 }
0077 map_info_type = btf__type_by_id(kern_btf, map_info_type_id);
0078
0079
0080 map_info_alloc_len = map_info_type->size;
0081 if (map_info_alloc_len < sizeof(struct bpf_map_info))
0082 map_info_alloc_len = sizeof(struct bpf_map_info);
0083
0084 return map_info_type_id;
0085 }
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102 static struct bpf_map_info *map_info_alloc(__u32 *alloc_len)
0103 {
0104 struct bpf_map_info *info;
0105
0106 if (get_map_info_type_id() < 0)
0107 return NULL;
0108
0109 info = calloc(1, map_info_alloc_len);
0110 if (!info)
0111 p_err("mem alloc failed");
0112 else
0113 *alloc_len = map_info_alloc_len;
0114
0115 return info;
0116 }
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130
0131
0132 static int get_next_struct_ops_map(const char *name, int *res_fd,
0133 struct bpf_map_info *info, __u32 info_len)
0134 {
0135 __u32 id = info->id;
0136 int err, fd;
0137
0138 while (true) {
0139 err = bpf_map_get_next_id(id, &id);
0140 if (err) {
0141 if (errno == ENOENT)
0142 return 0;
0143 p_err("can't get next map: %s", strerror(errno));
0144 return -1;
0145 }
0146
0147 fd = bpf_map_get_fd_by_id(id);
0148 if (fd < 0) {
0149 if (errno == ENOENT)
0150 continue;
0151 p_err("can't get map by id (%u): %s",
0152 id, strerror(errno));
0153 return -1;
0154 }
0155
0156 err = bpf_obj_get_info_by_fd(fd, info, &info_len);
0157 if (err) {
0158 p_err("can't get map info: %s", strerror(errno));
0159 close(fd);
0160 return -1;
0161 }
0162
0163 if (info->type == BPF_MAP_TYPE_STRUCT_OPS &&
0164 (!name || !strcmp(name, info->name))) {
0165 *res_fd = fd;
0166 return 1;
0167 }
0168 close(fd);
0169 }
0170 }
0171
0172 static int cmd_retval(const struct res *res, bool must_have_one_map)
0173 {
0174 if (res->nr_errs || (!res->nr_maps && must_have_one_map))
0175 return -1;
0176
0177 return 0;
0178 }
0179
0180
0181 typedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data,
0182 struct json_writer *wtr);
0183
0184
0185
0186
0187
0188 static struct res do_search(const char *name, work_func func, void *data,
0189 struct json_writer *wtr)
0190 {
0191 struct bpf_map_info *info;
0192 struct res res = {};
0193 __u32 info_len;
0194 int fd, err;
0195
0196 info = map_info_alloc(&info_len);
0197 if (!info) {
0198 res.nr_errs++;
0199 return res;
0200 }
0201
0202 if (wtr)
0203 jsonw_start_array(wtr);
0204 while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) {
0205 res.nr_maps++;
0206 err = func(fd, info, data, wtr);
0207 if (err)
0208 res.nr_errs++;
0209 close(fd);
0210 }
0211 if (wtr)
0212 jsonw_end_array(wtr);
0213
0214 if (err)
0215 res.nr_errs++;
0216
0217 if (!wtr && name && !res.nr_errs && !res.nr_maps)
0218
0219
0220
0221
0222 p_err("no struct_ops found for %s", name);
0223 else if (!wtr && json_output && !res.nr_errs)
0224
0225
0226
0227
0228
0229
0230
0231 jsonw_null(json_wtr);
0232
0233 free(info);
0234 return res;
0235 }
0236
0237 static struct res do_one_id(const char *id_str, work_func func, void *data,
0238 struct json_writer *wtr)
0239 {
0240 struct bpf_map_info *info;
0241 struct res res = {};
0242 unsigned long id;
0243 __u32 info_len;
0244 char *endptr;
0245 int fd;
0246
0247 id = strtoul(id_str, &endptr, 0);
0248 if (*endptr || !id || id > UINT32_MAX) {
0249 p_err("invalid id %s", id_str);
0250 res.nr_errs++;
0251 return res;
0252 }
0253
0254 fd = bpf_map_get_fd_by_id(id);
0255 if (fd < 0) {
0256 p_err("can't get map by id (%lu): %s", id, strerror(errno));
0257 res.nr_errs++;
0258 return res;
0259 }
0260
0261 info = map_info_alloc(&info_len);
0262 if (!info) {
0263 res.nr_errs++;
0264 goto done;
0265 }
0266
0267 if (bpf_obj_get_info_by_fd(fd, info, &info_len)) {
0268 p_err("can't get map info: %s", strerror(errno));
0269 res.nr_errs++;
0270 goto done;
0271 }
0272
0273 if (info->type != BPF_MAP_TYPE_STRUCT_OPS) {
0274 p_err("%s id %u is not a struct_ops map", info->name, info->id);
0275 res.nr_errs++;
0276 goto done;
0277 }
0278
0279 res.nr_maps++;
0280
0281 if (func(fd, info, data, wtr))
0282 res.nr_errs++;
0283 else if (!wtr && json_output)
0284
0285
0286
0287
0288
0289
0290
0291 jsonw_null(json_wtr);
0292
0293 done:
0294 free(info);
0295 close(fd);
0296
0297 return res;
0298 }
0299
0300 static struct res do_work_on_struct_ops(const char *search_type,
0301 const char *search_term,
0302 work_func func, void *data,
0303 struct json_writer *wtr)
0304 {
0305 if (search_type) {
0306 if (is_prefix(search_type, "id"))
0307 return do_one_id(search_term, func, data, wtr);
0308 else if (!is_prefix(search_type, "name"))
0309 usage();
0310 }
0311
0312 return do_search(search_term, func, data, wtr);
0313 }
0314
0315 static int __do_show(int fd, const struct bpf_map_info *info, void *data,
0316 struct json_writer *wtr)
0317 {
0318 if (wtr) {
0319 jsonw_start_object(wtr);
0320 jsonw_uint_field(wtr, "id", info->id);
0321 jsonw_string_field(wtr, "name", info->name);
0322 jsonw_string_field(wtr, "kernel_struct_ops",
0323 get_kern_struct_ops_name(info));
0324 jsonw_end_object(wtr);
0325 } else {
0326 printf("%u: %-15s %-32s\n", info->id, info->name,
0327 get_kern_struct_ops_name(info));
0328 }
0329
0330 return 0;
0331 }
0332
0333 static int do_show(int argc, char **argv)
0334 {
0335 const char *search_type = NULL, *search_term = NULL;
0336 struct res res;
0337
0338 if (argc && argc != 2)
0339 usage();
0340
0341 if (argc == 2) {
0342 search_type = GET_ARG();
0343 search_term = GET_ARG();
0344 }
0345
0346 res = do_work_on_struct_ops(search_type, search_term, __do_show,
0347 NULL, json_wtr);
0348
0349 return cmd_retval(&res, !!search_term);
0350 }
0351
0352 static int __do_dump(int fd, const struct bpf_map_info *info, void *data,
0353 struct json_writer *wtr)
0354 {
0355 struct btf_dumper *d = (struct btf_dumper *)data;
0356 const struct btf_type *struct_ops_type;
0357 const struct btf *kern_btf = d->btf;
0358 const char *struct_ops_name;
0359 int zero = 0;
0360 void *value;
0361
0362
0363
0364 kern_btf = d->btf;
0365
0366
0367
0368
0369 struct_ops_type = btf__type_by_id(kern_btf,
0370 info->btf_vmlinux_value_type_id);
0371 struct_ops_name = btf__name_by_offset(kern_btf,
0372 struct_ops_type->name_off);
0373 value = calloc(1, info->value_size);
0374 if (!value) {
0375 p_err("mem alloc failed");
0376 return -1;
0377 }
0378
0379 if (bpf_map_lookup_elem(fd, &zero, value)) {
0380 p_err("can't lookup struct_ops map %s id %u",
0381 info->name, info->id);
0382 free(value);
0383 return -1;
0384 }
0385
0386 jsonw_start_object(wtr);
0387 jsonw_name(wtr, "bpf_map_info");
0388 btf_dumper_type(d, map_info_type_id, (void *)info);
0389 jsonw_end_object(wtr);
0390
0391 jsonw_start_object(wtr);
0392 jsonw_name(wtr, struct_ops_name);
0393 btf_dumper_type(d, info->btf_vmlinux_value_type_id, value);
0394 jsonw_end_object(wtr);
0395
0396 free(value);
0397
0398 return 0;
0399 }
0400
0401 static int do_dump(int argc, char **argv)
0402 {
0403 const char *search_type = NULL, *search_term = NULL;
0404 json_writer_t *wtr = json_wtr;
0405 const struct btf *kern_btf;
0406 struct btf_dumper d = {};
0407 struct res res;
0408
0409 if (argc && argc != 2)
0410 usage();
0411
0412 if (argc == 2) {
0413 search_type = GET_ARG();
0414 search_term = GET_ARG();
0415 }
0416
0417 kern_btf = get_btf_vmlinux();
0418 if (libbpf_get_error(kern_btf))
0419 return -1;
0420
0421 if (!json_output) {
0422 wtr = jsonw_new(stdout);
0423 if (!wtr) {
0424 p_err("can't create json writer");
0425 return -1;
0426 }
0427 jsonw_pretty(wtr, true);
0428 }
0429
0430 d.btf = kern_btf;
0431 d.jw = wtr;
0432 d.is_plain_text = !json_output;
0433 d.prog_id_as_func_ptr = true;
0434
0435 res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d,
0436 wtr);
0437
0438 if (!json_output)
0439 jsonw_destroy(&wtr);
0440
0441 return cmd_retval(&res, !!search_term);
0442 }
0443
0444 static int __do_unregister(int fd, const struct bpf_map_info *info, void *data,
0445 struct json_writer *wtr)
0446 {
0447 int zero = 0;
0448
0449 if (bpf_map_delete_elem(fd, &zero)) {
0450 p_err("can't unload %s %s id %u: %s",
0451 get_kern_struct_ops_name(info), info->name,
0452 info->id, strerror(errno));
0453 return -1;
0454 }
0455
0456 p_info("Unregistered %s %s id %u",
0457 get_kern_struct_ops_name(info), info->name,
0458 info->id);
0459
0460 return 0;
0461 }
0462
0463 static int do_unregister(int argc, char **argv)
0464 {
0465 const char *search_type, *search_term;
0466 struct res res;
0467
0468 if (argc != 2)
0469 usage();
0470
0471 search_type = GET_ARG();
0472 search_term = GET_ARG();
0473
0474 res = do_work_on_struct_ops(search_type, search_term,
0475 __do_unregister, NULL, NULL);
0476
0477 return cmd_retval(&res, true);
0478 }
0479
0480 static int do_register(int argc, char **argv)
0481 {
0482 LIBBPF_OPTS(bpf_object_open_opts, open_opts);
0483 struct bpf_map_info info = {};
0484 __u32 info_len = sizeof(info);
0485 int nr_errs = 0, nr_maps = 0;
0486 struct bpf_object *obj;
0487 struct bpf_link *link;
0488 struct bpf_map *map;
0489 const char *file;
0490
0491 if (argc != 1)
0492 usage();
0493
0494 file = GET_ARG();
0495
0496 if (verifier_logs)
0497
0498 open_opts.kernel_log_level = 1 + 2 + 4;
0499
0500 obj = bpf_object__open_file(file, &open_opts);
0501 if (libbpf_get_error(obj))
0502 return -1;
0503
0504 set_max_rlimit();
0505
0506 if (bpf_object__load(obj)) {
0507 bpf_object__close(obj);
0508 return -1;
0509 }
0510
0511 bpf_object__for_each_map(map, obj) {
0512 if (bpf_map__type(map) != BPF_MAP_TYPE_STRUCT_OPS)
0513 continue;
0514
0515 link = bpf_map__attach_struct_ops(map);
0516 if (libbpf_get_error(link)) {
0517 p_err("can't register struct_ops %s: %s",
0518 bpf_map__name(map),
0519 strerror(-PTR_ERR(link)));
0520 nr_errs++;
0521 continue;
0522 }
0523 nr_maps++;
0524
0525 bpf_link__disconnect(link);
0526 bpf_link__destroy(link);
0527
0528 if (!bpf_obj_get_info_by_fd(bpf_map__fd(map), &info,
0529 &info_len))
0530 p_info("Registered %s %s id %u",
0531 get_kern_struct_ops_name(&info),
0532 bpf_map__name(map),
0533 info.id);
0534 else
0535
0536
0537
0538 p_info("Registered %s but can't find id: %s",
0539 bpf_map__name(map), strerror(errno));
0540 }
0541
0542 bpf_object__close(obj);
0543
0544 if (nr_errs)
0545 return -1;
0546
0547 if (!nr_maps) {
0548 p_err("no struct_ops found in %s", file);
0549 return -1;
0550 }
0551
0552 if (json_output)
0553 jsonw_null(json_wtr);
0554
0555 return 0;
0556 }
0557
0558 static int do_help(int argc, char **argv)
0559 {
0560 if (json_output) {
0561 jsonw_null(json_wtr);
0562 return 0;
0563 }
0564
0565 fprintf(stderr,
0566 "Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n"
0567 " %1$s %2$s dump [STRUCT_OPS_MAP]\n"
0568 " %1$s %2$s register OBJ\n"
0569 " %1$s %2$s unregister STRUCT_OPS_MAP\n"
0570 " %1$s %2$s help\n"
0571 "\n"
0572 " STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n"
0573 " " HELP_SPEC_OPTIONS " }\n"
0574 "",
0575 bin_name, argv[-2]);
0576
0577 return 0;
0578 }
0579
0580 static const struct cmd cmds[] = {
0581 { "show", do_show },
0582 { "list", do_show },
0583 { "register", do_register },
0584 { "unregister", do_unregister },
0585 { "dump", do_dump },
0586 { "help", do_help },
0587 { 0 }
0588 };
0589
0590 int do_struct_ops(int argc, char **argv)
0591 {
0592 int err;
0593
0594 err = cmd_select(cmds, argc, argv, do_help);
0595
0596 if (!libbpf_get_error(btf_vmlinux))
0597 btf__free(btf_vmlinux);
0598
0599 return err;
0600 }