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> 4471bb428fSJakub Kicinski #include <sys/types.h> 4571bb428fSJakub Kicinski #include <sys/stat.h> 4671bb428fSJakub Kicinski 4771bb428fSJakub Kicinski #include <bpf.h> 4849a086c2SRoman Gushchin #include <libbpf.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", 6945e5e121SRoman Gushchin [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device", 7071bb428fSJakub Kicinski }; 7171bb428fSJakub Kicinski 7271bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) 7371bb428fSJakub Kicinski { 7471bb428fSJakub Kicinski struct timespec real_time_ts, boot_time_ts; 7571bb428fSJakub Kicinski time_t wallclock_secs; 7671bb428fSJakub Kicinski struct tm load_tm; 7771bb428fSJakub Kicinski 7871bb428fSJakub Kicinski buf[--size] = '\0'; 7971bb428fSJakub Kicinski 8071bb428fSJakub Kicinski if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || 8171bb428fSJakub Kicinski clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { 8271bb428fSJakub Kicinski perror("Can't read clocks"); 8371bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 8471bb428fSJakub Kicinski return; 8571bb428fSJakub Kicinski } 8671bb428fSJakub Kicinski 8771bb428fSJakub Kicinski wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + 8871bb428fSJakub Kicinski nsecs / 1000000000; 8971bb428fSJakub Kicinski 9071bb428fSJakub Kicinski if (!localtime_r(&wallclock_secs, &load_tm)) { 9171bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 9271bb428fSJakub Kicinski return; 9371bb428fSJakub Kicinski } 9471bb428fSJakub Kicinski 9571bb428fSJakub Kicinski strftime(buf, size, "%b %d/%H:%M", &load_tm); 9671bb428fSJakub Kicinski } 9771bb428fSJakub Kicinski 9871bb428fSJakub Kicinski static int prog_fd_by_tag(unsigned char *tag) 9971bb428fSJakub Kicinski { 10071bb428fSJakub Kicinski struct bpf_prog_info info = {}; 10171bb428fSJakub Kicinski __u32 len = sizeof(info); 10271bb428fSJakub Kicinski unsigned int id = 0; 10371bb428fSJakub Kicinski int err; 10471bb428fSJakub Kicinski int fd; 10571bb428fSJakub Kicinski 10671bb428fSJakub Kicinski while (true) { 10771bb428fSJakub Kicinski err = bpf_prog_get_next_id(id, &id); 10871bb428fSJakub Kicinski if (err) { 1099a5ab8bfSQuentin Monnet p_err("%s", strerror(errno)); 11071bb428fSJakub Kicinski return -1; 11171bb428fSJakub Kicinski } 11271bb428fSJakub Kicinski 11371bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 11471bb428fSJakub Kicinski if (fd < 0) { 1159a5ab8bfSQuentin Monnet p_err("can't get prog by id (%u): %s", 11671bb428fSJakub Kicinski id, strerror(errno)); 11771bb428fSJakub Kicinski return -1; 11871bb428fSJakub Kicinski } 11971bb428fSJakub Kicinski 12071bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 12171bb428fSJakub Kicinski if (err) { 1229a5ab8bfSQuentin Monnet p_err("can't get prog info (%u): %s", 12371bb428fSJakub Kicinski id, strerror(errno)); 12471bb428fSJakub Kicinski close(fd); 12571bb428fSJakub Kicinski return -1; 12671bb428fSJakub Kicinski } 12771bb428fSJakub Kicinski 12871bb428fSJakub Kicinski if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) 12971bb428fSJakub Kicinski return fd; 13071bb428fSJakub Kicinski 13171bb428fSJakub Kicinski close(fd); 13271bb428fSJakub Kicinski } 13371bb428fSJakub Kicinski } 13471bb428fSJakub Kicinski 13571bb428fSJakub Kicinski int prog_parse_fd(int *argc, char ***argv) 13671bb428fSJakub Kicinski { 13771bb428fSJakub Kicinski int fd; 13871bb428fSJakub Kicinski 13971bb428fSJakub Kicinski if (is_prefix(**argv, "id")) { 14071bb428fSJakub Kicinski unsigned int id; 14171bb428fSJakub Kicinski char *endptr; 14271bb428fSJakub Kicinski 14371bb428fSJakub Kicinski NEXT_ARGP(); 14471bb428fSJakub Kicinski 14571bb428fSJakub Kicinski id = strtoul(**argv, &endptr, 0); 14671bb428fSJakub Kicinski if (*endptr) { 1479a5ab8bfSQuentin Monnet p_err("can't parse %s as ID", **argv); 14871bb428fSJakub Kicinski return -1; 14971bb428fSJakub Kicinski } 15071bb428fSJakub Kicinski NEXT_ARGP(); 15171bb428fSJakub Kicinski 15271bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 15371bb428fSJakub Kicinski if (fd < 0) 1549a5ab8bfSQuentin Monnet p_err("get by id (%u): %s", id, strerror(errno)); 15571bb428fSJakub Kicinski return fd; 15671bb428fSJakub Kicinski } else if (is_prefix(**argv, "tag")) { 15771bb428fSJakub Kicinski unsigned char tag[BPF_TAG_SIZE]; 15871bb428fSJakub Kicinski 15971bb428fSJakub Kicinski NEXT_ARGP(); 16071bb428fSJakub Kicinski 16171bb428fSJakub Kicinski if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, 16271bb428fSJakub Kicinski tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) 16371bb428fSJakub Kicinski != BPF_TAG_SIZE) { 1649a5ab8bfSQuentin Monnet p_err("can't parse tag"); 16571bb428fSJakub Kicinski return -1; 16671bb428fSJakub Kicinski } 16771bb428fSJakub Kicinski NEXT_ARGP(); 16871bb428fSJakub Kicinski 16971bb428fSJakub Kicinski return prog_fd_by_tag(tag); 17071bb428fSJakub Kicinski } else if (is_prefix(**argv, "pinned")) { 17171bb428fSJakub Kicinski char *path; 17271bb428fSJakub Kicinski 17371bb428fSJakub Kicinski NEXT_ARGP(); 17471bb428fSJakub Kicinski 17571bb428fSJakub Kicinski path = **argv; 17671bb428fSJakub Kicinski NEXT_ARGP(); 17771bb428fSJakub Kicinski 17871bb428fSJakub Kicinski return open_obj_pinned_any(path, BPF_OBJ_PROG); 17971bb428fSJakub Kicinski } 18071bb428fSJakub Kicinski 1819a5ab8bfSQuentin Monnet p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); 18271bb428fSJakub Kicinski return -1; 18371bb428fSJakub Kicinski } 18471bb428fSJakub Kicinski 18571bb428fSJakub Kicinski static void show_prog_maps(int fd, u32 num_maps) 18671bb428fSJakub Kicinski { 18771bb428fSJakub Kicinski struct bpf_prog_info info = {}; 18871bb428fSJakub Kicinski __u32 len = sizeof(info); 18971bb428fSJakub Kicinski __u32 map_ids[num_maps]; 19071bb428fSJakub Kicinski unsigned int i; 19171bb428fSJakub Kicinski int err; 19271bb428fSJakub Kicinski 19371bb428fSJakub Kicinski info.nr_map_ids = num_maps; 19471bb428fSJakub Kicinski info.map_ids = ptr_to_u64(map_ids); 19571bb428fSJakub Kicinski 19671bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 19771bb428fSJakub Kicinski if (err || !info.nr_map_ids) 19871bb428fSJakub Kicinski return; 19971bb428fSJakub Kicinski 200743cc665SQuentin Monnet if (json_output) { 201743cc665SQuentin Monnet jsonw_name(json_wtr, "map_ids"); 202743cc665SQuentin Monnet jsonw_start_array(json_wtr); 203743cc665SQuentin Monnet for (i = 0; i < info.nr_map_ids; i++) 204743cc665SQuentin Monnet jsonw_uint(json_wtr, map_ids[i]); 205743cc665SQuentin Monnet jsonw_end_array(json_wtr); 206743cc665SQuentin Monnet } else { 20771bb428fSJakub Kicinski printf(" map_ids "); 20871bb428fSJakub Kicinski for (i = 0; i < info.nr_map_ids; i++) 20971bb428fSJakub Kicinski printf("%u%s", map_ids[i], 21071bb428fSJakub Kicinski i == info.nr_map_ids - 1 ? "" : ","); 21171bb428fSJakub Kicinski } 21271bb428fSJakub Kicinski } 21371bb428fSJakub Kicinski 214743cc665SQuentin Monnet static void print_prog_json(struct bpf_prog_info *info, int fd) 215743cc665SQuentin Monnet { 216743cc665SQuentin Monnet char *memlock; 217743cc665SQuentin Monnet 218743cc665SQuentin Monnet jsonw_start_object(json_wtr); 219743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "id", info->id); 220743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 221743cc665SQuentin Monnet jsonw_string_field(json_wtr, "type", 222743cc665SQuentin Monnet prog_type_name[info->type]); 22371bb428fSJakub Kicinski else 224743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "type", info->type); 22571bb428fSJakub Kicinski 226743cc665SQuentin Monnet if (*info->name) 227743cc665SQuentin Monnet jsonw_string_field(json_wtr, "name", info->name); 22871bb428fSJakub Kicinski 229743cc665SQuentin Monnet jsonw_name(json_wtr, "tag"); 230743cc665SQuentin Monnet jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", 231743cc665SQuentin Monnet info->tag[0], info->tag[1], info->tag[2], info->tag[3], 232743cc665SQuentin Monnet info->tag[4], info->tag[5], info->tag[6], info->tag[7]); 23371bb428fSJakub Kicinski 23452262210SJakub Kicinski print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); 23552262210SJakub Kicinski 236743cc665SQuentin Monnet if (info->load_time) { 23771bb428fSJakub Kicinski char buf[32]; 23871bb428fSJakub Kicinski 239743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 24071bb428fSJakub Kicinski 24171bb428fSJakub Kicinski /* Piggy back on load_time, since 0 uid is a valid one */ 242743cc665SQuentin Monnet jsonw_string_field(json_wtr, "loaded_at", buf); 243743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "uid", info->created_by_uid); 24471bb428fSJakub Kicinski } 24571bb428fSJakub Kicinski 246743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); 24771bb428fSJakub Kicinski 248743cc665SQuentin Monnet if (info->jited_prog_len) { 249743cc665SQuentin Monnet jsonw_bool_field(json_wtr, "jited", true); 250743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); 251743cc665SQuentin Monnet } else { 252743cc665SQuentin Monnet jsonw_bool_field(json_wtr, "jited", false); 253743cc665SQuentin Monnet } 254743cc665SQuentin Monnet 255743cc665SQuentin Monnet memlock = get_fdinfo(fd, "memlock"); 256743cc665SQuentin Monnet if (memlock) 257743cc665SQuentin Monnet jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); 258743cc665SQuentin Monnet free(memlock); 259743cc665SQuentin Monnet 260743cc665SQuentin Monnet if (info->nr_map_ids) 261743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 262743cc665SQuentin Monnet 2634990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 2644990f1f4SPrashant Bhole struct pinned_obj *obj; 2654990f1f4SPrashant Bhole 2664990f1f4SPrashant Bhole jsonw_name(json_wtr, "pinned"); 2674990f1f4SPrashant Bhole jsonw_start_array(json_wtr); 2684990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 2694990f1f4SPrashant Bhole if (obj->id == info->id) 2704990f1f4SPrashant Bhole jsonw_string(json_wtr, obj->path); 2714990f1f4SPrashant Bhole } 2724990f1f4SPrashant Bhole jsonw_end_array(json_wtr); 2734990f1f4SPrashant Bhole } 2744990f1f4SPrashant Bhole 275743cc665SQuentin Monnet jsonw_end_object(json_wtr); 276743cc665SQuentin Monnet } 277743cc665SQuentin Monnet 278743cc665SQuentin Monnet static void print_prog_plain(struct bpf_prog_info *info, int fd) 279743cc665SQuentin Monnet { 280743cc665SQuentin Monnet char *memlock; 281743cc665SQuentin Monnet 282743cc665SQuentin Monnet printf("%u: ", info->id); 283743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 284743cc665SQuentin Monnet printf("%s ", prog_type_name[info->type]); 285743cc665SQuentin Monnet else 286743cc665SQuentin Monnet printf("type %u ", info->type); 287743cc665SQuentin Monnet 288743cc665SQuentin Monnet if (*info->name) 289743cc665SQuentin Monnet printf("name %s ", info->name); 290743cc665SQuentin Monnet 291743cc665SQuentin Monnet printf("tag "); 292743cc665SQuentin Monnet fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); 29352262210SJakub Kicinski print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); 294743cc665SQuentin Monnet printf("\n"); 295743cc665SQuentin Monnet 296743cc665SQuentin Monnet if (info->load_time) { 297743cc665SQuentin Monnet char buf[32]; 298743cc665SQuentin Monnet 299743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 300743cc665SQuentin Monnet 301743cc665SQuentin Monnet /* Piggy back on load_time, since 0 uid is a valid one */ 302743cc665SQuentin Monnet printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); 303743cc665SQuentin Monnet } 304743cc665SQuentin Monnet 305743cc665SQuentin Monnet printf("\txlated %uB", info->xlated_prog_len); 306743cc665SQuentin Monnet 307743cc665SQuentin Monnet if (info->jited_prog_len) 308743cc665SQuentin Monnet printf(" jited %uB", info->jited_prog_len); 30971bb428fSJakub Kicinski else 31071bb428fSJakub Kicinski printf(" not jited"); 31171bb428fSJakub Kicinski 31271bb428fSJakub Kicinski memlock = get_fdinfo(fd, "memlock"); 31371bb428fSJakub Kicinski if (memlock) 31471bb428fSJakub Kicinski printf(" memlock %sB", memlock); 31571bb428fSJakub Kicinski free(memlock); 31671bb428fSJakub Kicinski 317743cc665SQuentin Monnet if (info->nr_map_ids) 318743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 31971bb428fSJakub Kicinski 3204990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 3214990f1f4SPrashant Bhole struct pinned_obj *obj; 3224990f1f4SPrashant Bhole 3234990f1f4SPrashant Bhole printf("\n"); 3244990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 3254990f1f4SPrashant Bhole if (obj->id == info->id) 3264990f1f4SPrashant Bhole printf("\tpinned %s\n", obj->path); 3274990f1f4SPrashant Bhole } 3284990f1f4SPrashant Bhole } 3294990f1f4SPrashant Bhole 33071bb428fSJakub Kicinski printf("\n"); 331743cc665SQuentin Monnet } 332743cc665SQuentin Monnet 333743cc665SQuentin Monnet static int show_prog(int fd) 334743cc665SQuentin Monnet { 335743cc665SQuentin Monnet struct bpf_prog_info info = {}; 336743cc665SQuentin Monnet __u32 len = sizeof(info); 337743cc665SQuentin Monnet int err; 338743cc665SQuentin Monnet 339743cc665SQuentin Monnet err = bpf_obj_get_info_by_fd(fd, &info, &len); 340743cc665SQuentin Monnet if (err) { 3419a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 342743cc665SQuentin Monnet return -1; 343743cc665SQuentin Monnet } 344743cc665SQuentin Monnet 345743cc665SQuentin Monnet if (json_output) 346743cc665SQuentin Monnet print_prog_json(&info, fd); 347743cc665SQuentin Monnet else 348743cc665SQuentin Monnet print_prog_plain(&info, fd); 34971bb428fSJakub Kicinski 35071bb428fSJakub Kicinski return 0; 35171bb428fSJakub Kicinski } 35271bb428fSJakub Kicinski 35371bb428fSJakub Kicinski static int do_show(int argc, char **argv) 354743cc665SQuentin Monnet { 355743cc665SQuentin Monnet __u32 id = 0; 35671bb428fSJakub Kicinski int err; 35771bb428fSJakub Kicinski int fd; 35871bb428fSJakub Kicinski 359c541b734SPrashant Bhole if (show_pinned) 3604990f1f4SPrashant Bhole build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); 3614990f1f4SPrashant Bhole 36271bb428fSJakub Kicinski if (argc == 2) { 36371bb428fSJakub Kicinski fd = prog_parse_fd(&argc, &argv); 36471bb428fSJakub Kicinski if (fd < 0) 36571bb428fSJakub Kicinski return -1; 36671bb428fSJakub Kicinski 36771bb428fSJakub Kicinski return show_prog(fd); 36871bb428fSJakub Kicinski } 36971bb428fSJakub Kicinski 37071bb428fSJakub Kicinski if (argc) 37171bb428fSJakub Kicinski return BAD_ARG(); 37271bb428fSJakub Kicinski 373743cc665SQuentin Monnet if (json_output) 374743cc665SQuentin Monnet jsonw_start_array(json_wtr); 37571bb428fSJakub Kicinski while (true) { 37671bb428fSJakub Kicinski err = bpf_prog_get_next_id(id, &id); 37771bb428fSJakub Kicinski if (err) { 3781739c26dSQuentin Monnet if (errno == ENOENT) { 3791739c26dSQuentin Monnet err = 0; 38071bb428fSJakub Kicinski break; 3811739c26dSQuentin Monnet } 3829a5ab8bfSQuentin Monnet p_err("can't get next program: %s%s", strerror(errno), 3839a5ab8bfSQuentin Monnet errno == EINVAL ? " -- kernel too old?" : ""); 384743cc665SQuentin Monnet err = -1; 385743cc665SQuentin Monnet break; 38671bb428fSJakub Kicinski } 38771bb428fSJakub Kicinski 38871bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 38971bb428fSJakub Kicinski if (fd < 0) { 3908207c6ddSJakub Kicinski if (errno == ENOENT) 3918207c6ddSJakub Kicinski continue; 3929a5ab8bfSQuentin Monnet p_err("can't get prog by id (%u): %s", 39371bb428fSJakub Kicinski id, strerror(errno)); 394743cc665SQuentin Monnet err = -1; 395743cc665SQuentin Monnet break; 39671bb428fSJakub Kicinski } 39771bb428fSJakub Kicinski 39871bb428fSJakub Kicinski err = show_prog(fd); 39971bb428fSJakub Kicinski close(fd); 40071bb428fSJakub Kicinski if (err) 401743cc665SQuentin Monnet break; 40271bb428fSJakub Kicinski } 40371bb428fSJakub Kicinski 404743cc665SQuentin Monnet if (json_output) 405743cc665SQuentin Monnet jsonw_end_array(json_wtr); 406743cc665SQuentin Monnet 407743cc665SQuentin Monnet return err; 40871bb428fSJakub Kicinski } 40971bb428fSJakub Kicinski 4107105e828SDaniel Borkmann #define SYM_MAX_NAME 256 4117105e828SDaniel Borkmann 4127105e828SDaniel Borkmann struct kernel_sym { 4137105e828SDaniel Borkmann unsigned long address; 4147105e828SDaniel Borkmann char name[SYM_MAX_NAME]; 4157105e828SDaniel Borkmann }; 4167105e828SDaniel Borkmann 4177105e828SDaniel Borkmann struct dump_data { 4187105e828SDaniel Borkmann unsigned long address_call_base; 4197105e828SDaniel Borkmann struct kernel_sym *sym_mapping; 4207105e828SDaniel Borkmann __u32 sym_count; 4217105e828SDaniel Borkmann char scratch_buff[SYM_MAX_NAME]; 4227105e828SDaniel Borkmann }; 4237105e828SDaniel Borkmann 4247105e828SDaniel Borkmann static int kernel_syms_cmp(const void *sym_a, const void *sym_b) 4257105e828SDaniel Borkmann { 4267105e828SDaniel Borkmann return ((struct kernel_sym *)sym_a)->address - 4277105e828SDaniel Borkmann ((struct kernel_sym *)sym_b)->address; 4287105e828SDaniel Borkmann } 4297105e828SDaniel Borkmann 4307105e828SDaniel Borkmann static void kernel_syms_load(struct dump_data *dd) 4317105e828SDaniel Borkmann { 4327105e828SDaniel Borkmann struct kernel_sym *sym; 4337105e828SDaniel Borkmann char buff[256]; 4347105e828SDaniel Borkmann void *tmp, *address; 4357105e828SDaniel Borkmann FILE *fp; 4367105e828SDaniel Borkmann 4377105e828SDaniel Borkmann fp = fopen("/proc/kallsyms", "r"); 4387105e828SDaniel Borkmann if (!fp) 4397105e828SDaniel Borkmann return; 4407105e828SDaniel Borkmann 4417105e828SDaniel Borkmann while (!feof(fp)) { 4427105e828SDaniel Borkmann if (!fgets(buff, sizeof(buff), fp)) 4437105e828SDaniel Borkmann break; 4447105e828SDaniel Borkmann tmp = realloc(dd->sym_mapping, 4457105e828SDaniel Borkmann (dd->sym_count + 1) * 4467105e828SDaniel Borkmann sizeof(*dd->sym_mapping)); 4477105e828SDaniel Borkmann if (!tmp) { 4487105e828SDaniel Borkmann out: 4497105e828SDaniel Borkmann free(dd->sym_mapping); 4507105e828SDaniel Borkmann dd->sym_mapping = NULL; 4517105e828SDaniel Borkmann fclose(fp); 4527105e828SDaniel Borkmann return; 4537105e828SDaniel Borkmann } 4547105e828SDaniel Borkmann dd->sym_mapping = tmp; 4557105e828SDaniel Borkmann sym = &dd->sym_mapping[dd->sym_count]; 4567105e828SDaniel Borkmann if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2) 4577105e828SDaniel Borkmann continue; 4587105e828SDaniel Borkmann sym->address = (unsigned long)address; 4597105e828SDaniel Borkmann if (!strcmp(sym->name, "__bpf_call_base")) { 4607105e828SDaniel Borkmann dd->address_call_base = sym->address; 4617105e828SDaniel Borkmann /* sysctl kernel.kptr_restrict was set */ 4627105e828SDaniel Borkmann if (!sym->address) 4637105e828SDaniel Borkmann goto out; 4647105e828SDaniel Borkmann } 4657105e828SDaniel Borkmann if (sym->address) 4667105e828SDaniel Borkmann dd->sym_count++; 4677105e828SDaniel Borkmann } 4687105e828SDaniel Borkmann 4697105e828SDaniel Borkmann fclose(fp); 4707105e828SDaniel Borkmann 4717105e828SDaniel Borkmann qsort(dd->sym_mapping, dd->sym_count, 4727105e828SDaniel Borkmann sizeof(*dd->sym_mapping), kernel_syms_cmp); 4737105e828SDaniel Borkmann } 4747105e828SDaniel Borkmann 4757105e828SDaniel Borkmann static void kernel_syms_destroy(struct dump_data *dd) 4767105e828SDaniel Borkmann { 4777105e828SDaniel Borkmann free(dd->sym_mapping); 4787105e828SDaniel Borkmann } 4797105e828SDaniel Borkmann 4807105e828SDaniel Borkmann static struct kernel_sym *kernel_syms_search(struct dump_data *dd, 4817105e828SDaniel Borkmann unsigned long key) 4827105e828SDaniel Borkmann { 4837105e828SDaniel Borkmann struct kernel_sym sym = { 4847105e828SDaniel Borkmann .address = key, 4857105e828SDaniel Borkmann }; 4867105e828SDaniel Borkmann 4877105e828SDaniel Borkmann return dd->sym_mapping ? 4887105e828SDaniel Borkmann bsearch(&sym, dd->sym_mapping, dd->sym_count, 4897105e828SDaniel Borkmann sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL; 4907105e828SDaniel Borkmann } 4917105e828SDaniel Borkmann 492c9c35995SJakub Kicinski static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) 493c9c35995SJakub Kicinski { 494c9c35995SJakub Kicinski va_list args; 495c9c35995SJakub Kicinski 496c9c35995SJakub Kicinski va_start(args, fmt); 497c9c35995SJakub Kicinski vprintf(fmt, args); 498c9c35995SJakub Kicinski va_end(args); 499c9c35995SJakub Kicinski } 500c9c35995SJakub Kicinski 5017105e828SDaniel Borkmann static const char *print_call_pcrel(struct dump_data *dd, 5027105e828SDaniel Borkmann struct kernel_sym *sym, 5037105e828SDaniel Borkmann unsigned long address, 5047105e828SDaniel Borkmann const struct bpf_insn *insn) 505c9c35995SJakub Kicinski { 5067105e828SDaniel Borkmann if (sym) 5077105e828SDaniel Borkmann snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 5087105e828SDaniel Borkmann "%+d#%s", insn->off, sym->name); 5097105e828SDaniel Borkmann else 5107105e828SDaniel Borkmann snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 5117105e828SDaniel Borkmann "%+d#0x%lx", insn->off, address); 5127105e828SDaniel Borkmann return dd->scratch_buff; 5137105e828SDaniel Borkmann } 5147105e828SDaniel Borkmann 5157105e828SDaniel Borkmann static const char *print_call_helper(struct dump_data *dd, 5167105e828SDaniel Borkmann struct kernel_sym *sym, 5177105e828SDaniel Borkmann unsigned long address) 5187105e828SDaniel Borkmann { 5197105e828SDaniel Borkmann if (sym) 5207105e828SDaniel Borkmann snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 5217105e828SDaniel Borkmann "%s", sym->name); 5227105e828SDaniel Borkmann else 5237105e828SDaniel Borkmann snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 5247105e828SDaniel Borkmann "0x%lx", address); 5257105e828SDaniel Borkmann return dd->scratch_buff; 5267105e828SDaniel Borkmann } 5277105e828SDaniel Borkmann 5287105e828SDaniel Borkmann static const char *print_call(void *private_data, 5297105e828SDaniel Borkmann const struct bpf_insn *insn) 5307105e828SDaniel Borkmann { 5317105e828SDaniel Borkmann struct dump_data *dd = private_data; 5327105e828SDaniel Borkmann unsigned long address = dd->address_call_base + insn->imm; 5337105e828SDaniel Borkmann struct kernel_sym *sym; 5347105e828SDaniel Borkmann 5357105e828SDaniel Borkmann sym = kernel_syms_search(dd, address); 5367105e828SDaniel Borkmann if (insn->src_reg == BPF_PSEUDO_CALL) 5377105e828SDaniel Borkmann return print_call_pcrel(dd, sym, address, insn); 5387105e828SDaniel Borkmann else 5397105e828SDaniel Borkmann return print_call_helper(dd, sym, address); 5407105e828SDaniel Borkmann } 5417105e828SDaniel Borkmann 5427105e828SDaniel Borkmann static const char *print_imm(void *private_data, 5437105e828SDaniel Borkmann const struct bpf_insn *insn, 5447105e828SDaniel Borkmann __u64 full_imm) 5457105e828SDaniel Borkmann { 5467105e828SDaniel Borkmann struct dump_data *dd = private_data; 5477105e828SDaniel Borkmann 5487105e828SDaniel Borkmann if (insn->src_reg == BPF_PSEUDO_MAP_FD) 5497105e828SDaniel Borkmann snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 5507105e828SDaniel Borkmann "map[id:%u]", insn->imm); 5517105e828SDaniel Borkmann else 5527105e828SDaniel Borkmann snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), 5537105e828SDaniel Borkmann "0x%llx", (unsigned long long)full_imm); 5547105e828SDaniel Borkmann return dd->scratch_buff; 5557105e828SDaniel Borkmann } 5567105e828SDaniel Borkmann 5577105e828SDaniel Borkmann static void dump_xlated_plain(struct dump_data *dd, void *buf, 5587105e828SDaniel Borkmann unsigned int len, bool opcodes) 5597105e828SDaniel Borkmann { 5607105e828SDaniel Borkmann const struct bpf_insn_cbs cbs = { 5617105e828SDaniel Borkmann .cb_print = print_insn, 5627105e828SDaniel Borkmann .cb_call = print_call, 5637105e828SDaniel Borkmann .cb_imm = print_imm, 5647105e828SDaniel Borkmann .private_data = dd, 5657105e828SDaniel Borkmann }; 566c9c35995SJakub Kicinski struct bpf_insn *insn = buf; 5679e2308c1SQuentin Monnet bool double_insn = false; 568c9c35995SJakub Kicinski unsigned int i; 569c9c35995SJakub Kicinski 570c9c35995SJakub Kicinski for (i = 0; i < len / sizeof(*insn); i++) { 5719e2308c1SQuentin Monnet if (double_insn) { 5729e2308c1SQuentin Monnet double_insn = false; 5739e2308c1SQuentin Monnet continue; 5749e2308c1SQuentin Monnet } 5759e2308c1SQuentin Monnet 5769e2308c1SQuentin Monnet double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 5779e2308c1SQuentin Monnet 578c9c35995SJakub Kicinski printf("% 4d: ", i); 5797105e828SDaniel Borkmann print_bpf_insn(&cbs, NULL, insn + i, true); 580c9c35995SJakub Kicinski 581c9c35995SJakub Kicinski if (opcodes) { 582c9c35995SJakub Kicinski printf(" "); 5839cbe1f58SQuentin Monnet fprint_hex(stdout, insn + i, 8, " "); 5849e2308c1SQuentin Monnet if (double_insn && i < len - 1) { 5859e2308c1SQuentin Monnet printf(" "); 5869e2308c1SQuentin Monnet fprint_hex(stdout, insn + i + 1, 8, " "); 5879e2308c1SQuentin Monnet } 588c9c35995SJakub Kicinski printf("\n"); 589c9c35995SJakub Kicinski } 590c9c35995SJakub Kicinski } 591c9c35995SJakub Kicinski } 592c9c35995SJakub Kicinski 593f05e2c32SQuentin Monnet static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) 594f05e2c32SQuentin Monnet { 595f05e2c32SQuentin Monnet unsigned int l = strlen(fmt); 596f05e2c32SQuentin Monnet char chomped_fmt[l]; 597f05e2c32SQuentin Monnet va_list args; 598f05e2c32SQuentin Monnet 599f05e2c32SQuentin Monnet va_start(args, fmt); 600f05e2c32SQuentin Monnet if (l > 0) { 601f05e2c32SQuentin Monnet strncpy(chomped_fmt, fmt, l - 1); 602f05e2c32SQuentin Monnet chomped_fmt[l - 1] = '\0'; 603f05e2c32SQuentin Monnet } 604f05e2c32SQuentin Monnet jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); 605f05e2c32SQuentin Monnet va_end(args); 606f05e2c32SQuentin Monnet } 607f05e2c32SQuentin Monnet 6087105e828SDaniel Borkmann static void dump_xlated_json(struct dump_data *dd, void *buf, 6097105e828SDaniel Borkmann unsigned int len, bool opcodes) 610f05e2c32SQuentin Monnet { 6117105e828SDaniel Borkmann const struct bpf_insn_cbs cbs = { 6127105e828SDaniel Borkmann .cb_print = print_insn_json, 6137105e828SDaniel Borkmann .cb_call = print_call, 6147105e828SDaniel Borkmann .cb_imm = print_imm, 6157105e828SDaniel Borkmann .private_data = dd, 6167105e828SDaniel Borkmann }; 617f05e2c32SQuentin Monnet struct bpf_insn *insn = buf; 618f05e2c32SQuentin Monnet bool double_insn = false; 619f05e2c32SQuentin Monnet unsigned int i; 620f05e2c32SQuentin Monnet 621f05e2c32SQuentin Monnet jsonw_start_array(json_wtr); 622f05e2c32SQuentin Monnet for (i = 0; i < len / sizeof(*insn); i++) { 623f05e2c32SQuentin Monnet if (double_insn) { 624f05e2c32SQuentin Monnet double_insn = false; 625f05e2c32SQuentin Monnet continue; 626f05e2c32SQuentin Monnet } 627f05e2c32SQuentin Monnet double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); 628f05e2c32SQuentin Monnet 629f05e2c32SQuentin Monnet jsonw_start_object(json_wtr); 630f05e2c32SQuentin Monnet jsonw_name(json_wtr, "disasm"); 6317105e828SDaniel Borkmann print_bpf_insn(&cbs, NULL, insn + i, true); 632f05e2c32SQuentin Monnet 633f05e2c32SQuentin Monnet if (opcodes) { 634f05e2c32SQuentin Monnet jsonw_name(json_wtr, "opcodes"); 635f05e2c32SQuentin Monnet jsonw_start_object(json_wtr); 636f05e2c32SQuentin Monnet 637f05e2c32SQuentin Monnet jsonw_name(json_wtr, "code"); 638f05e2c32SQuentin Monnet jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); 639f05e2c32SQuentin Monnet 640f05e2c32SQuentin Monnet jsonw_name(json_wtr, "src_reg"); 641f05e2c32SQuentin Monnet jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); 642f05e2c32SQuentin Monnet 643f05e2c32SQuentin Monnet jsonw_name(json_wtr, "dst_reg"); 644f05e2c32SQuentin Monnet jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); 645f05e2c32SQuentin Monnet 646f05e2c32SQuentin Monnet jsonw_name(json_wtr, "off"); 647f05e2c32SQuentin Monnet print_hex_data_json((uint8_t *)(&insn[i].off), 2); 648f05e2c32SQuentin Monnet 649f05e2c32SQuentin Monnet jsonw_name(json_wtr, "imm"); 650f05e2c32SQuentin Monnet if (double_insn && i < len - 1) 651f05e2c32SQuentin Monnet print_hex_data_json((uint8_t *)(&insn[i].imm), 652f05e2c32SQuentin Monnet 12); 653f05e2c32SQuentin Monnet else 654f05e2c32SQuentin Monnet print_hex_data_json((uint8_t *)(&insn[i].imm), 655f05e2c32SQuentin Monnet 4); 656f05e2c32SQuentin Monnet jsonw_end_object(json_wtr); 657f05e2c32SQuentin Monnet } 658f05e2c32SQuentin Monnet jsonw_end_object(json_wtr); 659f05e2c32SQuentin Monnet } 660f05e2c32SQuentin Monnet jsonw_end_array(json_wtr); 661f05e2c32SQuentin Monnet } 662f05e2c32SQuentin Monnet 66371bb428fSJakub Kicinski static int do_dump(int argc, char **argv) 66471bb428fSJakub Kicinski { 66571bb428fSJakub Kicinski struct bpf_prog_info info = {}; 6667105e828SDaniel Borkmann struct dump_data dd = {}; 66771bb428fSJakub Kicinski __u32 len = sizeof(info); 66871bb428fSJakub Kicinski unsigned int buf_size; 66971bb428fSJakub Kicinski char *filepath = NULL; 67071bb428fSJakub Kicinski bool opcodes = false; 67171bb428fSJakub Kicinski unsigned char *buf; 67271bb428fSJakub Kicinski __u32 *member_len; 67371bb428fSJakub Kicinski __u64 *member_ptr; 67471bb428fSJakub Kicinski ssize_t n; 67571bb428fSJakub Kicinski int err; 67671bb428fSJakub Kicinski int fd; 67771bb428fSJakub Kicinski 67871bb428fSJakub Kicinski if (is_prefix(*argv, "jited")) { 67971bb428fSJakub Kicinski member_len = &info.jited_prog_len; 68071bb428fSJakub Kicinski member_ptr = &info.jited_prog_insns; 68171bb428fSJakub Kicinski } else if (is_prefix(*argv, "xlated")) { 68271bb428fSJakub Kicinski member_len = &info.xlated_prog_len; 68371bb428fSJakub Kicinski member_ptr = &info.xlated_prog_insns; 68471bb428fSJakub Kicinski } else { 6859a5ab8bfSQuentin Monnet p_err("expected 'xlated' or 'jited', got: %s", *argv); 68671bb428fSJakub Kicinski return -1; 68771bb428fSJakub Kicinski } 68871bb428fSJakub Kicinski NEXT_ARG(); 68971bb428fSJakub Kicinski 69071bb428fSJakub Kicinski if (argc < 2) 69171bb428fSJakub Kicinski usage(); 69271bb428fSJakub Kicinski 69371bb428fSJakub Kicinski fd = prog_parse_fd(&argc, &argv); 69471bb428fSJakub Kicinski if (fd < 0) 69571bb428fSJakub Kicinski return -1; 69671bb428fSJakub Kicinski 69771bb428fSJakub Kicinski if (is_prefix(*argv, "file")) { 69871bb428fSJakub Kicinski NEXT_ARG(); 69971bb428fSJakub Kicinski if (!argc) { 7009a5ab8bfSQuentin Monnet p_err("expected file path"); 70171bb428fSJakub Kicinski return -1; 70271bb428fSJakub Kicinski } 70371bb428fSJakub Kicinski 70471bb428fSJakub Kicinski filepath = *argv; 70571bb428fSJakub Kicinski NEXT_ARG(); 70671bb428fSJakub Kicinski } else if (is_prefix(*argv, "opcodes")) { 70771bb428fSJakub Kicinski opcodes = true; 70871bb428fSJakub Kicinski NEXT_ARG(); 70971bb428fSJakub Kicinski } 71071bb428fSJakub Kicinski 71171bb428fSJakub Kicinski if (argc) { 71271bb428fSJakub Kicinski usage(); 71371bb428fSJakub Kicinski return -1; 71471bb428fSJakub Kicinski } 71571bb428fSJakub Kicinski 71671bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 71771bb428fSJakub Kicinski if (err) { 7189a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 71971bb428fSJakub Kicinski return -1; 72071bb428fSJakub Kicinski } 72171bb428fSJakub Kicinski 72271bb428fSJakub Kicinski if (!*member_len) { 7239a5ab8bfSQuentin Monnet p_info("no instructions returned"); 72471bb428fSJakub Kicinski close(fd); 72571bb428fSJakub Kicinski return 0; 72671bb428fSJakub Kicinski } 72771bb428fSJakub Kicinski 72871bb428fSJakub Kicinski buf_size = *member_len; 72971bb428fSJakub Kicinski 73071bb428fSJakub Kicinski buf = malloc(buf_size); 73171bb428fSJakub Kicinski if (!buf) { 7329a5ab8bfSQuentin Monnet p_err("mem alloc failed"); 73371bb428fSJakub Kicinski close(fd); 73471bb428fSJakub Kicinski return -1; 73571bb428fSJakub Kicinski } 73671bb428fSJakub Kicinski 73771bb428fSJakub Kicinski memset(&info, 0, sizeof(info)); 73871bb428fSJakub Kicinski 73971bb428fSJakub Kicinski *member_ptr = ptr_to_u64(buf); 74071bb428fSJakub Kicinski *member_len = buf_size; 74171bb428fSJakub Kicinski 74271bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 74371bb428fSJakub Kicinski close(fd); 74471bb428fSJakub Kicinski if (err) { 7459a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 74671bb428fSJakub Kicinski goto err_free; 74771bb428fSJakub Kicinski } 74871bb428fSJakub Kicinski 74971bb428fSJakub Kicinski if (*member_len > buf_size) { 7509a5ab8bfSQuentin Monnet p_err("too many instructions returned"); 75171bb428fSJakub Kicinski goto err_free; 75271bb428fSJakub Kicinski } 75371bb428fSJakub Kicinski 7547105e828SDaniel Borkmann if ((member_len == &info.jited_prog_len && 7557105e828SDaniel Borkmann info.jited_prog_insns == 0) || 7567105e828SDaniel Borkmann (member_len == &info.xlated_prog_len && 7577105e828SDaniel Borkmann info.xlated_prog_insns == 0)) { 7587105e828SDaniel Borkmann p_err("error retrieving insn dump: kernel.kptr_restrict set?"); 7597105e828SDaniel Borkmann goto err_free; 7607105e828SDaniel Borkmann } 7617105e828SDaniel Borkmann 76271bb428fSJakub Kicinski if (filepath) { 76371bb428fSJakub Kicinski fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 76471bb428fSJakub Kicinski if (fd < 0) { 7659a5ab8bfSQuentin Monnet p_err("can't open file %s: %s", filepath, 76671bb428fSJakub Kicinski strerror(errno)); 76771bb428fSJakub Kicinski goto err_free; 76871bb428fSJakub Kicinski } 76971bb428fSJakub Kicinski 77071bb428fSJakub Kicinski n = write(fd, buf, *member_len); 77171bb428fSJakub Kicinski close(fd); 77271bb428fSJakub Kicinski if (n != *member_len) { 7739a5ab8bfSQuentin Monnet p_err("error writing output file: %s", 77471bb428fSJakub Kicinski n < 0 ? strerror(errno) : "short write"); 77571bb428fSJakub Kicinski goto err_free; 77671bb428fSJakub Kicinski } 777*52c84d36SQuentin Monnet 778*52c84d36SQuentin Monnet if (json_output) 779*52c84d36SQuentin Monnet jsonw_null(json_wtr); 78071bb428fSJakub Kicinski } else { 7817105e828SDaniel Borkmann if (member_len == &info.jited_prog_len) { 782e6593596SJiong Wang const char *name = NULL; 783e6593596SJiong Wang 784e6593596SJiong Wang if (info.ifindex) { 785e6593596SJiong Wang name = ifindex_to_bfd_name_ns(info.ifindex, 786e6593596SJiong Wang info.netns_dev, 787e6593596SJiong Wang info.netns_ino); 788e6593596SJiong Wang if (!name) 789e6593596SJiong Wang goto err_free; 790e6593596SJiong Wang } 791e6593596SJiong Wang 792e6593596SJiong Wang disasm_print_insn(buf, *member_len, opcodes, name); 7937105e828SDaniel Borkmann } else { 7947105e828SDaniel Borkmann kernel_syms_load(&dd); 795f05e2c32SQuentin Monnet if (json_output) 7967105e828SDaniel Borkmann dump_xlated_json(&dd, buf, *member_len, opcodes); 797f05e2c32SQuentin Monnet else 7987105e828SDaniel Borkmann dump_xlated_plain(&dd, buf, *member_len, opcodes); 7997105e828SDaniel Borkmann kernel_syms_destroy(&dd); 8007105e828SDaniel Borkmann } 80171bb428fSJakub Kicinski } 80271bb428fSJakub Kicinski 80371bb428fSJakub Kicinski free(buf); 80471bb428fSJakub Kicinski return 0; 80571bb428fSJakub Kicinski 80671bb428fSJakub Kicinski err_free: 80771bb428fSJakub Kicinski free(buf); 80871bb428fSJakub Kicinski return -1; 80971bb428fSJakub Kicinski } 81071bb428fSJakub Kicinski 81171bb428fSJakub Kicinski static int do_pin(int argc, char **argv) 81271bb428fSJakub Kicinski { 813004b45c0SQuentin Monnet int err; 814004b45c0SQuentin Monnet 815004b45c0SQuentin Monnet err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); 816004b45c0SQuentin Monnet if (!err && json_output) 817004b45c0SQuentin Monnet jsonw_null(json_wtr); 818004b45c0SQuentin Monnet return err; 81971bb428fSJakub Kicinski } 82071bb428fSJakub Kicinski 82149a086c2SRoman Gushchin static int do_load(int argc, char **argv) 82249a086c2SRoman Gushchin { 82349a086c2SRoman Gushchin struct bpf_object *obj; 82449a086c2SRoman Gushchin int prog_fd; 82549a086c2SRoman Gushchin 82649a086c2SRoman Gushchin if (argc != 2) 82749a086c2SRoman Gushchin usage(); 82849a086c2SRoman Gushchin 82949a086c2SRoman Gushchin if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) { 830b4fac96dSJakub Kicinski p_err("failed to load program"); 83149a086c2SRoman Gushchin return -1; 83249a086c2SRoman Gushchin } 83349a086c2SRoman Gushchin 83449a086c2SRoman Gushchin if (do_pin_fd(prog_fd, argv[1])) { 835b4fac96dSJakub Kicinski p_err("failed to pin program"); 83649a086c2SRoman Gushchin return -1; 83749a086c2SRoman Gushchin } 83849a086c2SRoman Gushchin 83949a086c2SRoman Gushchin if (json_output) 84049a086c2SRoman Gushchin jsonw_null(json_wtr); 84149a086c2SRoman Gushchin 84249a086c2SRoman Gushchin return 0; 84349a086c2SRoman Gushchin } 84449a086c2SRoman Gushchin 84571bb428fSJakub Kicinski static int do_help(int argc, char **argv) 84671bb428fSJakub Kicinski { 847004b45c0SQuentin Monnet if (json_output) { 848004b45c0SQuentin Monnet jsonw_null(json_wtr); 849004b45c0SQuentin Monnet return 0; 850004b45c0SQuentin Monnet } 851004b45c0SQuentin Monnet 85271bb428fSJakub Kicinski fprintf(stderr, 8536ebe6dbdSJakub Kicinski "Usage: %s %s { show | list } [PROG]\n" 8548dfbc6d1SQuentin Monnet " %s %s dump xlated PROG [{ file FILE | opcodes }]\n" 8558dfbc6d1SQuentin Monnet " %s %s dump jited PROG [{ file FILE | opcodes }]\n" 85671bb428fSJakub Kicinski " %s %s pin PROG FILE\n" 85749a086c2SRoman Gushchin " %s %s load OBJ FILE\n" 85871bb428fSJakub Kicinski " %s %s help\n" 85971bb428fSJakub Kicinski "\n" 86071bb428fSJakub Kicinski " " HELP_SPEC_PROGRAM "\n" 8610641c3c8SQuentin Monnet " " HELP_SPEC_OPTIONS "\n" 86271bb428fSJakub Kicinski "", 86371bb428fSJakub Kicinski bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 86449a086c2SRoman Gushchin bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); 86571bb428fSJakub Kicinski 86671bb428fSJakub Kicinski return 0; 86771bb428fSJakub Kicinski } 86871bb428fSJakub Kicinski 86971bb428fSJakub Kicinski static const struct cmd cmds[] = { 87071bb428fSJakub Kicinski { "show", do_show }, 8716ebe6dbdSJakub Kicinski { "list", do_show }, 8729f606179SQuentin Monnet { "help", do_help }, 87371bb428fSJakub Kicinski { "dump", do_dump }, 87471bb428fSJakub Kicinski { "pin", do_pin }, 87549a086c2SRoman Gushchin { "load", do_load }, 87671bb428fSJakub Kicinski { 0 } 87771bb428fSJakub Kicinski }; 87871bb428fSJakub Kicinski 87971bb428fSJakub Kicinski int do_prog(int argc, char **argv) 88071bb428fSJakub Kicinski { 88171bb428fSJakub Kicinski return cmd_select(cmds, argc, argv, do_help); 88271bb428fSJakub Kicinski } 883