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