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 int get_prog_info(int prog_id, struct bpf_prog_info *info) 81 { 82 __u32 len = sizeof(*info); 83 int err, prog_fd; 84 85 prog_fd = bpf_prog_get_fd_by_id(prog_id); 86 if (prog_fd < 0) 87 return prog_fd; 88 89 memset(info, 0, sizeof(*info)); 90 err = bpf_obj_get_info_by_fd(prog_fd, info, &len); 91 if (err) 92 p_err("can't get prog info: %s", strerror(errno)); 93 close(prog_fd); 94 return err; 95 } 96 97 static int show_link_close_json(int fd, struct bpf_link_info *info) 98 { 99 struct bpf_prog_info prog_info; 100 int err; 101 102 jsonw_start_object(json_wtr); 103 104 show_link_header_json(info, json_wtr); 105 106 switch (info->type) { 107 case BPF_LINK_TYPE_RAW_TRACEPOINT: 108 jsonw_string_field(json_wtr, "tp_name", 109 (const char *)info->raw_tracepoint.tp_name); 110 break; 111 case BPF_LINK_TYPE_TRACING: 112 err = get_prog_info(info->prog_id, &prog_info); 113 if (err) 114 return err; 115 116 if (prog_info.type < prog_type_name_size) 117 jsonw_string_field(json_wtr, "prog_type", 118 prog_type_name[prog_info.type]); 119 else 120 jsonw_uint_field(json_wtr, "prog_type", 121 prog_info.type); 122 123 show_link_attach_type_json(info->tracing.attach_type, 124 json_wtr); 125 break; 126 case BPF_LINK_TYPE_CGROUP: 127 jsonw_lluint_field(json_wtr, "cgroup_id", 128 info->cgroup.cgroup_id); 129 show_link_attach_type_json(info->cgroup.attach_type, json_wtr); 130 break; 131 case BPF_LINK_TYPE_NETNS: 132 jsonw_uint_field(json_wtr, "netns_ino", 133 info->netns.netns_ino); 134 show_link_attach_type_json(info->netns.attach_type, json_wtr); 135 break; 136 default: 137 break; 138 } 139 140 if (!hash_empty(link_table.table)) { 141 struct pinned_obj *obj; 142 143 jsonw_name(json_wtr, "pinned"); 144 jsonw_start_array(json_wtr); 145 hash_for_each_possible(link_table.table, obj, hash, info->id) { 146 if (obj->id == info->id) 147 jsonw_string(json_wtr, obj->path); 148 } 149 jsonw_end_array(json_wtr); 150 } 151 152 emit_obj_refs_json(&refs_table, info->id, json_wtr); 153 154 jsonw_end_object(json_wtr); 155 156 return 0; 157 } 158 159 static void show_link_header_plain(struct bpf_link_info *info) 160 { 161 printf("%u: ", info->id); 162 if (info->type < ARRAY_SIZE(link_type_name)) 163 printf("%s ", link_type_name[info->type]); 164 else 165 printf("type %u ", info->type); 166 167 printf("prog %u ", info->prog_id); 168 } 169 170 static void show_link_attach_type_plain(__u32 attach_type) 171 { 172 if (attach_type < ARRAY_SIZE(attach_type_name)) 173 printf("attach_type %s ", attach_type_name[attach_type]); 174 else 175 printf("attach_type %u ", attach_type); 176 } 177 178 static int show_link_close_plain(int fd, struct bpf_link_info *info) 179 { 180 struct bpf_prog_info prog_info; 181 int err; 182 183 show_link_header_plain(info); 184 185 switch (info->type) { 186 case BPF_LINK_TYPE_RAW_TRACEPOINT: 187 printf("\n\ttp '%s' ", 188 (const char *)info->raw_tracepoint.tp_name); 189 break; 190 case BPF_LINK_TYPE_TRACING: 191 err = get_prog_info(info->prog_id, &prog_info); 192 if (err) 193 return err; 194 195 if (prog_info.type < prog_type_name_size) 196 printf("\n\tprog_type %s ", 197 prog_type_name[prog_info.type]); 198 else 199 printf("\n\tprog_type %u ", prog_info.type); 200 201 show_link_attach_type_plain(info->tracing.attach_type); 202 break; 203 case BPF_LINK_TYPE_CGROUP: 204 printf("\n\tcgroup_id %zu ", (size_t)info->cgroup.cgroup_id); 205 show_link_attach_type_plain(info->cgroup.attach_type); 206 break; 207 case BPF_LINK_TYPE_NETNS: 208 printf("\n\tnetns_ino %u ", info->netns.netns_ino); 209 show_link_attach_type_plain(info->netns.attach_type); 210 break; 211 default: 212 break; 213 } 214 215 if (!hash_empty(link_table.table)) { 216 struct pinned_obj *obj; 217 218 hash_for_each_possible(link_table.table, obj, hash, info->id) { 219 if (obj->id == info->id) 220 printf("\n\tpinned %s", obj->path); 221 } 222 } 223 emit_obj_refs_plain(&refs_table, info->id, "\n\tpids "); 224 225 printf("\n"); 226 227 return 0; 228 } 229 230 static int do_show_link(int fd) 231 { 232 struct bpf_link_info info; 233 __u32 len = sizeof(info); 234 char raw_tp_name[256]; 235 int err; 236 237 memset(&info, 0, sizeof(info)); 238 again: 239 err = bpf_obj_get_info_by_fd(fd, &info, &len); 240 if (err) { 241 p_err("can't get link info: %s", 242 strerror(errno)); 243 close(fd); 244 return err; 245 } 246 if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT && 247 !info.raw_tracepoint.tp_name) { 248 info.raw_tracepoint.tp_name = (unsigned long)&raw_tp_name; 249 info.raw_tracepoint.tp_name_len = sizeof(raw_tp_name); 250 goto again; 251 } 252 253 if (json_output) 254 show_link_close_json(fd, &info); 255 else 256 show_link_close_plain(fd, &info); 257 258 close(fd); 259 return 0; 260 } 261 262 static int do_show(int argc, char **argv) 263 { 264 __u32 id = 0; 265 int err, fd; 266 267 if (show_pinned) 268 build_pinned_obj_table(&link_table, BPF_OBJ_LINK); 269 build_obj_refs_table(&refs_table, BPF_OBJ_LINK); 270 271 if (argc == 2) { 272 fd = link_parse_fd(&argc, &argv); 273 if (fd < 0) 274 return fd; 275 return do_show_link(fd); 276 } 277 278 if (argc) 279 return BAD_ARG(); 280 281 if (json_output) 282 jsonw_start_array(json_wtr); 283 while (true) { 284 err = bpf_link_get_next_id(id, &id); 285 if (err) { 286 if (errno == ENOENT) 287 break; 288 p_err("can't get next link: %s%s", strerror(errno), 289 errno == EINVAL ? " -- kernel too old?" : ""); 290 break; 291 } 292 293 fd = bpf_link_get_fd_by_id(id); 294 if (fd < 0) { 295 if (errno == ENOENT) 296 continue; 297 p_err("can't get link by id (%u): %s", 298 id, strerror(errno)); 299 break; 300 } 301 302 err = do_show_link(fd); 303 if (err) 304 break; 305 } 306 if (json_output) 307 jsonw_end_array(json_wtr); 308 309 delete_obj_refs_table(&refs_table); 310 311 return errno == ENOENT ? 0 : -1; 312 } 313 314 static int do_pin(int argc, char **argv) 315 { 316 int err; 317 318 err = do_pin_any(argc, argv, link_parse_fd); 319 if (!err && json_output) 320 jsonw_null(json_wtr); 321 return err; 322 } 323 324 static int do_detach(int argc, char **argv) 325 { 326 int err, fd; 327 328 if (argc != 2) { 329 p_err("link specifier is invalid or missing\n"); 330 return 1; 331 } 332 333 fd = link_parse_fd(&argc, &argv); 334 if (fd < 0) 335 return 1; 336 337 err = bpf_link_detach(fd); 338 if (err) 339 err = -errno; 340 close(fd); 341 if (err) { 342 p_err("failed link detach: %s", strerror(-err)); 343 return 1; 344 } 345 346 if (json_output) 347 jsonw_null(json_wtr); 348 349 return 0; 350 } 351 352 static int do_help(int argc, char **argv) 353 { 354 if (json_output) { 355 jsonw_null(json_wtr); 356 return 0; 357 } 358 359 fprintf(stderr, 360 "Usage: %1$s %2$s { show | list } [LINK]\n" 361 " %1$s %2$s pin LINK FILE\n" 362 " %1$s %2$s detach LINK\n" 363 " %1$s %2$s help\n" 364 "\n" 365 " " HELP_SPEC_LINK "\n" 366 " " HELP_SPEC_OPTIONS "\n" 367 "", 368 bin_name, argv[-2]); 369 370 return 0; 371 } 372 373 static const struct cmd cmds[] = { 374 { "show", do_show }, 375 { "list", do_show }, 376 { "help", do_help }, 377 { "pin", do_pin }, 378 { "detach", do_detach }, 379 { 0 } 380 }; 381 382 int do_link(int argc, char **argv) 383 { 384 return cmd_select(cmds, argc, argv, do_help); 385 } 386