1 /* 2 * Copyright (C) 2017 Netronome Systems, Inc. 3 * 4 * This software is dual licensed under the GNU General License Version 2, 5 * June 1991 as shown in the file COPYING in the top-level directory of this 6 * source tree or the BSD 2-Clause License provided below. You have the 7 * option to license this software under the complete terms of either license. 8 * 9 * The BSD 2-Clause License: 10 * 11 * Redistribution and use in source and binary forms, with or 12 * without modification, are permitted provided that the following 13 * conditions are met: 14 * 15 * 1. Redistributions of source code must retain the above 16 * copyright notice, this list of conditions and the following 17 * disclaimer. 18 * 19 * 2. Redistributions in binary form must reproduce the above 20 * copyright notice, this list of conditions and the following 21 * disclaimer in the documentation and/or other materials 22 * provided with the distribution. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 * SOFTWARE. 32 */ 33 34 /* Author: Jakub Kicinski <kubakici@wp.pl> */ 35 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <stdarg.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <time.h> 43 #include <unistd.h> 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 47 #include <bpf.h> 48 49 #include "main.h" 50 #include "disasm.h" 51 52 static const char * const prog_type_name[] = { 53 [BPF_PROG_TYPE_UNSPEC] = "unspec", 54 [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", 55 [BPF_PROG_TYPE_KPROBE] = "kprobe", 56 [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", 57 [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", 58 [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", 59 [BPF_PROG_TYPE_XDP] = "xdp", 60 [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", 61 [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", 62 [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", 63 [BPF_PROG_TYPE_LWT_IN] = "lwt_in", 64 [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", 65 [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", 66 [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", 67 [BPF_PROG_TYPE_SK_SKB] = "sk_skb", 68 }; 69 70 static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) 71 { 72 struct timespec real_time_ts, boot_time_ts; 73 time_t wallclock_secs; 74 struct tm load_tm; 75 76 buf[--size] = '\0'; 77 78 if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || 79 clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { 80 perror("Can't read clocks"); 81 snprintf(buf, size, "%llu", nsecs / 1000000000); 82 return; 83 } 84 85 wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + 86 nsecs / 1000000000; 87 88 if (!localtime_r(&wallclock_secs, &load_tm)) { 89 snprintf(buf, size, "%llu", nsecs / 1000000000); 90 return; 91 } 92 93 strftime(buf, size, "%b %d/%H:%M", &load_tm); 94 } 95 96 static int prog_fd_by_tag(unsigned char *tag) 97 { 98 struct bpf_prog_info info = {}; 99 __u32 len = sizeof(info); 100 unsigned int id = 0; 101 int err; 102 int fd; 103 104 while (true) { 105 err = bpf_prog_get_next_id(id, &id); 106 if (err) { 107 err("%s\n", strerror(errno)); 108 return -1; 109 } 110 111 fd = bpf_prog_get_fd_by_id(id); 112 if (fd < 0) { 113 err("can't get prog by id (%u): %s\n", 114 id, strerror(errno)); 115 return -1; 116 } 117 118 err = bpf_obj_get_info_by_fd(fd, &info, &len); 119 if (err) { 120 err("can't get prog info (%u): %s\n", 121 id, strerror(errno)); 122 close(fd); 123 return -1; 124 } 125 126 if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) 127 return fd; 128 129 close(fd); 130 } 131 } 132 133 int prog_parse_fd(int *argc, char ***argv) 134 { 135 int fd; 136 137 if (is_prefix(**argv, "id")) { 138 unsigned int id; 139 char *endptr; 140 141 NEXT_ARGP(); 142 143 id = strtoul(**argv, &endptr, 0); 144 if (*endptr) { 145 err("can't parse %s as ID\n", **argv); 146 return -1; 147 } 148 NEXT_ARGP(); 149 150 fd = bpf_prog_get_fd_by_id(id); 151 if (fd < 0) 152 err("get by id (%u): %s\n", id, strerror(errno)); 153 return fd; 154 } else if (is_prefix(**argv, "tag")) { 155 unsigned char tag[BPF_TAG_SIZE]; 156 157 NEXT_ARGP(); 158 159 if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, 160 tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) 161 != BPF_TAG_SIZE) { 162 err("can't parse tag\n"); 163 return -1; 164 } 165 NEXT_ARGP(); 166 167 return prog_fd_by_tag(tag); 168 } else if (is_prefix(**argv, "pinned")) { 169 char *path; 170 171 NEXT_ARGP(); 172 173 path = **argv; 174 NEXT_ARGP(); 175 176 return open_obj_pinned_any(path, BPF_OBJ_PROG); 177 } 178 179 err("expected 'id', 'tag' or 'pinned', got: '%s'?\n", **argv); 180 return -1; 181 } 182 183 static void show_prog_maps(int fd, u32 num_maps) 184 { 185 struct bpf_prog_info info = {}; 186 __u32 len = sizeof(info); 187 __u32 map_ids[num_maps]; 188 unsigned int i; 189 int err; 190 191 info.nr_map_ids = num_maps; 192 info.map_ids = ptr_to_u64(map_ids); 193 194 err = bpf_obj_get_info_by_fd(fd, &info, &len); 195 if (err || !info.nr_map_ids) 196 return; 197 198 printf(" map_ids "); 199 for (i = 0; i < info.nr_map_ids; i++) 200 printf("%u%s", map_ids[i], 201 i == info.nr_map_ids - 1 ? "" : ","); 202 } 203 204 static int show_prog(int fd) 205 { 206 struct bpf_prog_info info = {}; 207 __u32 len = sizeof(info); 208 char *memlock; 209 int err; 210 211 err = bpf_obj_get_info_by_fd(fd, &info, &len); 212 if (err) { 213 err("can't get prog info: %s\n", strerror(errno)); 214 return -1; 215 } 216 217 printf("%u: ", info.id); 218 if (info.type < ARRAY_SIZE(prog_type_name)) 219 printf("%s ", prog_type_name[info.type]); 220 else 221 printf("type %u ", info.type); 222 223 if (*info.name) 224 printf("name %s ", info.name); 225 226 printf("tag "); 227 print_hex(info.tag, BPF_TAG_SIZE, ":"); 228 printf("\n"); 229 230 if (info.load_time) { 231 char buf[32]; 232 233 print_boot_time(info.load_time, buf, sizeof(buf)); 234 235 /* Piggy back on load_time, since 0 uid is a valid one */ 236 printf("\tloaded_at %s uid %u\n", buf, info.created_by_uid); 237 } 238 239 printf("\txlated %uB", info.xlated_prog_len); 240 241 if (info.jited_prog_len) 242 printf(" jited %uB", info.jited_prog_len); 243 else 244 printf(" not jited"); 245 246 memlock = get_fdinfo(fd, "memlock"); 247 if (memlock) 248 printf(" memlock %sB", memlock); 249 free(memlock); 250 251 if (info.nr_map_ids) 252 show_prog_maps(fd, info.nr_map_ids); 253 254 printf("\n"); 255 256 return 0; 257 } 258 259 static int do_show(int argc, char **argv) 260 { __u32 id = 0; 261 int err; 262 int fd; 263 264 if (argc == 2) { 265 fd = prog_parse_fd(&argc, &argv); 266 if (fd < 0) 267 return -1; 268 269 return show_prog(fd); 270 } 271 272 if (argc) 273 return BAD_ARG(); 274 275 while (true) { 276 err = bpf_prog_get_next_id(id, &id); 277 if (err) { 278 if (errno == ENOENT) 279 break; 280 err("can't get next program: %s\n", strerror(errno)); 281 if (errno == EINVAL) 282 err("kernel too old?\n"); 283 return -1; 284 } 285 286 fd = bpf_prog_get_fd_by_id(id); 287 if (fd < 0) { 288 err("can't get prog by id (%u): %s\n", 289 id, strerror(errno)); 290 return -1; 291 } 292 293 err = show_prog(fd); 294 close(fd); 295 if (err) 296 return err; 297 } 298 299 return 0; 300 } 301 302 static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) 303 { 304 va_list args; 305 306 va_start(args, fmt); 307 vprintf(fmt, args); 308 va_end(args); 309 } 310 311 static void dump_xlated(void *buf, unsigned int len, bool opcodes) 312 { 313 struct bpf_insn *insn = buf; 314 unsigned int i; 315 316 for (i = 0; i < len / sizeof(*insn); i++) { 317 printf("% 4d: ", i); 318 print_bpf_insn(print_insn, NULL, insn + i, true); 319 320 if (opcodes) { 321 printf(" "); 322 print_hex(insn + i, 8, " "); 323 printf("\n"); 324 } 325 326 if (insn[i].code == (BPF_LD | BPF_IMM | BPF_DW)) 327 i++; 328 } 329 } 330 331 static int do_dump(int argc, char **argv) 332 { 333 struct bpf_prog_info info = {}; 334 __u32 len = sizeof(info); 335 unsigned int buf_size; 336 char *filepath = NULL; 337 bool opcodes = false; 338 unsigned char *buf; 339 __u32 *member_len; 340 __u64 *member_ptr; 341 ssize_t n; 342 int err; 343 int fd; 344 345 if (is_prefix(*argv, "jited")) { 346 member_len = &info.jited_prog_len; 347 member_ptr = &info.jited_prog_insns; 348 } else if (is_prefix(*argv, "xlated")) { 349 member_len = &info.xlated_prog_len; 350 member_ptr = &info.xlated_prog_insns; 351 } else { 352 err("expected 'xlated' or 'jited', got: %s\n", *argv); 353 return -1; 354 } 355 NEXT_ARG(); 356 357 if (argc < 2) 358 usage(); 359 360 fd = prog_parse_fd(&argc, &argv); 361 if (fd < 0) 362 return -1; 363 364 if (is_prefix(*argv, "file")) { 365 NEXT_ARG(); 366 if (!argc) { 367 err("expected file path\n"); 368 return -1; 369 } 370 371 filepath = *argv; 372 NEXT_ARG(); 373 } else if (is_prefix(*argv, "opcodes")) { 374 opcodes = true; 375 NEXT_ARG(); 376 } 377 378 if (argc) { 379 usage(); 380 return -1; 381 } 382 383 err = bpf_obj_get_info_by_fd(fd, &info, &len); 384 if (err) { 385 err("can't get prog info: %s\n", strerror(errno)); 386 return -1; 387 } 388 389 if (!*member_len) { 390 info("no instructions returned\n"); 391 close(fd); 392 return 0; 393 } 394 395 buf_size = *member_len; 396 397 buf = malloc(buf_size); 398 if (!buf) { 399 err("mem alloc failed\n"); 400 close(fd); 401 return -1; 402 } 403 404 memset(&info, 0, sizeof(info)); 405 406 *member_ptr = ptr_to_u64(buf); 407 *member_len = buf_size; 408 409 err = bpf_obj_get_info_by_fd(fd, &info, &len); 410 close(fd); 411 if (err) { 412 err("can't get prog info: %s\n", strerror(errno)); 413 goto err_free; 414 } 415 416 if (*member_len > buf_size) { 417 info("too many instructions returned\n"); 418 goto err_free; 419 } 420 421 if (filepath) { 422 fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 423 if (fd < 0) { 424 err("can't open file %s: %s\n", filepath, 425 strerror(errno)); 426 goto err_free; 427 } 428 429 n = write(fd, buf, *member_len); 430 close(fd); 431 if (n != *member_len) { 432 err("error writing output file: %s\n", 433 n < 0 ? strerror(errno) : "short write"); 434 goto err_free; 435 } 436 } else { 437 if (member_len == &info.jited_prog_len) 438 disasm_print_insn(buf, *member_len, opcodes); 439 else 440 dump_xlated(buf, *member_len, opcodes); 441 } 442 443 free(buf); 444 445 return 0; 446 447 err_free: 448 free(buf); 449 return -1; 450 } 451 452 static int do_pin(int argc, char **argv) 453 { 454 return do_pin_any(argc, argv, bpf_prog_get_fd_by_id); 455 } 456 457 static int do_help(int argc, char **argv) 458 { 459 fprintf(stderr, 460 "Usage: %s %s show [PROG]\n" 461 " %s %s dump xlated PROG [file FILE] [opcodes]\n" 462 " %s %s dump jited PROG [file FILE] [opcodes]\n" 463 " %s %s pin PROG FILE\n" 464 " %s %s help\n" 465 "\n" 466 " " HELP_SPEC_PROGRAM "\n" 467 "", 468 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 469 bin_name, argv[-2], bin_name, argv[-2]); 470 471 return 0; 472 } 473 474 static const struct cmd cmds[] = { 475 { "show", do_show }, 476 { "dump", do_dump }, 477 { "pin", do_pin }, 478 { 0 } 479 }; 480 481 int do_prog(int argc, char **argv) 482 { 483 return cmd_select(cmds, argc, argv, do_help); 484 } 485