171bb428fSJakub Kicinski /* 271bb428fSJakub Kicinski * Copyright (C) 2017 Netronome Systems, Inc. 371bb428fSJakub Kicinski * 471bb428fSJakub Kicinski * This software is dual licensed under the GNU General License Version 2, 571bb428fSJakub Kicinski * June 1991 as shown in the file COPYING in the top-level directory of this 671bb428fSJakub Kicinski * source tree or the BSD 2-Clause License provided below. You have the 771bb428fSJakub Kicinski * option to license this software under the complete terms of either license. 871bb428fSJakub Kicinski * 971bb428fSJakub Kicinski * The BSD 2-Clause License: 1071bb428fSJakub Kicinski * 1171bb428fSJakub Kicinski * Redistribution and use in source and binary forms, with or 1271bb428fSJakub Kicinski * without modification, are permitted provided that the following 1371bb428fSJakub Kicinski * conditions are met: 1471bb428fSJakub Kicinski * 1571bb428fSJakub Kicinski * 1. Redistributions of source code must retain the above 1671bb428fSJakub Kicinski * copyright notice, this list of conditions and the following 1771bb428fSJakub Kicinski * disclaimer. 1871bb428fSJakub Kicinski * 1971bb428fSJakub Kicinski * 2. Redistributions in binary form must reproduce the above 2071bb428fSJakub Kicinski * copyright notice, this list of conditions and the following 2171bb428fSJakub Kicinski * disclaimer in the documentation and/or other materials 2271bb428fSJakub Kicinski * provided with the distribution. 2371bb428fSJakub Kicinski * 2471bb428fSJakub Kicinski * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2571bb428fSJakub Kicinski * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2671bb428fSJakub Kicinski * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2771bb428fSJakub Kicinski * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2871bb428fSJakub Kicinski * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2971bb428fSJakub Kicinski * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3071bb428fSJakub Kicinski * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3171bb428fSJakub Kicinski * SOFTWARE. 3271bb428fSJakub Kicinski */ 3371bb428fSJakub Kicinski 3471bb428fSJakub Kicinski /* Author: Jakub Kicinski <kubakici@wp.pl> */ 3571bb428fSJakub Kicinski 3671bb428fSJakub Kicinski #include <errno.h> 3771bb428fSJakub Kicinski #include <fcntl.h> 38c9c35995SJakub Kicinski #include <stdarg.h> 3971bb428fSJakub Kicinski #include <stdio.h> 4071bb428fSJakub Kicinski #include <stdlib.h> 4171bb428fSJakub Kicinski #include <string.h> 4271bb428fSJakub Kicinski #include <time.h> 4371bb428fSJakub Kicinski #include <unistd.h> 44928631e0SJakub Kicinski #include <net/if.h> 4571bb428fSJakub Kicinski #include <sys/types.h> 4671bb428fSJakub Kicinski #include <sys/stat.h> 4771bb428fSJakub Kicinski 4871bb428fSJakub Kicinski #include <bpf.h> 4971bb428fSJakub Kicinski 5071bb428fSJakub Kicinski #include "main.h" 51c9c35995SJakub Kicinski #include "disasm.h" 5271bb428fSJakub Kicinski 5371bb428fSJakub Kicinski static const char * const prog_type_name[] = { 5471bb428fSJakub Kicinski [BPF_PROG_TYPE_UNSPEC] = "unspec", 5571bb428fSJakub Kicinski [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", 5671bb428fSJakub Kicinski [BPF_PROG_TYPE_KPROBE] = "kprobe", 5771bb428fSJakub Kicinski [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", 5871bb428fSJakub Kicinski [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", 5971bb428fSJakub Kicinski [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", 6071bb428fSJakub Kicinski [BPF_PROG_TYPE_XDP] = "xdp", 6171bb428fSJakub Kicinski [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", 6271bb428fSJakub Kicinski [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", 6371bb428fSJakub Kicinski [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", 6471bb428fSJakub Kicinski [BPF_PROG_TYPE_LWT_IN] = "lwt_in", 6571bb428fSJakub Kicinski [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", 6671bb428fSJakub Kicinski [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", 6771bb428fSJakub Kicinski [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", 6871bb428fSJakub Kicinski [BPF_PROG_TYPE_SK_SKB] = "sk_skb", 6971bb428fSJakub Kicinski }; 7071bb428fSJakub Kicinski 7171bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) 7271bb428fSJakub Kicinski { 7371bb428fSJakub Kicinski struct timespec real_time_ts, boot_time_ts; 7471bb428fSJakub Kicinski time_t wallclock_secs; 7571bb428fSJakub Kicinski struct tm load_tm; 7671bb428fSJakub Kicinski 7771bb428fSJakub Kicinski buf[--size] = '\0'; 7871bb428fSJakub Kicinski 7971bb428fSJakub Kicinski if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || 8071bb428fSJakub Kicinski clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { 8171bb428fSJakub Kicinski perror("Can't read clocks"); 8271bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 8371bb428fSJakub Kicinski return; 8471bb428fSJakub Kicinski } 8571bb428fSJakub Kicinski 8671bb428fSJakub Kicinski wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + 8771bb428fSJakub Kicinski nsecs / 1000000000; 8871bb428fSJakub Kicinski 8971bb428fSJakub Kicinski if (!localtime_r(&wallclock_secs, &load_tm)) { 9071bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 9171bb428fSJakub Kicinski return; 9271bb428fSJakub Kicinski } 9371bb428fSJakub Kicinski 9471bb428fSJakub Kicinski strftime(buf, size, "%b %d/%H:%M", &load_tm); 9571bb428fSJakub Kicinski } 9671bb428fSJakub Kicinski 9771bb428fSJakub Kicinski static int prog_fd_by_tag(unsigned char *tag) 9871bb428fSJakub Kicinski { 9971bb428fSJakub Kicinski struct bpf_prog_info info = {}; 10071bb428fSJakub Kicinski __u32 len = sizeof(info); 10171bb428fSJakub Kicinski unsigned int id = 0; 10271bb428fSJakub Kicinski int err; 10371bb428fSJakub Kicinski int fd; 10471bb428fSJakub Kicinski 10571bb428fSJakub Kicinski while (true) { 10671bb428fSJakub Kicinski err = bpf_prog_get_next_id(id, &id); 10771bb428fSJakub Kicinski if (err) { 1089a5ab8bfSQuentin Monnet p_err("%s", strerror(errno)); 10971bb428fSJakub Kicinski return -1; 11071bb428fSJakub Kicinski } 11171bb428fSJakub Kicinski 11271bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 11371bb428fSJakub Kicinski if (fd < 0) { 1149a5ab8bfSQuentin Monnet p_err("can't get prog by id (%u): %s", 11571bb428fSJakub Kicinski id, strerror(errno)); 11671bb428fSJakub Kicinski return -1; 11771bb428fSJakub Kicinski } 11871bb428fSJakub Kicinski 11971bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 12071bb428fSJakub Kicinski if (err) { 1219a5ab8bfSQuentin Monnet p_err("can't get prog info (%u): %s", 12271bb428fSJakub Kicinski id, strerror(errno)); 12371bb428fSJakub Kicinski close(fd); 12471bb428fSJakub Kicinski return -1; 12571bb428fSJakub Kicinski } 12671bb428fSJakub Kicinski 12771bb428fSJakub Kicinski if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) 12871bb428fSJakub Kicinski return fd; 12971bb428fSJakub Kicinski 13071bb428fSJakub Kicinski close(fd); 13171bb428fSJakub Kicinski } 13271bb428fSJakub Kicinski } 13371bb428fSJakub Kicinski 13471bb428fSJakub Kicinski int prog_parse_fd(int *argc, char ***argv) 13571bb428fSJakub Kicinski { 13671bb428fSJakub Kicinski int fd; 13771bb428fSJakub Kicinski 13871bb428fSJakub Kicinski if (is_prefix(**argv, "id")) { 13971bb428fSJakub Kicinski unsigned int id; 14071bb428fSJakub Kicinski char *endptr; 14171bb428fSJakub Kicinski 14271bb428fSJakub Kicinski NEXT_ARGP(); 14371bb428fSJakub Kicinski 14471bb428fSJakub Kicinski id = strtoul(**argv, &endptr, 0); 14571bb428fSJakub Kicinski if (*endptr) { 1469a5ab8bfSQuentin Monnet p_err("can't parse %s as ID", **argv); 14771bb428fSJakub Kicinski return -1; 14871bb428fSJakub Kicinski } 14971bb428fSJakub Kicinski NEXT_ARGP(); 15071bb428fSJakub Kicinski 15171bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 15271bb428fSJakub Kicinski if (fd < 0) 1539a5ab8bfSQuentin Monnet p_err("get by id (%u): %s", id, strerror(errno)); 15471bb428fSJakub Kicinski return fd; 15571bb428fSJakub Kicinski } else if (is_prefix(**argv, "tag")) { 15671bb428fSJakub Kicinski unsigned char tag[BPF_TAG_SIZE]; 15771bb428fSJakub Kicinski 15871bb428fSJakub Kicinski NEXT_ARGP(); 15971bb428fSJakub Kicinski 16071bb428fSJakub Kicinski if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, 16171bb428fSJakub Kicinski tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) 16271bb428fSJakub Kicinski != BPF_TAG_SIZE) { 1639a5ab8bfSQuentin Monnet p_err("can't parse tag"); 16471bb428fSJakub Kicinski return -1; 16571bb428fSJakub Kicinski } 16671bb428fSJakub Kicinski NEXT_ARGP(); 16771bb428fSJakub Kicinski 16871bb428fSJakub Kicinski return prog_fd_by_tag(tag); 16971bb428fSJakub Kicinski } else if (is_prefix(**argv, "pinned")) { 17071bb428fSJakub Kicinski char *path; 17171bb428fSJakub Kicinski 17271bb428fSJakub Kicinski NEXT_ARGP(); 17371bb428fSJakub Kicinski 17471bb428fSJakub Kicinski path = **argv; 17571bb428fSJakub Kicinski NEXT_ARGP(); 17671bb428fSJakub Kicinski 17771bb428fSJakub Kicinski return open_obj_pinned_any(path, BPF_OBJ_PROG); 17871bb428fSJakub Kicinski } 17971bb428fSJakub Kicinski 1809a5ab8bfSQuentin Monnet p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); 18171bb428fSJakub Kicinski return -1; 18271bb428fSJakub Kicinski } 18371bb428fSJakub Kicinski 18471bb428fSJakub Kicinski static void show_prog_maps(int fd, u32 num_maps) 18571bb428fSJakub Kicinski { 18671bb428fSJakub Kicinski struct bpf_prog_info info = {}; 18771bb428fSJakub Kicinski __u32 len = sizeof(info); 18871bb428fSJakub Kicinski __u32 map_ids[num_maps]; 18971bb428fSJakub Kicinski unsigned int i; 19071bb428fSJakub Kicinski int err; 19171bb428fSJakub Kicinski 19271bb428fSJakub Kicinski info.nr_map_ids = num_maps; 19371bb428fSJakub Kicinski info.map_ids = ptr_to_u64(map_ids); 19471bb428fSJakub Kicinski 19571bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 19671bb428fSJakub Kicinski if (err || !info.nr_map_ids) 19771bb428fSJakub Kicinski return; 19871bb428fSJakub Kicinski 199743cc665SQuentin Monnet if (json_output) { 200743cc665SQuentin Monnet jsonw_name(json_wtr, "map_ids"); 201743cc665SQuentin Monnet jsonw_start_array(json_wtr); 202743cc665SQuentin Monnet for (i = 0; i < info.nr_map_ids; i++) 203743cc665SQuentin Monnet jsonw_uint(json_wtr, map_ids[i]); 204743cc665SQuentin Monnet jsonw_end_array(json_wtr); 205743cc665SQuentin Monnet } else { 20671bb428fSJakub Kicinski printf(" map_ids "); 20771bb428fSJakub Kicinski for (i = 0; i < info.nr_map_ids; i++) 20871bb428fSJakub Kicinski printf("%u%s", map_ids[i], 20971bb428fSJakub Kicinski i == info.nr_map_ids - 1 ? "" : ","); 21071bb428fSJakub Kicinski } 21171bb428fSJakub Kicinski } 21271bb428fSJakub Kicinski 213743cc665SQuentin Monnet static void print_prog_json(struct bpf_prog_info *info, int fd) 214743cc665SQuentin Monnet { 215743cc665SQuentin Monnet char *memlock; 216743cc665SQuentin Monnet 217743cc665SQuentin Monnet jsonw_start_object(json_wtr); 218743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "id", info->id); 219743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 220743cc665SQuentin Monnet jsonw_string_field(json_wtr, "type", 221743cc665SQuentin Monnet prog_type_name[info->type]); 22271bb428fSJakub Kicinski else 223743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "type", info->type); 22471bb428fSJakub Kicinski 225743cc665SQuentin Monnet if (*info->name) 226743cc665SQuentin Monnet jsonw_string_field(json_wtr, "name", info->name); 22771bb428fSJakub Kicinski 228743cc665SQuentin Monnet jsonw_name(json_wtr, "tag"); 229743cc665SQuentin Monnet jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", 230743cc665SQuentin Monnet info->tag[0], info->tag[1], info->tag[2], info->tag[3], 231743cc665SQuentin Monnet info->tag[4], info->tag[5], info->tag[6], info->tag[7]); 23271bb428fSJakub Kicinski 233928631e0SJakub Kicinski if (info->status & BPF_PROG_STATUS_DEV_BOUND) { 234928631e0SJakub Kicinski jsonw_name(json_wtr, "dev"); 235928631e0SJakub Kicinski if (info->ifindex) { 236928631e0SJakub Kicinski char name[IF_NAMESIZE]; 237928631e0SJakub Kicinski 238928631e0SJakub Kicinski if (!if_indextoname(info->ifindex, name)) 239928631e0SJakub Kicinski jsonw_printf(json_wtr, "\"ifindex:%d\"", 240928631e0SJakub Kicinski info->ifindex); 241928631e0SJakub Kicinski else 242928631e0SJakub Kicinski jsonw_printf(json_wtr, "\"%s\"", name); 243928631e0SJakub Kicinski } else { 244928631e0SJakub Kicinski jsonw_printf(json_wtr, "\"unknown\""); 245928631e0SJakub Kicinski } 246928631e0SJakub Kicinski } 247928631e0SJakub Kicinski 248743cc665SQuentin Monnet if (info->load_time) { 24971bb428fSJakub Kicinski char buf[32]; 25071bb428fSJakub Kicinski 251743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 25271bb428fSJakub Kicinski 25371bb428fSJakub Kicinski /* Piggy back on load_time, since 0 uid is a valid one */ 254743cc665SQuentin Monnet jsonw_string_field(json_wtr, "loaded_at", buf); 255743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "uid", info->created_by_uid); 25671bb428fSJakub Kicinski } 25771bb428fSJakub Kicinski 258743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); 25971bb428fSJakub Kicinski 260743cc665SQuentin Monnet if (info->jited_prog_len) { 261743cc665SQuentin Monnet jsonw_bool_field(json_wtr, "jited", true); 262743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); 263743cc665SQuentin Monnet } else { 264743cc665SQuentin Monnet jsonw_bool_field(json_wtr, "jited", false); 265743cc665SQuentin Monnet } 266743cc665SQuentin Monnet 267743cc665SQuentin Monnet memlock = get_fdinfo(fd, "memlock"); 268743cc665SQuentin Monnet if (memlock) 269743cc665SQuentin Monnet jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); 270743cc665SQuentin Monnet free(memlock); 271743cc665SQuentin Monnet 272743cc665SQuentin Monnet if (info->nr_map_ids) 273743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 274743cc665SQuentin Monnet 275*4990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 276*4990f1f4SPrashant Bhole struct pinned_obj *obj; 277*4990f1f4SPrashant Bhole 278*4990f1f4SPrashant Bhole jsonw_name(json_wtr, "pinned"); 279*4990f1f4SPrashant Bhole jsonw_start_array(json_wtr); 280*4990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 281*4990f1f4SPrashant Bhole if (obj->id == info->id) 282*4990f1f4SPrashant Bhole jsonw_string(json_wtr, obj->path); 283*4990f1f4SPrashant Bhole } 284*4990f1f4SPrashant Bhole jsonw_end_array(json_wtr); 285*4990f1f4SPrashant Bhole } 286*4990f1f4SPrashant Bhole 287743cc665SQuentin Monnet jsonw_end_object(json_wtr); 288743cc665SQuentin Monnet } 289743cc665SQuentin Monnet 290743cc665SQuentin Monnet static void print_prog_plain(struct bpf_prog_info *info, int fd) 291743cc665SQuentin Monnet { 292743cc665SQuentin Monnet char *memlock; 293743cc665SQuentin Monnet 294743cc665SQuentin Monnet printf("%u: ", info->id); 295743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 296743cc665SQuentin Monnet printf("%s ", prog_type_name[info->type]); 297743cc665SQuentin Monnet else 298743cc665SQuentin Monnet printf("type %u ", info->type); 299743cc665SQuentin Monnet 300743cc665SQuentin Monnet if (*info->name) 301743cc665SQuentin Monnet printf("name %s ", info->name); 302743cc665SQuentin Monnet 303743cc665SQuentin Monnet printf("tag "); 304743cc665SQuentin Monnet fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); 305928631e0SJakub Kicinski printf(" "); 306928631e0SJakub Kicinski 307928631e0SJakub Kicinski if (info->status & BPF_PROG_STATUS_DEV_BOUND) { 308928631e0SJakub Kicinski printf("dev "); 309928631e0SJakub Kicinski if (info->ifindex) { 310928631e0SJakub Kicinski char name[IF_NAMESIZE]; 311928631e0SJakub Kicinski 312928631e0SJakub Kicinski if (!if_indextoname(info->ifindex, name)) 313928631e0SJakub Kicinski printf("ifindex:%d ", info->ifindex); 314928631e0SJakub Kicinski else 315928631e0SJakub Kicinski printf("%s ", name); 316928631e0SJakub Kicinski } else { 317928631e0SJakub Kicinski printf("unknown "); 318928631e0SJakub Kicinski } 319928631e0SJakub Kicinski } 320743cc665SQuentin Monnet printf("\n"); 321743cc665SQuentin Monnet 322743cc665SQuentin Monnet if (info->load_time) { 323743cc665SQuentin Monnet char buf[32]; 324743cc665SQuentin Monnet 325743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 326743cc665SQuentin Monnet 327743cc665SQuentin Monnet /* Piggy back on load_time, since 0 uid is a valid one */ 328743cc665SQuentin Monnet printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); 329743cc665SQuentin Monnet } 330743cc665SQuentin Monnet 331743cc665SQuentin Monnet printf("\txlated %uB", info->xlated_prog_len); 332743cc665SQuentin Monnet 333743cc665SQuentin Monnet if (info->jited_prog_len) 334743cc665SQuentin Monnet printf(" jited %uB", info->jited_prog_len); 33571bb428fSJakub Kicinski else 33671bb428fSJakub Kicinski printf(" not jited"); 33771bb428fSJakub Kicinski 33871bb428fSJakub Kicinski memlock = get_fdinfo(fd, "memlock"); 33971bb428fSJakub Kicinski if (memlock) 34071bb428fSJakub Kicinski printf(" memlock %sB", memlock); 34171bb428fSJakub Kicinski free(memlock); 34271bb428fSJakub Kicinski 343743cc665SQuentin Monnet if (info->nr_map_ids) 344743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 34571bb428fSJakub Kicinski 346*4990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 347*4990f1f4SPrashant Bhole struct pinned_obj *obj; 348*4990f1f4SPrashant Bhole 349*4990f1f4SPrashant Bhole printf("\n"); 350*4990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 351*4990f1f4SPrashant Bhole if (obj->id == info->id) 352*4990f1f4SPrashant Bhole printf("\tpinned %s\n", obj->path); 353*4990f1f4SPrashant Bhole } 354*4990f1f4SPrashant Bhole } 355*4990f1f4SPrashant Bhole 35671bb428fSJakub Kicinski printf("\n"); 357743cc665SQuentin Monnet } 358743cc665SQuentin Monnet 359743cc665SQuentin Monnet static int show_prog(int fd) 360743cc665SQuentin Monnet { 361743cc665SQuentin Monnet struct bpf_prog_info info = {}; 362743cc665SQuentin Monnet __u32 len = sizeof(info); 363743cc665SQuentin Monnet int err; 364743cc665SQuentin Monnet 365743cc665SQuentin Monnet err = bpf_obj_get_info_by_fd(fd, &info, &len); 366743cc665SQuentin Monnet if (err) { 3679a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 368743cc665SQuentin Monnet return -1; 369743cc665SQuentin Monnet } 370743cc665SQuentin Monnet 371743cc665SQuentin Monnet if (json_output) 372743cc665SQuentin Monnet print_prog_json(&info, fd); 373743cc665SQuentin Monnet else 374743cc665SQuentin Monnet print_prog_plain(&info, fd); 37571bb428fSJakub Kicinski 37671bb428fSJakub Kicinski return 0; 37771bb428fSJakub Kicinski } 37871bb428fSJakub Kicinski 37971bb428fSJakub Kicinski static int do_show(int argc, char **argv) 380743cc665SQuentin Monnet { 381743cc665SQuentin Monnet __u32 id = 0; 38271bb428fSJakub Kicinski int err; 38371bb428fSJakub Kicinski int fd; 38471bb428fSJakub Kicinski 385*4990f1f4SPrashant Bhole build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); 386*4990f1f4SPrashant Bhole 38771bb428fSJakub Kicinski if (argc == 2) { 38871bb428fSJakub Kicinski fd = prog_parse_fd(&argc, &argv); 38971bb428fSJakub Kicinski if (fd < 0) 39071bb428fSJakub Kicinski return -1; 39171bb428fSJakub Kicinski 39271bb428fSJakub Kicinski return show_prog(fd); 39371bb428fSJakub Kicinski } 39471bb428fSJakub Kicinski 39571bb428fSJakub Kicinski if (argc) 39671bb428fSJakub Kicinski return BAD_ARG(); 39771bb428fSJakub Kicinski 398743cc665SQuentin Monnet if (json_output) 399743cc665SQuentin Monnet jsonw_start_array(json_wtr); 40071bb428fSJakub Kicinski while (true) { 40171bb428fSJakub Kicinski err = bpf_prog_get_next_id(id, &id); 40271bb428fSJakub Kicinski if (err) { 4031739c26dSQuentin Monnet if (errno == ENOENT) { 4041739c26dSQuentin Monnet err = 0; 40571bb428fSJakub Kicinski break; 4061739c26dSQuentin Monnet } 4079a5ab8bfSQuentin Monnet p_err("can't get next program: %s%s", strerror(errno), 4089a5ab8bfSQuentin Monnet errno == EINVAL ? " -- kernel too old?" : ""); 409743cc665SQuentin Monnet err = -1; 410743cc665SQuentin Monnet break; 41171bb428fSJakub Kicinski } 41271bb428fSJakub Kicinski 41371bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 41471bb428fSJakub Kicinski if (fd < 0) { 4159a5ab8bfSQuentin Monnet p_err("can't get prog by id (%u): %s", 41671bb428fSJakub Kicinski id, strerror(errno)); 417743cc665SQuentin Monnet err = -1; 418743cc665SQuentin Monnet break; 41971bb428fSJakub Kicinski } 42071bb428fSJakub Kicinski 42171bb428fSJakub Kicinski err = show_prog(fd); 42271bb428fSJakub Kicinski close(fd); 42371bb428fSJakub Kicinski if (err) 424743cc665SQuentin Monnet break; 42571bb428fSJakub Kicinski } 42671bb428fSJakub Kicinski 427743cc665SQuentin Monnet if (json_output) 428743cc665SQuentin Monnet jsonw_end_array(json_wtr); 429743cc665SQuentin Monnet 430743cc665SQuentin Monnet return err; 43171bb428fSJakub Kicinski } 43271bb428fSJakub Kicinski 433c9c35995SJakub Kicinski static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) 434c9c35995SJakub Kicinski { 435c9c35995SJakub Kicinski va_list args; 436c9c35995SJakub Kicinski 437c9c35995SJakub Kicinski va_start(args, fmt); 438c9c35995SJakub Kicinski vprintf(fmt, args); 439c9c35995SJakub Kicinski va_end(args); 440c9c35995SJakub Kicinski } 441c9c35995SJakub Kicinski 442f05e2c32SQuentin Monnet static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes) 443c9c35995SJakub Kicinski { 444c9c35995SJakub Kicinski struct bpf_insn *insn = buf; 4459e2308c1SQuentin Monnet bool double_insn = false; 446c9c35995SJakub Kicinski unsigned int i; 447c9c35995SJakub Kicinski 448c9c35995SJakub Kicinski for (i = 0; i < len / sizeof(*insn); i++) { 4499e2308c1SQuentin Monnet if (double_insn) { 4509e2308c1SQuentin Monnet double_insn = false; 4519e2308c1SQuentin Monnet continue; 4529e2308c1SQuentin Monnet } 4539e2308c1SQuentin Monnet 4549e2308c1SQuentin Monnet double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 4559e2308c1SQuentin Monnet 456c9c35995SJakub Kicinski printf("% 4d: ", i); 457c9c35995SJakub Kicinski print_bpf_insn(print_insn, NULL, insn + i, true); 458c9c35995SJakub Kicinski 459c9c35995SJakub Kicinski if (opcodes) { 460c9c35995SJakub Kicinski printf(" "); 4619cbe1f58SQuentin Monnet fprint_hex(stdout, insn + i, 8, " "); 4629e2308c1SQuentin Monnet if (double_insn && i < len - 1) { 4639e2308c1SQuentin Monnet printf(" "); 4649e2308c1SQuentin Monnet fprint_hex(stdout, insn + i + 1, 8, " "); 4659e2308c1SQuentin Monnet } 466c9c35995SJakub Kicinski printf("\n"); 467c9c35995SJakub Kicinski } 468c9c35995SJakub Kicinski } 469c9c35995SJakub Kicinski } 470c9c35995SJakub Kicinski 471f05e2c32SQuentin Monnet static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) 472f05e2c32SQuentin Monnet { 473f05e2c32SQuentin Monnet unsigned int l = strlen(fmt); 474f05e2c32SQuentin Monnet char chomped_fmt[l]; 475f05e2c32SQuentin Monnet va_list args; 476f05e2c32SQuentin Monnet 477f05e2c32SQuentin Monnet va_start(args, fmt); 478f05e2c32SQuentin Monnet if (l > 0) { 479f05e2c32SQuentin Monnet strncpy(chomped_fmt, fmt, l - 1); 480f05e2c32SQuentin Monnet chomped_fmt[l - 1] = '\0'; 481f05e2c32SQuentin Monnet } 482f05e2c32SQuentin Monnet jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); 483f05e2c32SQuentin Monnet va_end(args); 484f05e2c32SQuentin Monnet } 485f05e2c32SQuentin Monnet 486f05e2c32SQuentin Monnet static void dump_xlated_json(void *buf, unsigned int len, bool opcodes) 487f05e2c32SQuentin Monnet { 488f05e2c32SQuentin Monnet struct bpf_insn *insn = buf; 489f05e2c32SQuentin Monnet bool double_insn = false; 490f05e2c32SQuentin Monnet unsigned int i; 491f05e2c32SQuentin Monnet 492f05e2c32SQuentin Monnet jsonw_start_array(json_wtr); 493f05e2c32SQuentin Monnet for (i = 0; i < len / sizeof(*insn); i++) { 494f05e2c32SQuentin Monnet if (double_insn) { 495f05e2c32SQuentin Monnet double_insn = false; 496f05e2c32SQuentin Monnet continue; 497f05e2c32SQuentin Monnet } 498f05e2c32SQuentin Monnet double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 499f05e2c32SQuentin Monnet 500f05e2c32SQuentin Monnet jsonw_start_object(json_wtr); 501f05e2c32SQuentin Monnet jsonw_name(json_wtr, "disasm"); 502f05e2c32SQuentin Monnet print_bpf_insn(print_insn_json, NULL, insn + i, true); 503f05e2c32SQuentin Monnet 504f05e2c32SQuentin Monnet if (opcodes) { 505f05e2c32SQuentin Monnet jsonw_name(json_wtr, "opcodes"); 506f05e2c32SQuentin Monnet jsonw_start_object(json_wtr); 507f05e2c32SQuentin Monnet 508f05e2c32SQuentin Monnet jsonw_name(json_wtr, "code"); 509f05e2c32SQuentin Monnet jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); 510f05e2c32SQuentin Monnet 511f05e2c32SQuentin Monnet jsonw_name(json_wtr, "src_reg"); 512f05e2c32SQuentin Monnet jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); 513f05e2c32SQuentin Monnet 514f05e2c32SQuentin Monnet jsonw_name(json_wtr, "dst_reg"); 515f05e2c32SQuentin Monnet jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); 516f05e2c32SQuentin Monnet 517f05e2c32SQuentin Monnet jsonw_name(json_wtr, "off"); 518f05e2c32SQuentin Monnet print_hex_data_json((uint8_t *)(&insn[i].off), 2); 519f05e2c32SQuentin Monnet 520f05e2c32SQuentin Monnet jsonw_name(json_wtr, "imm"); 521f05e2c32SQuentin Monnet if (double_insn && i < len - 1) 522f05e2c32SQuentin Monnet print_hex_data_json((uint8_t *)(&insn[i].imm), 523f05e2c32SQuentin Monnet 12); 524f05e2c32SQuentin Monnet else 525f05e2c32SQuentin Monnet print_hex_data_json((uint8_t *)(&insn[i].imm), 526f05e2c32SQuentin Monnet 4); 527f05e2c32SQuentin Monnet jsonw_end_object(json_wtr); 528f05e2c32SQuentin Monnet } 529f05e2c32SQuentin Monnet jsonw_end_object(json_wtr); 530f05e2c32SQuentin Monnet } 531f05e2c32SQuentin Monnet jsonw_end_array(json_wtr); 532f05e2c32SQuentin Monnet } 533f05e2c32SQuentin Monnet 53471bb428fSJakub Kicinski static int do_dump(int argc, char **argv) 53571bb428fSJakub Kicinski { 53671bb428fSJakub Kicinski struct bpf_prog_info info = {}; 53771bb428fSJakub Kicinski __u32 len = sizeof(info); 53871bb428fSJakub Kicinski unsigned int buf_size; 53971bb428fSJakub Kicinski char *filepath = NULL; 54071bb428fSJakub Kicinski bool opcodes = false; 54171bb428fSJakub Kicinski unsigned char *buf; 54271bb428fSJakub Kicinski __u32 *member_len; 54371bb428fSJakub Kicinski __u64 *member_ptr; 54471bb428fSJakub Kicinski ssize_t n; 54571bb428fSJakub Kicinski int err; 54671bb428fSJakub Kicinski int fd; 54771bb428fSJakub Kicinski 54871bb428fSJakub Kicinski if (is_prefix(*argv, "jited")) { 54971bb428fSJakub Kicinski member_len = &info.jited_prog_len; 55071bb428fSJakub Kicinski member_ptr = &info.jited_prog_insns; 55171bb428fSJakub Kicinski } else if (is_prefix(*argv, "xlated")) { 55271bb428fSJakub Kicinski member_len = &info.xlated_prog_len; 55371bb428fSJakub Kicinski member_ptr = &info.xlated_prog_insns; 55471bb428fSJakub Kicinski } else { 5559a5ab8bfSQuentin Monnet p_err("expected 'xlated' or 'jited', got: %s", *argv); 55671bb428fSJakub Kicinski return -1; 55771bb428fSJakub Kicinski } 55871bb428fSJakub Kicinski NEXT_ARG(); 55971bb428fSJakub Kicinski 56071bb428fSJakub Kicinski if (argc < 2) 56171bb428fSJakub Kicinski usage(); 56271bb428fSJakub Kicinski 56371bb428fSJakub Kicinski fd = prog_parse_fd(&argc, &argv); 56471bb428fSJakub Kicinski if (fd < 0) 56571bb428fSJakub Kicinski return -1; 56671bb428fSJakub Kicinski 56771bb428fSJakub Kicinski if (is_prefix(*argv, "file")) { 56871bb428fSJakub Kicinski NEXT_ARG(); 56971bb428fSJakub Kicinski if (!argc) { 5709a5ab8bfSQuentin Monnet p_err("expected file path"); 57171bb428fSJakub Kicinski return -1; 57271bb428fSJakub Kicinski } 57371bb428fSJakub Kicinski 57471bb428fSJakub Kicinski filepath = *argv; 57571bb428fSJakub Kicinski NEXT_ARG(); 57671bb428fSJakub Kicinski } else if (is_prefix(*argv, "opcodes")) { 57771bb428fSJakub Kicinski opcodes = true; 57871bb428fSJakub Kicinski NEXT_ARG(); 57971bb428fSJakub Kicinski } 58071bb428fSJakub Kicinski 58171bb428fSJakub Kicinski if (argc) { 58271bb428fSJakub Kicinski usage(); 58371bb428fSJakub Kicinski return -1; 58471bb428fSJakub Kicinski } 58571bb428fSJakub Kicinski 58671bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 58771bb428fSJakub Kicinski if (err) { 5889a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 58971bb428fSJakub Kicinski return -1; 59071bb428fSJakub Kicinski } 59171bb428fSJakub Kicinski 59271bb428fSJakub Kicinski if (!*member_len) { 5939a5ab8bfSQuentin Monnet p_info("no instructions returned"); 59471bb428fSJakub Kicinski close(fd); 59571bb428fSJakub Kicinski return 0; 59671bb428fSJakub Kicinski } 59771bb428fSJakub Kicinski 59871bb428fSJakub Kicinski buf_size = *member_len; 59971bb428fSJakub Kicinski 60071bb428fSJakub Kicinski buf = malloc(buf_size); 60171bb428fSJakub Kicinski if (!buf) { 6029a5ab8bfSQuentin Monnet p_err("mem alloc failed"); 60371bb428fSJakub Kicinski close(fd); 60471bb428fSJakub Kicinski return -1; 60571bb428fSJakub Kicinski } 60671bb428fSJakub Kicinski 60771bb428fSJakub Kicinski memset(&info, 0, sizeof(info)); 60871bb428fSJakub Kicinski 60971bb428fSJakub Kicinski *member_ptr = ptr_to_u64(buf); 61071bb428fSJakub Kicinski *member_len = buf_size; 61171bb428fSJakub Kicinski 61271bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 61371bb428fSJakub Kicinski close(fd); 61471bb428fSJakub Kicinski if (err) { 6159a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 61671bb428fSJakub Kicinski goto err_free; 61771bb428fSJakub Kicinski } 61871bb428fSJakub Kicinski 61971bb428fSJakub Kicinski if (*member_len > buf_size) { 6209a5ab8bfSQuentin Monnet p_err("too many instructions returned"); 62171bb428fSJakub Kicinski goto err_free; 62271bb428fSJakub Kicinski } 62371bb428fSJakub Kicinski 62471bb428fSJakub Kicinski if (filepath) { 62571bb428fSJakub Kicinski fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 62671bb428fSJakub Kicinski if (fd < 0) { 6279a5ab8bfSQuentin Monnet p_err("can't open file %s: %s", filepath, 62871bb428fSJakub Kicinski strerror(errno)); 62971bb428fSJakub Kicinski goto err_free; 63071bb428fSJakub Kicinski } 63171bb428fSJakub Kicinski 63271bb428fSJakub Kicinski n = write(fd, buf, *member_len); 63371bb428fSJakub Kicinski close(fd); 63471bb428fSJakub Kicinski if (n != *member_len) { 6359a5ab8bfSQuentin Monnet p_err("error writing output file: %s", 63671bb428fSJakub Kicinski n < 0 ? strerror(errno) : "short write"); 63771bb428fSJakub Kicinski goto err_free; 63871bb428fSJakub Kicinski } 63971bb428fSJakub Kicinski } else { 640c9c35995SJakub Kicinski if (member_len == &info.jited_prog_len) 64171bb428fSJakub Kicinski disasm_print_insn(buf, *member_len, opcodes); 642c9c35995SJakub Kicinski else 643f05e2c32SQuentin Monnet if (json_output) 644f05e2c32SQuentin Monnet dump_xlated_json(buf, *member_len, opcodes); 645f05e2c32SQuentin Monnet else 646f05e2c32SQuentin Monnet dump_xlated_plain(buf, *member_len, opcodes); 64771bb428fSJakub Kicinski } 64871bb428fSJakub Kicinski 64971bb428fSJakub Kicinski free(buf); 65071bb428fSJakub Kicinski 65171bb428fSJakub Kicinski return 0; 65271bb428fSJakub Kicinski 65371bb428fSJakub Kicinski err_free: 65471bb428fSJakub Kicinski free(buf); 65571bb428fSJakub Kicinski return -1; 65671bb428fSJakub Kicinski } 65771bb428fSJakub Kicinski 65871bb428fSJakub Kicinski static int do_pin(int argc, char **argv) 65971bb428fSJakub Kicinski { 660004b45c0SQuentin Monnet int err; 661004b45c0SQuentin Monnet 662004b45c0SQuentin Monnet err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); 663004b45c0SQuentin Monnet if (!err && json_output) 664004b45c0SQuentin Monnet jsonw_null(json_wtr); 665004b45c0SQuentin Monnet return err; 66671bb428fSJakub Kicinski } 66771bb428fSJakub Kicinski 66871bb428fSJakub Kicinski static int do_help(int argc, char **argv) 66971bb428fSJakub Kicinski { 670004b45c0SQuentin Monnet if (json_output) { 671004b45c0SQuentin Monnet jsonw_null(json_wtr); 672004b45c0SQuentin Monnet return 0; 673004b45c0SQuentin Monnet } 674004b45c0SQuentin Monnet 67571bb428fSJakub Kicinski fprintf(stderr, 67671bb428fSJakub Kicinski "Usage: %s %s show [PROG]\n" 6778dfbc6d1SQuentin Monnet " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" 6788dfbc6d1SQuentin Monnet " %s %s dump jited PROG [{ file FILE | opcodes }]\n" 67971bb428fSJakub Kicinski " %s %s pin PROG FILE\n" 68071bb428fSJakub Kicinski " %s %s help\n" 68171bb428fSJakub Kicinski "\n" 68271bb428fSJakub Kicinski " " HELP_SPEC_PROGRAM "\n" 6830641c3c8SQuentin Monnet " " HELP_SPEC_OPTIONS "\n" 68471bb428fSJakub Kicinski "", 68571bb428fSJakub Kicinski bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 68671bb428fSJakub Kicinski bin_name, argv[-2], bin_name, argv[-2]); 68771bb428fSJakub Kicinski 68871bb428fSJakub Kicinski return 0; 68971bb428fSJakub Kicinski } 69071bb428fSJakub Kicinski 69171bb428fSJakub Kicinski static const struct cmd cmds[] = { 69271bb428fSJakub Kicinski { "show", do_show }, 6939f606179SQuentin Monnet { "help", do_help }, 69471bb428fSJakub Kicinski { "dump", do_dump }, 69571bb428fSJakub Kicinski { "pin", do_pin }, 69671bb428fSJakub Kicinski { 0 } 69771bb428fSJakub Kicinski }; 69871bb428fSJakub Kicinski 69971bb428fSJakub Kicinski int do_prog(int argc, char **argv) 70071bb428fSJakub Kicinski { 70171bb428fSJakub Kicinski return cmd_select(cmds, argc, argv, do_help); 70271bb428fSJakub Kicinski } 703