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 }; 21 22 static int link_parse_fd(int *argc, char ***argv) 23 { 24 if (is_prefix(**argv, "id")) { 25 unsigned int id; 26 char *endptr; 27 28 NEXT_ARGP(); 29 30 id = strtoul(**argv, &endptr, 0); 31 if (*endptr) { 32 p_err("can't parse %s as ID", **argv); 33 return -1; 34 } 35 NEXT_ARGP(); 36 37 return bpf_link_get_fd_by_id(id); 38 } else if (is_prefix(**argv, "pinned")) { 39 char *path; 40 41 NEXT_ARGP(); 42 43 path = **argv; 44 NEXT_ARGP(); 45 46 return open_obj_pinned_any(path, BPF_OBJ_LINK); 47 } 48 49 p_err("expected 'id' or 'pinned', got: '%s'?", **argv); 50 return -1; 51 } 52 53 static void 54 show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr) 55 { 56 jsonw_uint_field(wtr, "id", info->id); 57 if (info->type < ARRAY_SIZE(link_type_name)) 58 jsonw_string_field(wtr, "type", link_type_name[info->type]); 59 else 60 jsonw_uint_field(wtr, "type", info->type); 61 62 jsonw_uint_field(json_wtr, "prog_id", info->prog_id); 63 } 64 65 static int get_prog_info(int prog_id, struct bpf_prog_info *info) 66 { 67 __u32 len = sizeof(*info); 68 int err, prog_fd; 69 70 prog_fd = bpf_prog_get_fd_by_id(prog_id); 71 if (prog_fd < 0) 72 return prog_fd; 73 74 memset(info, 0, sizeof(*info)); 75 err = bpf_obj_get_info_by_fd(prog_fd, info, &len); 76 if (err) 77 p_err("can't get prog info: %s", strerror(errno)); 78 close(prog_fd); 79 return err; 80 } 81 82 static int show_link_close_json(int fd, struct bpf_link_info *info) 83 { 84 struct bpf_prog_info prog_info; 85 int err; 86 87 jsonw_start_object(json_wtr); 88 89 show_link_header_json(info, json_wtr); 90 91 switch (info->type) { 92 case BPF_LINK_TYPE_RAW_TRACEPOINT: 93 jsonw_string_field(json_wtr, "tp_name", 94 (const char *)info->raw_tracepoint.tp_name); 95 break; 96 case BPF_LINK_TYPE_TRACING: 97 err = get_prog_info(info->prog_id, &prog_info); 98 if (err) 99 return err; 100 101 if (prog_info.type < ARRAY_SIZE(prog_type_name)) 102 jsonw_string_field(json_wtr, "prog_type", 103 prog_type_name[prog_info.type]); 104 else 105 jsonw_uint_field(json_wtr, "prog_type", 106 prog_info.type); 107 108 if (info->tracing.attach_type < ARRAY_SIZE(attach_type_name)) 109 jsonw_string_field(json_wtr, "attach_type", 110 attach_type_name[info->tracing.attach_type]); 111 else 112 jsonw_uint_field(json_wtr, "attach_type", 113 info->tracing.attach_type); 114 break; 115 case BPF_LINK_TYPE_CGROUP: 116 jsonw_lluint_field(json_wtr, "cgroup_id", 117 info->cgroup.cgroup_id); 118 if (info->cgroup.attach_type < ARRAY_SIZE(attach_type_name)) 119 jsonw_string_field(json_wtr, "attach_type", 120 attach_type_name[info->cgroup.attach_type]); 121 else 122 jsonw_uint_field(json_wtr, "attach_type", 123 info->cgroup.attach_type); 124 break; 125 default: 126 break; 127 } 128 129 if (!hash_empty(link_table.table)) { 130 struct pinned_obj *obj; 131 132 jsonw_name(json_wtr, "pinned"); 133 jsonw_start_array(json_wtr); 134 hash_for_each_possible(link_table.table, obj, hash, info->id) { 135 if (obj->id == info->id) 136 jsonw_string(json_wtr, obj->path); 137 } 138 jsonw_end_array(json_wtr); 139 } 140 jsonw_end_object(json_wtr); 141 142 return 0; 143 } 144 145 static void show_link_header_plain(struct bpf_link_info *info) 146 { 147 printf("%u: ", info->id); 148 if (info->type < ARRAY_SIZE(link_type_name)) 149 printf("%s ", link_type_name[info->type]); 150 else 151 printf("type %u ", info->type); 152 153 printf("prog %u ", info->prog_id); 154 } 155 156 static int show_link_close_plain(int fd, struct bpf_link_info *info) 157 { 158 struct bpf_prog_info prog_info; 159 int err; 160 161 show_link_header_plain(info); 162 163 switch (info->type) { 164 case BPF_LINK_TYPE_RAW_TRACEPOINT: 165 printf("\n\ttp '%s' ", 166 (const char *)info->raw_tracepoint.tp_name); 167 break; 168 case BPF_LINK_TYPE_TRACING: 169 err = get_prog_info(info->prog_id, &prog_info); 170 if (err) 171 return err; 172 173 if (prog_info.type < ARRAY_SIZE(prog_type_name)) 174 printf("\n\tprog_type %s ", 175 prog_type_name[prog_info.type]); 176 else 177 printf("\n\tprog_type %u ", prog_info.type); 178 179 if (info->tracing.attach_type < ARRAY_SIZE(attach_type_name)) 180 printf("attach_type %s ", 181 attach_type_name[info->tracing.attach_type]); 182 else 183 printf("attach_type %u ", info->tracing.attach_type); 184 break; 185 case BPF_LINK_TYPE_CGROUP: 186 printf("\n\tcgroup_id %zu ", (size_t)info->cgroup.cgroup_id); 187 if (info->cgroup.attach_type < ARRAY_SIZE(attach_type_name)) 188 printf("attach_type %s ", 189 attach_type_name[info->cgroup.attach_type]); 190 else 191 printf("attach_type %u ", info->cgroup.attach_type); 192 break; 193 default: 194 break; 195 } 196 197 if (!hash_empty(link_table.table)) { 198 struct pinned_obj *obj; 199 200 hash_for_each_possible(link_table.table, obj, hash, info->id) { 201 if (obj->id == info->id) 202 printf("\n\tpinned %s", obj->path); 203 } 204 } 205 206 printf("\n"); 207 208 return 0; 209 } 210 211 static int do_show_link(int fd) 212 { 213 struct bpf_link_info info; 214 __u32 len = sizeof(info); 215 char raw_tp_name[256]; 216 int err; 217 218 memset(&info, 0, sizeof(info)); 219 again: 220 err = bpf_obj_get_info_by_fd(fd, &info, &len); 221 if (err) { 222 p_err("can't get link info: %s", 223 strerror(errno)); 224 close(fd); 225 return err; 226 } 227 if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT && 228 !info.raw_tracepoint.tp_name) { 229 info.raw_tracepoint.tp_name = (unsigned long)&raw_tp_name; 230 info.raw_tracepoint.tp_name_len = sizeof(raw_tp_name); 231 goto again; 232 } 233 234 if (json_output) 235 show_link_close_json(fd, &info); 236 else 237 show_link_close_plain(fd, &info); 238 239 close(fd); 240 return 0; 241 } 242 243 static int do_show(int argc, char **argv) 244 { 245 __u32 id = 0; 246 int err, fd; 247 248 if (show_pinned) 249 build_pinned_obj_table(&link_table, BPF_OBJ_LINK); 250 251 if (argc == 2) { 252 fd = link_parse_fd(&argc, &argv); 253 if (fd < 0) 254 return fd; 255 return do_show_link(fd); 256 } 257 258 if (argc) 259 return BAD_ARG(); 260 261 if (json_output) 262 jsonw_start_array(json_wtr); 263 while (true) { 264 err = bpf_link_get_next_id(id, &id); 265 if (err) { 266 if (errno == ENOENT) 267 break; 268 p_err("can't get next link: %s%s", strerror(errno), 269 errno == EINVAL ? " -- kernel too old?" : ""); 270 break; 271 } 272 273 fd = bpf_link_get_fd_by_id(id); 274 if (fd < 0) { 275 if (errno == ENOENT) 276 continue; 277 p_err("can't get link by id (%u): %s", 278 id, strerror(errno)); 279 break; 280 } 281 282 err = do_show_link(fd); 283 if (err) 284 break; 285 } 286 if (json_output) 287 jsonw_end_array(json_wtr); 288 289 return errno == ENOENT ? 0 : -1; 290 } 291 292 static int do_pin(int argc, char **argv) 293 { 294 int err; 295 296 err = do_pin_any(argc, argv, link_parse_fd); 297 if (!err && json_output) 298 jsonw_null(json_wtr); 299 return err; 300 } 301 302 static int do_help(int argc, char **argv) 303 { 304 if (json_output) { 305 jsonw_null(json_wtr); 306 return 0; 307 } 308 309 fprintf(stderr, 310 "Usage: %1$s %2$s { show | list } [LINK]\n" 311 " %1$s %2$s pin LINK FILE\n" 312 " %1$s %2$s help\n" 313 "\n" 314 " " HELP_SPEC_LINK "\n" 315 " " HELP_SPEC_PROGRAM "\n" 316 " " HELP_SPEC_OPTIONS "\n" 317 "", 318 bin_name, argv[-2]); 319 320 return 0; 321 } 322 323 static const struct cmd cmds[] = { 324 { "show", do_show }, 325 { "list", do_show }, 326 { "help", do_help }, 327 { "pin", do_pin }, 328 { 0 } 329 }; 330 331 int do_link(int argc, char **argv) 332 { 333 return cmd_select(cmds, argc, argv, do_help); 334 } 335