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 50*b6c1cedbSJiong Wang #include "cfg.h" 5171bb428fSJakub Kicinski #include "main.h" 5273bb5b4fSJiong Wang #include "xlated_dumper.h" 5371bb428fSJakub Kicinski 5471bb428fSJakub Kicinski static const char * const prog_type_name[] = { 5571bb428fSJakub Kicinski [BPF_PROG_TYPE_UNSPEC] = "unspec", 5671bb428fSJakub Kicinski [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", 5771bb428fSJakub Kicinski [BPF_PROG_TYPE_KPROBE] = "kprobe", 5871bb428fSJakub Kicinski [BPF_PROG_TYPE_SCHED_CLS] = "sched_cls", 5971bb428fSJakub Kicinski [BPF_PROG_TYPE_SCHED_ACT] = "sched_act", 6071bb428fSJakub Kicinski [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint", 6171bb428fSJakub Kicinski [BPF_PROG_TYPE_XDP] = "xdp", 6271bb428fSJakub Kicinski [BPF_PROG_TYPE_PERF_EVENT] = "perf_event", 6371bb428fSJakub Kicinski [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup_skb", 6471bb428fSJakub Kicinski [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup_sock", 6571bb428fSJakub Kicinski [BPF_PROG_TYPE_LWT_IN] = "lwt_in", 6671bb428fSJakub Kicinski [BPF_PROG_TYPE_LWT_OUT] = "lwt_out", 6771bb428fSJakub Kicinski [BPF_PROG_TYPE_LWT_XMIT] = "lwt_xmit", 6871bb428fSJakub Kicinski [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops", 6971bb428fSJakub Kicinski [BPF_PROG_TYPE_SK_SKB] = "sk_skb", 7045e5e121SRoman Gushchin [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device", 7171bb428fSJakub Kicinski }; 7271bb428fSJakub Kicinski 7371bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) 7471bb428fSJakub Kicinski { 7571bb428fSJakub Kicinski struct timespec real_time_ts, boot_time_ts; 7671bb428fSJakub Kicinski time_t wallclock_secs; 7771bb428fSJakub Kicinski struct tm load_tm; 7871bb428fSJakub Kicinski 7971bb428fSJakub Kicinski buf[--size] = '\0'; 8071bb428fSJakub Kicinski 8171bb428fSJakub Kicinski if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || 8271bb428fSJakub Kicinski clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { 8371bb428fSJakub Kicinski perror("Can't read clocks"); 8471bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 8571bb428fSJakub Kicinski return; 8671bb428fSJakub Kicinski } 8771bb428fSJakub Kicinski 8871bb428fSJakub Kicinski wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + 8971bb428fSJakub Kicinski nsecs / 1000000000; 9071bb428fSJakub Kicinski 9171bb428fSJakub Kicinski if (!localtime_r(&wallclock_secs, &load_tm)) { 9271bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 9371bb428fSJakub Kicinski return; 9471bb428fSJakub Kicinski } 9571bb428fSJakub Kicinski 9671bb428fSJakub Kicinski strftime(buf, size, "%b %d/%H:%M", &load_tm); 9771bb428fSJakub Kicinski } 9871bb428fSJakub Kicinski 9971bb428fSJakub Kicinski static int prog_fd_by_tag(unsigned char *tag) 10071bb428fSJakub Kicinski { 10171bb428fSJakub Kicinski struct bpf_prog_info info = {}; 10271bb428fSJakub Kicinski __u32 len = sizeof(info); 10371bb428fSJakub Kicinski unsigned int id = 0; 10471bb428fSJakub Kicinski int err; 10571bb428fSJakub Kicinski int fd; 10671bb428fSJakub Kicinski 10771bb428fSJakub Kicinski while (true) { 10871bb428fSJakub Kicinski err = bpf_prog_get_next_id(id, &id); 10971bb428fSJakub Kicinski if (err) { 1109a5ab8bfSQuentin Monnet p_err("%s", strerror(errno)); 11171bb428fSJakub Kicinski return -1; 11271bb428fSJakub Kicinski } 11371bb428fSJakub Kicinski 11471bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 11571bb428fSJakub Kicinski if (fd < 0) { 1169a5ab8bfSQuentin Monnet p_err("can't get prog by id (%u): %s", 11771bb428fSJakub Kicinski id, strerror(errno)); 11871bb428fSJakub Kicinski return -1; 11971bb428fSJakub Kicinski } 12071bb428fSJakub Kicinski 12171bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 12271bb428fSJakub Kicinski if (err) { 1239a5ab8bfSQuentin Monnet p_err("can't get prog info (%u): %s", 12471bb428fSJakub Kicinski id, strerror(errno)); 12571bb428fSJakub Kicinski close(fd); 12671bb428fSJakub Kicinski return -1; 12771bb428fSJakub Kicinski } 12871bb428fSJakub Kicinski 12971bb428fSJakub Kicinski if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) 13071bb428fSJakub Kicinski return fd; 13171bb428fSJakub Kicinski 13271bb428fSJakub Kicinski close(fd); 13371bb428fSJakub Kicinski } 13471bb428fSJakub Kicinski } 13571bb428fSJakub Kicinski 13671bb428fSJakub Kicinski int prog_parse_fd(int *argc, char ***argv) 13771bb428fSJakub Kicinski { 13871bb428fSJakub Kicinski int fd; 13971bb428fSJakub Kicinski 14071bb428fSJakub Kicinski if (is_prefix(**argv, "id")) { 14171bb428fSJakub Kicinski unsigned int id; 14271bb428fSJakub Kicinski char *endptr; 14371bb428fSJakub Kicinski 14471bb428fSJakub Kicinski NEXT_ARGP(); 14571bb428fSJakub Kicinski 14671bb428fSJakub Kicinski id = strtoul(**argv, &endptr, 0); 14771bb428fSJakub Kicinski if (*endptr) { 1489a5ab8bfSQuentin Monnet p_err("can't parse %s as ID", **argv); 14971bb428fSJakub Kicinski return -1; 15071bb428fSJakub Kicinski } 15171bb428fSJakub Kicinski NEXT_ARGP(); 15271bb428fSJakub Kicinski 15371bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 15471bb428fSJakub Kicinski if (fd < 0) 1559a5ab8bfSQuentin Monnet p_err("get by id (%u): %s", id, strerror(errno)); 15671bb428fSJakub Kicinski return fd; 15771bb428fSJakub Kicinski } else if (is_prefix(**argv, "tag")) { 15871bb428fSJakub Kicinski unsigned char tag[BPF_TAG_SIZE]; 15971bb428fSJakub Kicinski 16071bb428fSJakub Kicinski NEXT_ARGP(); 16171bb428fSJakub Kicinski 16271bb428fSJakub Kicinski if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, 16371bb428fSJakub Kicinski tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) 16471bb428fSJakub Kicinski != BPF_TAG_SIZE) { 1659a5ab8bfSQuentin Monnet p_err("can't parse tag"); 16671bb428fSJakub Kicinski return -1; 16771bb428fSJakub Kicinski } 16871bb428fSJakub Kicinski NEXT_ARGP(); 16971bb428fSJakub Kicinski 17071bb428fSJakub Kicinski return prog_fd_by_tag(tag); 17171bb428fSJakub Kicinski } else if (is_prefix(**argv, "pinned")) { 17271bb428fSJakub Kicinski char *path; 17371bb428fSJakub Kicinski 17471bb428fSJakub Kicinski NEXT_ARGP(); 17571bb428fSJakub Kicinski 17671bb428fSJakub Kicinski path = **argv; 17771bb428fSJakub Kicinski NEXT_ARGP(); 17871bb428fSJakub Kicinski 17971bb428fSJakub Kicinski return open_obj_pinned_any(path, BPF_OBJ_PROG); 18071bb428fSJakub Kicinski } 18171bb428fSJakub Kicinski 1829a5ab8bfSQuentin Monnet p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); 18371bb428fSJakub Kicinski return -1; 18471bb428fSJakub Kicinski } 18571bb428fSJakub Kicinski 18671bb428fSJakub Kicinski static void show_prog_maps(int fd, u32 num_maps) 18771bb428fSJakub Kicinski { 18871bb428fSJakub Kicinski struct bpf_prog_info info = {}; 18971bb428fSJakub Kicinski __u32 len = sizeof(info); 19071bb428fSJakub Kicinski __u32 map_ids[num_maps]; 19171bb428fSJakub Kicinski unsigned int i; 19271bb428fSJakub Kicinski int err; 19371bb428fSJakub Kicinski 19471bb428fSJakub Kicinski info.nr_map_ids = num_maps; 19571bb428fSJakub Kicinski info.map_ids = ptr_to_u64(map_ids); 19671bb428fSJakub Kicinski 19771bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 19871bb428fSJakub Kicinski if (err || !info.nr_map_ids) 19971bb428fSJakub Kicinski return; 20071bb428fSJakub Kicinski 201743cc665SQuentin Monnet if (json_output) { 202743cc665SQuentin Monnet jsonw_name(json_wtr, "map_ids"); 203743cc665SQuentin Monnet jsonw_start_array(json_wtr); 204743cc665SQuentin Monnet for (i = 0; i < info.nr_map_ids; i++) 205743cc665SQuentin Monnet jsonw_uint(json_wtr, map_ids[i]); 206743cc665SQuentin Monnet jsonw_end_array(json_wtr); 207743cc665SQuentin Monnet } else { 20871bb428fSJakub Kicinski printf(" map_ids "); 20971bb428fSJakub Kicinski for (i = 0; i < info.nr_map_ids; i++) 21071bb428fSJakub Kicinski printf("%u%s", map_ids[i], 21171bb428fSJakub Kicinski i == info.nr_map_ids - 1 ? "" : ","); 21271bb428fSJakub Kicinski } 21371bb428fSJakub Kicinski } 21471bb428fSJakub Kicinski 215743cc665SQuentin Monnet static void print_prog_json(struct bpf_prog_info *info, int fd) 216743cc665SQuentin Monnet { 217743cc665SQuentin Monnet char *memlock; 218743cc665SQuentin Monnet 219743cc665SQuentin Monnet jsonw_start_object(json_wtr); 220743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "id", info->id); 221743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 222743cc665SQuentin Monnet jsonw_string_field(json_wtr, "type", 223743cc665SQuentin Monnet prog_type_name[info->type]); 22471bb428fSJakub Kicinski else 225743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "type", info->type); 22671bb428fSJakub Kicinski 227743cc665SQuentin Monnet if (*info->name) 228743cc665SQuentin Monnet jsonw_string_field(json_wtr, "name", info->name); 22971bb428fSJakub Kicinski 230743cc665SQuentin Monnet jsonw_name(json_wtr, "tag"); 231743cc665SQuentin Monnet jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", 232743cc665SQuentin Monnet info->tag[0], info->tag[1], info->tag[2], info->tag[3], 233743cc665SQuentin Monnet info->tag[4], info->tag[5], info->tag[6], info->tag[7]); 23471bb428fSJakub Kicinski 23552262210SJakub Kicinski print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); 23652262210SJakub Kicinski 237743cc665SQuentin Monnet if (info->load_time) { 23871bb428fSJakub Kicinski char buf[32]; 23971bb428fSJakub Kicinski 240743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 24171bb428fSJakub Kicinski 24271bb428fSJakub Kicinski /* Piggy back on load_time, since 0 uid is a valid one */ 243743cc665SQuentin Monnet jsonw_string_field(json_wtr, "loaded_at", buf); 244743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "uid", info->created_by_uid); 24571bb428fSJakub Kicinski } 24671bb428fSJakub Kicinski 247743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len); 24871bb428fSJakub Kicinski 249743cc665SQuentin Monnet if (info->jited_prog_len) { 250743cc665SQuentin Monnet jsonw_bool_field(json_wtr, "jited", true); 251743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len); 252743cc665SQuentin Monnet } else { 253743cc665SQuentin Monnet jsonw_bool_field(json_wtr, "jited", false); 254743cc665SQuentin Monnet } 255743cc665SQuentin Monnet 256743cc665SQuentin Monnet memlock = get_fdinfo(fd, "memlock"); 257743cc665SQuentin Monnet if (memlock) 258743cc665SQuentin Monnet jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock)); 259743cc665SQuentin Monnet free(memlock); 260743cc665SQuentin Monnet 261743cc665SQuentin Monnet if (info->nr_map_ids) 262743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 263743cc665SQuentin Monnet 2644990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 2654990f1f4SPrashant Bhole struct pinned_obj *obj; 2664990f1f4SPrashant Bhole 2674990f1f4SPrashant Bhole jsonw_name(json_wtr, "pinned"); 2684990f1f4SPrashant Bhole jsonw_start_array(json_wtr); 2694990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 2704990f1f4SPrashant Bhole if (obj->id == info->id) 2714990f1f4SPrashant Bhole jsonw_string(json_wtr, obj->path); 2724990f1f4SPrashant Bhole } 2734990f1f4SPrashant Bhole jsonw_end_array(json_wtr); 2744990f1f4SPrashant Bhole } 2754990f1f4SPrashant Bhole 276743cc665SQuentin Monnet jsonw_end_object(json_wtr); 277743cc665SQuentin Monnet } 278743cc665SQuentin Monnet 279743cc665SQuentin Monnet static void print_prog_plain(struct bpf_prog_info *info, int fd) 280743cc665SQuentin Monnet { 281743cc665SQuentin Monnet char *memlock; 282743cc665SQuentin Monnet 283743cc665SQuentin Monnet printf("%u: ", info->id); 284743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 285743cc665SQuentin Monnet printf("%s ", prog_type_name[info->type]); 286743cc665SQuentin Monnet else 287743cc665SQuentin Monnet printf("type %u ", info->type); 288743cc665SQuentin Monnet 289743cc665SQuentin Monnet if (*info->name) 290743cc665SQuentin Monnet printf("name %s ", info->name); 291743cc665SQuentin Monnet 292743cc665SQuentin Monnet printf("tag "); 293743cc665SQuentin Monnet fprint_hex(stdout, info->tag, BPF_TAG_SIZE, ""); 29452262210SJakub Kicinski print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); 295743cc665SQuentin Monnet printf("\n"); 296743cc665SQuentin Monnet 297743cc665SQuentin Monnet if (info->load_time) { 298743cc665SQuentin Monnet char buf[32]; 299743cc665SQuentin Monnet 300743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 301743cc665SQuentin Monnet 302743cc665SQuentin Monnet /* Piggy back on load_time, since 0 uid is a valid one */ 303743cc665SQuentin Monnet printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); 304743cc665SQuentin Monnet } 305743cc665SQuentin Monnet 306743cc665SQuentin Monnet printf("\txlated %uB", info->xlated_prog_len); 307743cc665SQuentin Monnet 308743cc665SQuentin Monnet if (info->jited_prog_len) 309743cc665SQuentin Monnet printf(" jited %uB", info->jited_prog_len); 31071bb428fSJakub Kicinski else 31171bb428fSJakub Kicinski printf(" not jited"); 31271bb428fSJakub Kicinski 31371bb428fSJakub Kicinski memlock = get_fdinfo(fd, "memlock"); 31471bb428fSJakub Kicinski if (memlock) 31571bb428fSJakub Kicinski printf(" memlock %sB", memlock); 31671bb428fSJakub Kicinski free(memlock); 31771bb428fSJakub Kicinski 318743cc665SQuentin Monnet if (info->nr_map_ids) 319743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 32071bb428fSJakub Kicinski 3214990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 3224990f1f4SPrashant Bhole struct pinned_obj *obj; 3234990f1f4SPrashant Bhole 3244990f1f4SPrashant Bhole printf("\n"); 3254990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 3264990f1f4SPrashant Bhole if (obj->id == info->id) 3274990f1f4SPrashant Bhole printf("\tpinned %s\n", obj->path); 3284990f1f4SPrashant Bhole } 3294990f1f4SPrashant Bhole } 3304990f1f4SPrashant Bhole 33171bb428fSJakub Kicinski printf("\n"); 332743cc665SQuentin Monnet } 333743cc665SQuentin Monnet 334743cc665SQuentin Monnet static int show_prog(int fd) 335743cc665SQuentin Monnet { 336743cc665SQuentin Monnet struct bpf_prog_info info = {}; 337743cc665SQuentin Monnet __u32 len = sizeof(info); 338743cc665SQuentin Monnet int err; 339743cc665SQuentin Monnet 340743cc665SQuentin Monnet err = bpf_obj_get_info_by_fd(fd, &info, &len); 341743cc665SQuentin Monnet if (err) { 3429a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 343743cc665SQuentin Monnet return -1; 344743cc665SQuentin Monnet } 345743cc665SQuentin Monnet 346743cc665SQuentin Monnet if (json_output) 347743cc665SQuentin Monnet print_prog_json(&info, fd); 348743cc665SQuentin Monnet else 349743cc665SQuentin Monnet print_prog_plain(&info, fd); 35071bb428fSJakub Kicinski 35171bb428fSJakub Kicinski return 0; 35271bb428fSJakub Kicinski } 35371bb428fSJakub Kicinski 35471bb428fSJakub Kicinski static int do_show(int argc, char **argv) 355743cc665SQuentin Monnet { 356743cc665SQuentin Monnet __u32 id = 0; 35771bb428fSJakub Kicinski int err; 35871bb428fSJakub Kicinski int fd; 35971bb428fSJakub Kicinski 360c541b734SPrashant Bhole if (show_pinned) 3614990f1f4SPrashant Bhole build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); 3624990f1f4SPrashant Bhole 36371bb428fSJakub Kicinski if (argc == 2) { 36471bb428fSJakub Kicinski fd = prog_parse_fd(&argc, &argv); 36571bb428fSJakub Kicinski if (fd < 0) 36671bb428fSJakub Kicinski return -1; 36771bb428fSJakub Kicinski 36871bb428fSJakub Kicinski return show_prog(fd); 36971bb428fSJakub Kicinski } 37071bb428fSJakub Kicinski 37171bb428fSJakub Kicinski if (argc) 37271bb428fSJakub Kicinski return BAD_ARG(); 37371bb428fSJakub Kicinski 374743cc665SQuentin Monnet if (json_output) 375743cc665SQuentin Monnet jsonw_start_array(json_wtr); 37671bb428fSJakub Kicinski while (true) { 37771bb428fSJakub Kicinski err = bpf_prog_get_next_id(id, &id); 37871bb428fSJakub Kicinski if (err) { 3791739c26dSQuentin Monnet if (errno == ENOENT) { 3801739c26dSQuentin Monnet err = 0; 38171bb428fSJakub Kicinski break; 3821739c26dSQuentin Monnet } 3839a5ab8bfSQuentin Monnet p_err("can't get next program: %s%s", strerror(errno), 3849a5ab8bfSQuentin Monnet errno == EINVAL ? " -- kernel too old?" : ""); 385743cc665SQuentin Monnet err = -1; 386743cc665SQuentin Monnet break; 38771bb428fSJakub Kicinski } 38871bb428fSJakub Kicinski 38971bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 39071bb428fSJakub Kicinski if (fd < 0) { 3918207c6ddSJakub Kicinski if (errno == ENOENT) 3928207c6ddSJakub Kicinski continue; 3939a5ab8bfSQuentin Monnet p_err("can't get prog by id (%u): %s", 39471bb428fSJakub Kicinski id, strerror(errno)); 395743cc665SQuentin Monnet err = -1; 396743cc665SQuentin Monnet break; 39771bb428fSJakub Kicinski } 39871bb428fSJakub Kicinski 39971bb428fSJakub Kicinski err = show_prog(fd); 40071bb428fSJakub Kicinski close(fd); 40171bb428fSJakub Kicinski if (err) 402743cc665SQuentin Monnet break; 40371bb428fSJakub Kicinski } 40471bb428fSJakub Kicinski 405743cc665SQuentin Monnet if (json_output) 406743cc665SQuentin Monnet jsonw_end_array(json_wtr); 407743cc665SQuentin Monnet 408743cc665SQuentin Monnet return err; 40971bb428fSJakub Kicinski } 41071bb428fSJakub Kicinski 41171bb428fSJakub Kicinski static int do_dump(int argc, char **argv) 41271bb428fSJakub Kicinski { 41371bb428fSJakub Kicinski struct bpf_prog_info info = {}; 4147105e828SDaniel Borkmann struct dump_data dd = {}; 41571bb428fSJakub Kicinski __u32 len = sizeof(info); 41671bb428fSJakub Kicinski unsigned int buf_size; 41771bb428fSJakub Kicinski char *filepath = NULL; 41871bb428fSJakub Kicinski bool opcodes = false; 419*b6c1cedbSJiong Wang bool visual = false; 42071bb428fSJakub Kicinski unsigned char *buf; 42171bb428fSJakub Kicinski __u32 *member_len; 42271bb428fSJakub Kicinski __u64 *member_ptr; 42371bb428fSJakub Kicinski ssize_t n; 42471bb428fSJakub Kicinski int err; 42571bb428fSJakub Kicinski int fd; 42671bb428fSJakub Kicinski 42771bb428fSJakub Kicinski if (is_prefix(*argv, "jited")) { 42871bb428fSJakub Kicinski member_len = &info.jited_prog_len; 42971bb428fSJakub Kicinski member_ptr = &info.jited_prog_insns; 43071bb428fSJakub Kicinski } else if (is_prefix(*argv, "xlated")) { 43171bb428fSJakub Kicinski member_len = &info.xlated_prog_len; 43271bb428fSJakub Kicinski member_ptr = &info.xlated_prog_insns; 43371bb428fSJakub Kicinski } else { 4349a5ab8bfSQuentin Monnet p_err("expected 'xlated' or 'jited', got: %s", *argv); 43571bb428fSJakub Kicinski return -1; 43671bb428fSJakub Kicinski } 43771bb428fSJakub Kicinski NEXT_ARG(); 43871bb428fSJakub Kicinski 43971bb428fSJakub Kicinski if (argc < 2) 44071bb428fSJakub Kicinski usage(); 44171bb428fSJakub Kicinski 44271bb428fSJakub Kicinski fd = prog_parse_fd(&argc, &argv); 44371bb428fSJakub Kicinski if (fd < 0) 44471bb428fSJakub Kicinski return -1; 44571bb428fSJakub Kicinski 44671bb428fSJakub Kicinski if (is_prefix(*argv, "file")) { 44771bb428fSJakub Kicinski NEXT_ARG(); 44871bb428fSJakub Kicinski if (!argc) { 4499a5ab8bfSQuentin Monnet p_err("expected file path"); 45071bb428fSJakub Kicinski return -1; 45171bb428fSJakub Kicinski } 45271bb428fSJakub Kicinski 45371bb428fSJakub Kicinski filepath = *argv; 45471bb428fSJakub Kicinski NEXT_ARG(); 45571bb428fSJakub Kicinski } else if (is_prefix(*argv, "opcodes")) { 45671bb428fSJakub Kicinski opcodes = true; 45771bb428fSJakub Kicinski NEXT_ARG(); 458*b6c1cedbSJiong Wang } else if (is_prefix(*argv, "visual")) { 459*b6c1cedbSJiong Wang visual = true; 460*b6c1cedbSJiong Wang NEXT_ARG(); 46171bb428fSJakub Kicinski } 46271bb428fSJakub Kicinski 46371bb428fSJakub Kicinski if (argc) { 46471bb428fSJakub Kicinski usage(); 46571bb428fSJakub Kicinski return -1; 46671bb428fSJakub Kicinski } 46771bb428fSJakub Kicinski 46871bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 46971bb428fSJakub Kicinski if (err) { 4709a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 47171bb428fSJakub Kicinski return -1; 47271bb428fSJakub Kicinski } 47371bb428fSJakub Kicinski 47471bb428fSJakub Kicinski if (!*member_len) { 4759a5ab8bfSQuentin Monnet p_info("no instructions returned"); 47671bb428fSJakub Kicinski close(fd); 47771bb428fSJakub Kicinski return 0; 47871bb428fSJakub Kicinski } 47971bb428fSJakub Kicinski 48071bb428fSJakub Kicinski buf_size = *member_len; 48171bb428fSJakub Kicinski 48271bb428fSJakub Kicinski buf = malloc(buf_size); 48371bb428fSJakub Kicinski if (!buf) { 4849a5ab8bfSQuentin Monnet p_err("mem alloc failed"); 48571bb428fSJakub Kicinski close(fd); 48671bb428fSJakub Kicinski return -1; 48771bb428fSJakub Kicinski } 48871bb428fSJakub Kicinski 48971bb428fSJakub Kicinski memset(&info, 0, sizeof(info)); 49071bb428fSJakub Kicinski 49171bb428fSJakub Kicinski *member_ptr = ptr_to_u64(buf); 49271bb428fSJakub Kicinski *member_len = buf_size; 49371bb428fSJakub Kicinski 49471bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 49571bb428fSJakub Kicinski close(fd); 49671bb428fSJakub Kicinski if (err) { 4979a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 49871bb428fSJakub Kicinski goto err_free; 49971bb428fSJakub Kicinski } 50071bb428fSJakub Kicinski 50171bb428fSJakub Kicinski if (*member_len > buf_size) { 5029a5ab8bfSQuentin Monnet p_err("too many instructions returned"); 50371bb428fSJakub Kicinski goto err_free; 50471bb428fSJakub Kicinski } 50571bb428fSJakub Kicinski 5067105e828SDaniel Borkmann if ((member_len == &info.jited_prog_len && 5077105e828SDaniel Borkmann info.jited_prog_insns == 0) || 5087105e828SDaniel Borkmann (member_len == &info.xlated_prog_len && 5097105e828SDaniel Borkmann info.xlated_prog_insns == 0)) { 5107105e828SDaniel Borkmann p_err("error retrieving insn dump: kernel.kptr_restrict set?"); 5117105e828SDaniel Borkmann goto err_free; 5127105e828SDaniel Borkmann } 5137105e828SDaniel Borkmann 51471bb428fSJakub Kicinski if (filepath) { 51571bb428fSJakub Kicinski fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 51671bb428fSJakub Kicinski if (fd < 0) { 5179a5ab8bfSQuentin Monnet p_err("can't open file %s: %s", filepath, 51871bb428fSJakub Kicinski strerror(errno)); 51971bb428fSJakub Kicinski goto err_free; 52071bb428fSJakub Kicinski } 52171bb428fSJakub Kicinski 52271bb428fSJakub Kicinski n = write(fd, buf, *member_len); 52371bb428fSJakub Kicinski close(fd); 52471bb428fSJakub Kicinski if (n != *member_len) { 5259a5ab8bfSQuentin Monnet p_err("error writing output file: %s", 52671bb428fSJakub Kicinski n < 0 ? strerror(errno) : "short write"); 52771bb428fSJakub Kicinski goto err_free; 52871bb428fSJakub Kicinski } 52952c84d36SQuentin Monnet 53052c84d36SQuentin Monnet if (json_output) 53152c84d36SQuentin Monnet jsonw_null(json_wtr); 5323197239dSJiong Wang } else if (member_len == &info.jited_prog_len) { 533e6593596SJiong Wang const char *name = NULL; 534e6593596SJiong Wang 535e6593596SJiong Wang if (info.ifindex) { 536e6593596SJiong Wang name = ifindex_to_bfd_name_ns(info.ifindex, 537e6593596SJiong Wang info.netns_dev, 538e6593596SJiong Wang info.netns_ino); 539e6593596SJiong Wang if (!name) 540e6593596SJiong Wang goto err_free; 541e6593596SJiong Wang } 542e6593596SJiong Wang 543e6593596SJiong Wang disasm_print_insn(buf, *member_len, opcodes, name); 544*b6c1cedbSJiong Wang } else if (visual) { 545*b6c1cedbSJiong Wang if (json_output) 546*b6c1cedbSJiong Wang jsonw_null(json_wtr); 547*b6c1cedbSJiong Wang else 548*b6c1cedbSJiong Wang dump_xlated_cfg(buf, *member_len); 5497105e828SDaniel Borkmann } else { 5507105e828SDaniel Borkmann kernel_syms_load(&dd); 551f05e2c32SQuentin Monnet if (json_output) 5527105e828SDaniel Borkmann dump_xlated_json(&dd, buf, *member_len, opcodes); 553f05e2c32SQuentin Monnet else 5547105e828SDaniel Borkmann dump_xlated_plain(&dd, buf, *member_len, opcodes); 5557105e828SDaniel Borkmann kernel_syms_destroy(&dd); 5567105e828SDaniel Borkmann } 55771bb428fSJakub Kicinski 55871bb428fSJakub Kicinski free(buf); 55971bb428fSJakub Kicinski return 0; 56071bb428fSJakub Kicinski 56171bb428fSJakub Kicinski err_free: 56271bb428fSJakub Kicinski free(buf); 56371bb428fSJakub Kicinski return -1; 56471bb428fSJakub Kicinski } 56571bb428fSJakub Kicinski 56671bb428fSJakub Kicinski static int do_pin(int argc, char **argv) 56771bb428fSJakub Kicinski { 568004b45c0SQuentin Monnet int err; 569004b45c0SQuentin Monnet 570004b45c0SQuentin Monnet err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); 571004b45c0SQuentin Monnet if (!err && json_output) 572004b45c0SQuentin Monnet jsonw_null(json_wtr); 573004b45c0SQuentin Monnet return err; 57471bb428fSJakub Kicinski } 57571bb428fSJakub Kicinski 57649a086c2SRoman Gushchin static int do_load(int argc, char **argv) 57749a086c2SRoman Gushchin { 57849a086c2SRoman Gushchin struct bpf_object *obj; 57949a086c2SRoman Gushchin int prog_fd; 58049a086c2SRoman Gushchin 58149a086c2SRoman Gushchin if (argc != 2) 58249a086c2SRoman Gushchin usage(); 58349a086c2SRoman Gushchin 58449a086c2SRoman Gushchin if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) { 585b4fac96dSJakub Kicinski p_err("failed to load program"); 58649a086c2SRoman Gushchin return -1; 58749a086c2SRoman Gushchin } 58849a086c2SRoman Gushchin 58949a086c2SRoman Gushchin if (do_pin_fd(prog_fd, argv[1])) { 590b4fac96dSJakub Kicinski p_err("failed to pin program"); 59149a086c2SRoman Gushchin return -1; 59249a086c2SRoman Gushchin } 59349a086c2SRoman Gushchin 59449a086c2SRoman Gushchin if (json_output) 59549a086c2SRoman Gushchin jsonw_null(json_wtr); 59649a086c2SRoman Gushchin 59749a086c2SRoman Gushchin return 0; 59849a086c2SRoman Gushchin } 59949a086c2SRoman Gushchin 60071bb428fSJakub Kicinski static int do_help(int argc, char **argv) 60171bb428fSJakub Kicinski { 602004b45c0SQuentin Monnet if (json_output) { 603004b45c0SQuentin Monnet jsonw_null(json_wtr); 604004b45c0SQuentin Monnet return 0; 605004b45c0SQuentin Monnet } 606004b45c0SQuentin Monnet 60771bb428fSJakub Kicinski fprintf(stderr, 6086ebe6dbdSJakub Kicinski "Usage: %s %s { show | list } [PROG]\n" 609*b6c1cedbSJiong Wang " %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n" 6108dfbc6d1SQuentin Monnet " %s %s dump jited PROG [{ file FILE | opcodes }]\n" 61171bb428fSJakub Kicinski " %s %s pin PROG FILE\n" 61249a086c2SRoman Gushchin " %s %s load OBJ FILE\n" 61371bb428fSJakub Kicinski " %s %s help\n" 61471bb428fSJakub Kicinski "\n" 61571bb428fSJakub Kicinski " " HELP_SPEC_PROGRAM "\n" 6160641c3c8SQuentin Monnet " " HELP_SPEC_OPTIONS "\n" 61771bb428fSJakub Kicinski "", 61871bb428fSJakub Kicinski bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 61949a086c2SRoman Gushchin bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); 62071bb428fSJakub Kicinski 62171bb428fSJakub Kicinski return 0; 62271bb428fSJakub Kicinski } 62371bb428fSJakub Kicinski 62471bb428fSJakub Kicinski static const struct cmd cmds[] = { 62571bb428fSJakub Kicinski { "show", do_show }, 6266ebe6dbdSJakub Kicinski { "list", do_show }, 6279f606179SQuentin Monnet { "help", do_help }, 62871bb428fSJakub Kicinski { "dump", do_dump }, 62971bb428fSJakub Kicinski { "pin", do_pin }, 63049a086c2SRoman Gushchin { "load", do_load }, 63171bb428fSJakub Kicinski { 0 } 63271bb428fSJakub Kicinski }; 63371bb428fSJakub Kicinski 63471bb428fSJakub Kicinski int do_prog(int argc, char **argv) 63571bb428fSJakub Kicinski { 63671bb428fSJakub Kicinski return cmd_select(cmds, argc, argv, do_help); 63771bb428fSJakub Kicinski } 638