1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright (C) 2020 Facebook */ 3 4 #include <errno.h> 5 #include <stdio.h> 6 #include <unistd.h> 7 8 #include <linux/err.h> 9 10 #include <bpf/bpf.h> 11 #include <bpf/btf.h> 12 #include <bpf/libbpf.h> 13 14 #include "json_writer.h" 15 #include "main.h" 16 17 #define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_" 18 19 static const struct btf_type *map_info_type; 20 static __u32 map_info_alloc_len; 21 static struct btf *btf_vmlinux; 22 static __s32 map_info_type_id; 23 24 struct res { 25 unsigned int nr_maps; 26 unsigned int nr_errs; 27 }; 28 29 static const struct btf *get_btf_vmlinux(void) 30 { 31 if (btf_vmlinux) 32 return btf_vmlinux; 33 34 btf_vmlinux = libbpf_find_kernel_btf(); 35 if (IS_ERR(btf_vmlinux)) 36 p_err("struct_ops requires kernel CONFIG_DEBUG_INFO_BTF=y"); 37 38 return btf_vmlinux; 39 } 40 41 static const char *get_kern_struct_ops_name(const struct bpf_map_info *info) 42 { 43 const struct btf *kern_btf; 44 const struct btf_type *t; 45 const char *st_ops_name; 46 47 kern_btf = get_btf_vmlinux(); 48 if (IS_ERR(kern_btf)) 49 return "<btf_vmlinux_not_found>"; 50 51 t = btf__type_by_id(kern_btf, info->btf_vmlinux_value_type_id); 52 st_ops_name = btf__name_by_offset(kern_btf, t->name_off); 53 st_ops_name += strlen(STRUCT_OPS_VALUE_PREFIX); 54 55 return st_ops_name; 56 } 57 58 static __s32 get_map_info_type_id(void) 59 { 60 const struct btf *kern_btf; 61 62 if (map_info_type_id) 63 return map_info_type_id; 64 65 kern_btf = get_btf_vmlinux(); 66 if (IS_ERR(kern_btf)) { 67 map_info_type_id = PTR_ERR(kern_btf); 68 return map_info_type_id; 69 } 70 71 map_info_type_id = btf__find_by_name_kind(kern_btf, "bpf_map_info", 72 BTF_KIND_STRUCT); 73 if (map_info_type_id < 0) { 74 p_err("can't find bpf_map_info from btf_vmlinux"); 75 return map_info_type_id; 76 } 77 map_info_type = btf__type_by_id(kern_btf, map_info_type_id); 78 79 /* Ensure map_info_alloc() has at least what the bpftool needs */ 80 map_info_alloc_len = map_info_type->size; 81 if (map_info_alloc_len < sizeof(struct bpf_map_info)) 82 map_info_alloc_len = sizeof(struct bpf_map_info); 83 84 return map_info_type_id; 85 } 86 87 /* If the subcmd needs to print out the bpf_map_info, 88 * it should always call map_info_alloc to allocate 89 * a bpf_map_info object instead of allocating it 90 * on the stack. 91 * 92 * map_info_alloc() will take the running kernel's btf 93 * into account. i.e. it will consider the 94 * sizeof(struct bpf_map_info) of the running kernel. 95 * 96 * It will enable the "struct_ops" cmd to print the latest 97 * "struct bpf_map_info". 98 * 99 * [ Recall that "struct_ops" requires the kernel's btf to 100 * be available ] 101 */ 102 static struct bpf_map_info *map_info_alloc(__u32 *alloc_len) 103 { 104 struct bpf_map_info *info; 105 106 if (get_map_info_type_id() < 0) 107 return NULL; 108 109 info = calloc(1, map_info_alloc_len); 110 if (!info) 111 p_err("mem alloc failed"); 112 else 113 *alloc_len = map_info_alloc_len; 114 115 return info; 116 } 117 118 /* It iterates all struct_ops maps of the system. 119 * It returns the fd in "*res_fd" and map_info in "*info". 120 * In the very first iteration, info->id should be 0. 121 * An optional map "*name" filter can be specified. 122 * The filter can be made more flexible in the future. 123 * e.g. filter by kernel-struct-ops-name, regex-name, glob-name, ...etc. 124 * 125 * Return value: 126 * 1: A struct_ops map found. It is returned in "*res_fd" and "*info". 127 * The caller can continue to call get_next in the future. 128 * 0: No struct_ops map is returned. 129 * All struct_ops map has been found. 130 * -1: Error and the caller should abort the iteration. 131 */ 132 static int get_next_struct_ops_map(const char *name, int *res_fd, 133 struct bpf_map_info *info, __u32 info_len) 134 { 135 __u32 id = info->id; 136 int err, fd; 137 138 while (true) { 139 err = bpf_map_get_next_id(id, &id); 140 if (err) { 141 if (errno == ENOENT) 142 return 0; 143 p_err("can't get next map: %s", strerror(errno)); 144 return -1; 145 } 146 147 fd = bpf_map_get_fd_by_id(id); 148 if (fd < 0) { 149 if (errno == ENOENT) 150 continue; 151 p_err("can't get map by id (%u): %s", 152 id, strerror(errno)); 153 return -1; 154 } 155 156 err = bpf_obj_get_info_by_fd(fd, info, &info_len); 157 if (err) { 158 p_err("can't get map info: %s", strerror(errno)); 159 close(fd); 160 return -1; 161 } 162 163 if (info->type == BPF_MAP_TYPE_STRUCT_OPS && 164 (!name || !strcmp(name, info->name))) { 165 *res_fd = fd; 166 return 1; 167 } 168 close(fd); 169 } 170 } 171 172 static int cmd_retval(const struct res *res, bool must_have_one_map) 173 { 174 if (res->nr_errs || (!res->nr_maps && must_have_one_map)) 175 return -1; 176 177 return 0; 178 } 179 180 /* "data" is the work_func private storage */ 181 typedef int (*work_func)(int fd, const struct bpf_map_info *info, void *data, 182 struct json_writer *wtr); 183 184 /* Find all struct_ops map in the system. 185 * Filter out by "name" (if specified). 186 * Then call "func(fd, info, data, wtr)" on each struct_ops map found. 187 */ 188 static struct res do_search(const char *name, work_func func, void *data, 189 struct json_writer *wtr) 190 { 191 struct bpf_map_info *info; 192 struct res res = {}; 193 __u32 info_len; 194 int fd, err; 195 196 info = map_info_alloc(&info_len); 197 if (!info) { 198 res.nr_errs++; 199 return res; 200 } 201 202 if (wtr) 203 jsonw_start_array(wtr); 204 while ((err = get_next_struct_ops_map(name, &fd, info, info_len)) == 1) { 205 res.nr_maps++; 206 err = func(fd, info, data, wtr); 207 if (err) 208 res.nr_errs++; 209 close(fd); 210 } 211 if (wtr) 212 jsonw_end_array(wtr); 213 214 if (err) 215 res.nr_errs++; 216 217 if (!wtr && name && !res.nr_errs && !res.nr_maps) 218 /* It is not printing empty []. 219 * Thus, needs to specifically say nothing found 220 * for "name" here. 221 */ 222 p_err("no struct_ops found for %s", name); 223 else if (!wtr && json_output && !res.nr_errs) 224 /* The "func()" above is not writing any json (i.e. !wtr 225 * test here). 226 * 227 * However, "-j" is enabled and there is no errs here, 228 * so call json_null() as the current convention of 229 * other cmds. 230 */ 231 jsonw_null(json_wtr); 232 233 free(info); 234 return res; 235 } 236 237 static struct res do_one_id(const char *id_str, work_func func, void *data, 238 struct json_writer *wtr) 239 { 240 struct bpf_map_info *info; 241 struct res res = {}; 242 unsigned long id; 243 __u32 info_len; 244 char *endptr; 245 int fd; 246 247 id = strtoul(id_str, &endptr, 0); 248 if (*endptr || !id || id > UINT32_MAX) { 249 p_err("invalid id %s", id_str); 250 res.nr_errs++; 251 return res; 252 } 253 254 fd = bpf_map_get_fd_by_id(id); 255 if (fd == -1) { 256 p_err("can't get map by id (%lu): %s", id, strerror(errno)); 257 res.nr_errs++; 258 return res; 259 } 260 261 info = map_info_alloc(&info_len); 262 if (!info) { 263 res.nr_errs++; 264 goto done; 265 } 266 267 if (bpf_obj_get_info_by_fd(fd, info, &info_len)) { 268 p_err("can't get map info: %s", strerror(errno)); 269 res.nr_errs++; 270 goto done; 271 } 272 273 if (info->type != BPF_MAP_TYPE_STRUCT_OPS) { 274 p_err("%s id %u is not a struct_ops map", info->name, info->id); 275 res.nr_errs++; 276 goto done; 277 } 278 279 res.nr_maps++; 280 281 if (func(fd, info, data, wtr)) 282 res.nr_errs++; 283 else if (!wtr && json_output) 284 /* The "func()" above is not writing any json (i.e. !wtr 285 * test here). 286 * 287 * However, "-j" is enabled and there is no errs here, 288 * so call json_null() as the current convention of 289 * other cmds. 290 */ 291 jsonw_null(json_wtr); 292 293 done: 294 free(info); 295 close(fd); 296 297 return res; 298 } 299 300 static struct res do_work_on_struct_ops(const char *search_type, 301 const char *search_term, 302 work_func func, void *data, 303 struct json_writer *wtr) 304 { 305 if (search_type) { 306 if (is_prefix(search_type, "id")) 307 return do_one_id(search_term, func, data, wtr); 308 else if (!is_prefix(search_type, "name")) 309 usage(); 310 } 311 312 return do_search(search_term, func, data, wtr); 313 } 314 315 static int __do_show(int fd, const struct bpf_map_info *info, void *data, 316 struct json_writer *wtr) 317 { 318 if (wtr) { 319 jsonw_start_object(wtr); 320 jsonw_uint_field(wtr, "id", info->id); 321 jsonw_string_field(wtr, "name", info->name); 322 jsonw_string_field(wtr, "kernel_struct_ops", 323 get_kern_struct_ops_name(info)); 324 jsonw_end_object(wtr); 325 } else { 326 printf("%u: %-15s %-32s\n", info->id, info->name, 327 get_kern_struct_ops_name(info)); 328 } 329 330 return 0; 331 } 332 333 static int do_show(int argc, char **argv) 334 { 335 const char *search_type = NULL, *search_term = NULL; 336 struct res res; 337 338 if (argc && argc != 2) 339 usage(); 340 341 if (argc == 2) { 342 search_type = GET_ARG(); 343 search_term = GET_ARG(); 344 } 345 346 res = do_work_on_struct_ops(search_type, search_term, __do_show, 347 NULL, json_wtr); 348 349 return cmd_retval(&res, !!search_term); 350 } 351 352 static int __do_dump(int fd, const struct bpf_map_info *info, void *data, 353 struct json_writer *wtr) 354 { 355 struct btf_dumper *d = (struct btf_dumper *)data; 356 const struct btf_type *struct_ops_type; 357 const struct btf *kern_btf = d->btf; 358 const char *struct_ops_name; 359 int zero = 0; 360 void *value; 361 362 /* note: d->jw == wtr */ 363 364 kern_btf = d->btf; 365 366 /* The kernel supporting BPF_MAP_TYPE_STRUCT_OPS must have 367 * btf_vmlinux_value_type_id. 368 */ 369 struct_ops_type = btf__type_by_id(kern_btf, 370 info->btf_vmlinux_value_type_id); 371 struct_ops_name = btf__name_by_offset(kern_btf, 372 struct_ops_type->name_off); 373 value = calloc(1, info->value_size); 374 if (!value) { 375 p_err("mem alloc failed"); 376 return -1; 377 } 378 379 if (bpf_map_lookup_elem(fd, &zero, value)) { 380 p_err("can't lookup struct_ops map %s id %u", 381 info->name, info->id); 382 free(value); 383 return -1; 384 } 385 386 jsonw_start_object(wtr); 387 jsonw_name(wtr, "bpf_map_info"); 388 btf_dumper_type(d, map_info_type_id, (void *)info); 389 jsonw_end_object(wtr); 390 391 jsonw_start_object(wtr); 392 jsonw_name(wtr, struct_ops_name); 393 btf_dumper_type(d, info->btf_vmlinux_value_type_id, value); 394 jsonw_end_object(wtr); 395 396 free(value); 397 398 return 0; 399 } 400 401 static int do_dump(int argc, char **argv) 402 { 403 const char *search_type = NULL, *search_term = NULL; 404 json_writer_t *wtr = json_wtr; 405 const struct btf *kern_btf; 406 struct btf_dumper d = {}; 407 struct res res; 408 409 if (argc && argc != 2) 410 usage(); 411 412 if (argc == 2) { 413 search_type = GET_ARG(); 414 search_term = GET_ARG(); 415 } 416 417 kern_btf = get_btf_vmlinux(); 418 if (IS_ERR(kern_btf)) 419 return -1; 420 421 if (!json_output) { 422 wtr = jsonw_new(stdout); 423 if (!wtr) { 424 p_err("can't create json writer"); 425 return -1; 426 } 427 jsonw_pretty(wtr, true); 428 } 429 430 d.btf = kern_btf; 431 d.jw = wtr; 432 d.is_plain_text = !json_output; 433 d.prog_id_as_func_ptr = true; 434 435 res = do_work_on_struct_ops(search_type, search_term, __do_dump, &d, 436 wtr); 437 438 if (!json_output) 439 jsonw_destroy(&wtr); 440 441 return cmd_retval(&res, !!search_term); 442 } 443 444 static int __do_unregister(int fd, const struct bpf_map_info *info, void *data, 445 struct json_writer *wtr) 446 { 447 int zero = 0; 448 449 if (bpf_map_delete_elem(fd, &zero)) { 450 p_err("can't unload %s %s id %u: %s", 451 get_kern_struct_ops_name(info), info->name, 452 info->id, strerror(errno)); 453 return -1; 454 } 455 456 p_info("Unregistered %s %s id %u", 457 get_kern_struct_ops_name(info), info->name, 458 info->id); 459 460 return 0; 461 } 462 463 static int do_unregister(int argc, char **argv) 464 { 465 const char *search_type, *search_term; 466 struct res res; 467 468 if (argc != 2) 469 usage(); 470 471 search_type = GET_ARG(); 472 search_term = GET_ARG(); 473 474 res = do_work_on_struct_ops(search_type, search_term, 475 __do_unregister, NULL, NULL); 476 477 return cmd_retval(&res, true); 478 } 479 480 static int do_register(int argc, char **argv) 481 { 482 struct bpf_object_load_attr load_attr = {}; 483 const struct bpf_map_def *def; 484 struct bpf_map_info info = {}; 485 __u32 info_len = sizeof(info); 486 int nr_errs = 0, nr_maps = 0; 487 struct bpf_object *obj; 488 struct bpf_link *link; 489 struct bpf_map *map; 490 const char *file; 491 492 if (argc != 1) 493 usage(); 494 495 file = GET_ARG(); 496 497 obj = bpf_object__open(file); 498 if (IS_ERR_OR_NULL(obj)) 499 return -1; 500 501 set_max_rlimit(); 502 503 load_attr.obj = obj; 504 if (verifier_logs) 505 /* log_level1 + log_level2 + stats, but not stable UAPI */ 506 load_attr.log_level = 1 + 2 + 4; 507 508 if (bpf_object__load_xattr(&load_attr)) { 509 bpf_object__close(obj); 510 return -1; 511 } 512 513 bpf_object__for_each_map(map, obj) { 514 def = bpf_map__def(map); 515 if (def->type != BPF_MAP_TYPE_STRUCT_OPS) 516 continue; 517 518 link = bpf_map__attach_struct_ops(map); 519 if (IS_ERR(link)) { 520 p_err("can't register struct_ops %s: %s", 521 bpf_map__name(map), 522 strerror(-PTR_ERR(link))); 523 nr_errs++; 524 continue; 525 } 526 nr_maps++; 527 528 bpf_link__disconnect(link); 529 bpf_link__destroy(link); 530 531 if (!bpf_obj_get_info_by_fd(bpf_map__fd(map), &info, 532 &info_len)) 533 p_info("Registered %s %s id %u", 534 get_kern_struct_ops_name(&info), 535 bpf_map__name(map), 536 info.id); 537 else 538 /* Not p_err. The struct_ops was attached 539 * successfully. 540 */ 541 p_info("Registered %s but can't find id: %s", 542 bpf_map__name(map), strerror(errno)); 543 } 544 545 bpf_object__close(obj); 546 547 if (nr_errs) 548 return -1; 549 550 if (!nr_maps) { 551 p_err("no struct_ops found in %s", file); 552 return -1; 553 } 554 555 if (json_output) 556 jsonw_null(json_wtr); 557 558 return 0; 559 } 560 561 static int do_help(int argc, char **argv) 562 { 563 if (json_output) { 564 jsonw_null(json_wtr); 565 return 0; 566 } 567 568 fprintf(stderr, 569 "Usage: %1$s %2$s { show | list } [STRUCT_OPS_MAP]\n" 570 " %1$s %2$s dump [STRUCT_OPS_MAP]\n" 571 " %1$s %2$s register OBJ\n" 572 " %1$s %2$s unregister STRUCT_OPS_MAP\n" 573 " %1$s %2$s help\n" 574 "\n" 575 " OPTIONS := { {-j|--json} [{-p|--pretty}] }\n" 576 " STRUCT_OPS_MAP := [ id STRUCT_OPS_MAP_ID | name STRUCT_OPS_MAP_NAME ]\n" 577 "", 578 bin_name, argv[-2]); 579 580 return 0; 581 } 582 583 static const struct cmd cmds[] = { 584 { "show", do_show }, 585 { "list", do_show }, 586 { "register", do_register }, 587 { "unregister", do_unregister }, 588 { "dump", do_dump }, 589 { "help", do_help }, 590 { 0 } 591 }; 592 593 int do_struct_ops(int argc, char **argv) 594 { 595 int err; 596 597 err = cmd_select(cmds, argc, argv, do_help); 598 599 if (!IS_ERR(btf_vmlinux)) 600 btf__free(btf_vmlinux); 601 602 return err; 603 } 604