171bb428fSJakub Kicinski /* 271e07ddcSJakub Kicinski * Copyright (C) 2017-2018 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 #include <errno.h> 3571bb428fSJakub Kicinski #include <fcntl.h> 36c9c35995SJakub Kicinski #include <stdarg.h> 3771bb428fSJakub Kicinski #include <stdio.h> 3871bb428fSJakub Kicinski #include <stdlib.h> 3971bb428fSJakub Kicinski #include <string.h> 4071bb428fSJakub Kicinski #include <time.h> 4171bb428fSJakub Kicinski #include <unistd.h> 42*ba6dd679SJakub Kicinski #include <net/if.h> 4371bb428fSJakub Kicinski #include <sys/types.h> 4471bb428fSJakub Kicinski #include <sys/stat.h> 4571bb428fSJakub Kicinski 4671bb428fSJakub Kicinski #include <bpf.h> 4749a086c2SRoman Gushchin #include <libbpf.h> 4871bb428fSJakub Kicinski 49b6c1cedbSJiong Wang #include "cfg.h" 5071bb428fSJakub Kicinski #include "main.h" 5173bb5b4fSJiong Wang #include "xlated_dumper.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", 70393de512SAndrey Ignatov [BPF_PROG_TYPE_SK_MSG] = "sk_msg", 71393de512SAndrey Ignatov [BPF_PROG_TYPE_RAW_TRACEPOINT] = "raw_tracepoint", 72393de512SAndrey Ignatov [BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr", 736bdd533cSSean Young [BPF_PROG_TYPE_LIRC_MODE2] = "lirc_mode2", 7471bb428fSJakub Kicinski }; 7571bb428fSJakub Kicinski 7671bb428fSJakub Kicinski static void print_boot_time(__u64 nsecs, char *buf, unsigned int size) 7771bb428fSJakub Kicinski { 7871bb428fSJakub Kicinski struct timespec real_time_ts, boot_time_ts; 7971bb428fSJakub Kicinski time_t wallclock_secs; 8071bb428fSJakub Kicinski struct tm load_tm; 8171bb428fSJakub Kicinski 8271bb428fSJakub Kicinski buf[--size] = '\0'; 8371bb428fSJakub Kicinski 8471bb428fSJakub Kicinski if (clock_gettime(CLOCK_REALTIME, &real_time_ts) || 8571bb428fSJakub Kicinski clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) { 8671bb428fSJakub Kicinski perror("Can't read clocks"); 8771bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 8871bb428fSJakub Kicinski return; 8971bb428fSJakub Kicinski } 9071bb428fSJakub Kicinski 9171bb428fSJakub Kicinski wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) + 9207480cbcSJakub Kicinski (real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) / 9307480cbcSJakub Kicinski 1000000000; 9407480cbcSJakub Kicinski 9571bb428fSJakub Kicinski 9671bb428fSJakub Kicinski if (!localtime_r(&wallclock_secs, &load_tm)) { 9771bb428fSJakub Kicinski snprintf(buf, size, "%llu", nsecs / 1000000000); 9871bb428fSJakub Kicinski return; 9971bb428fSJakub Kicinski } 10071bb428fSJakub Kicinski 101a3fe1f6fSQuentin Monnet if (json_output) 102a3fe1f6fSQuentin Monnet strftime(buf, size, "%s", &load_tm); 103a3fe1f6fSQuentin Monnet else 104a3fe1f6fSQuentin Monnet strftime(buf, size, "%FT%T%z", &load_tm); 10571bb428fSJakub Kicinski } 10671bb428fSJakub Kicinski 10771bb428fSJakub Kicinski static int prog_fd_by_tag(unsigned char *tag) 10871bb428fSJakub Kicinski { 10971bb428fSJakub Kicinski struct bpf_prog_info info = {}; 11071bb428fSJakub Kicinski __u32 len = sizeof(info); 11171bb428fSJakub Kicinski unsigned int id = 0; 11271bb428fSJakub Kicinski int err; 11371bb428fSJakub Kicinski int fd; 11471bb428fSJakub Kicinski 11571bb428fSJakub Kicinski while (true) { 11671bb428fSJakub Kicinski err = bpf_prog_get_next_id(id, &id); 11771bb428fSJakub Kicinski if (err) { 1189a5ab8bfSQuentin Monnet p_err("%s", strerror(errno)); 11971bb428fSJakub Kicinski return -1; 12071bb428fSJakub Kicinski } 12171bb428fSJakub Kicinski 12271bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 12371bb428fSJakub Kicinski if (fd < 0) { 1249a5ab8bfSQuentin Monnet p_err("can't get prog by id (%u): %s", 12571bb428fSJakub Kicinski id, strerror(errno)); 12671bb428fSJakub Kicinski return -1; 12771bb428fSJakub Kicinski } 12871bb428fSJakub Kicinski 12971bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 13071bb428fSJakub Kicinski if (err) { 1319a5ab8bfSQuentin Monnet p_err("can't get prog info (%u): %s", 13271bb428fSJakub Kicinski id, strerror(errno)); 13371bb428fSJakub Kicinski close(fd); 13471bb428fSJakub Kicinski return -1; 13571bb428fSJakub Kicinski } 13671bb428fSJakub Kicinski 13771bb428fSJakub Kicinski if (!memcmp(tag, info.tag, BPF_TAG_SIZE)) 13871bb428fSJakub Kicinski return fd; 13971bb428fSJakub Kicinski 14071bb428fSJakub Kicinski close(fd); 14171bb428fSJakub Kicinski } 14271bb428fSJakub Kicinski } 14371bb428fSJakub Kicinski 14471bb428fSJakub Kicinski int prog_parse_fd(int *argc, char ***argv) 14571bb428fSJakub Kicinski { 14671bb428fSJakub Kicinski int fd; 14771bb428fSJakub Kicinski 14871bb428fSJakub Kicinski if (is_prefix(**argv, "id")) { 14971bb428fSJakub Kicinski unsigned int id; 15071bb428fSJakub Kicinski char *endptr; 15171bb428fSJakub Kicinski 15271bb428fSJakub Kicinski NEXT_ARGP(); 15371bb428fSJakub Kicinski 15471bb428fSJakub Kicinski id = strtoul(**argv, &endptr, 0); 15571bb428fSJakub Kicinski if (*endptr) { 1569a5ab8bfSQuentin Monnet p_err("can't parse %s as ID", **argv); 15771bb428fSJakub Kicinski return -1; 15871bb428fSJakub Kicinski } 15971bb428fSJakub Kicinski NEXT_ARGP(); 16071bb428fSJakub Kicinski 16171bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 16271bb428fSJakub Kicinski if (fd < 0) 1639a5ab8bfSQuentin Monnet p_err("get by id (%u): %s", id, strerror(errno)); 16471bb428fSJakub Kicinski return fd; 16571bb428fSJakub Kicinski } else if (is_prefix(**argv, "tag")) { 16671bb428fSJakub Kicinski unsigned char tag[BPF_TAG_SIZE]; 16771bb428fSJakub Kicinski 16871bb428fSJakub Kicinski NEXT_ARGP(); 16971bb428fSJakub Kicinski 17071bb428fSJakub Kicinski if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2, 17171bb428fSJakub Kicinski tag + 3, tag + 4, tag + 5, tag + 6, tag + 7) 17271bb428fSJakub Kicinski != BPF_TAG_SIZE) { 1739a5ab8bfSQuentin Monnet p_err("can't parse tag"); 17471bb428fSJakub Kicinski return -1; 17571bb428fSJakub Kicinski } 17671bb428fSJakub Kicinski NEXT_ARGP(); 17771bb428fSJakub Kicinski 17871bb428fSJakub Kicinski return prog_fd_by_tag(tag); 17971bb428fSJakub Kicinski } else if (is_prefix(**argv, "pinned")) { 18071bb428fSJakub Kicinski char *path; 18171bb428fSJakub Kicinski 18271bb428fSJakub Kicinski NEXT_ARGP(); 18371bb428fSJakub Kicinski 18471bb428fSJakub Kicinski path = **argv; 18571bb428fSJakub Kicinski NEXT_ARGP(); 18671bb428fSJakub Kicinski 18771bb428fSJakub Kicinski return open_obj_pinned_any(path, BPF_OBJ_PROG); 18871bb428fSJakub Kicinski } 18971bb428fSJakub Kicinski 1909a5ab8bfSQuentin Monnet p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv); 19171bb428fSJakub Kicinski return -1; 19271bb428fSJakub Kicinski } 19371bb428fSJakub Kicinski 19471bb428fSJakub Kicinski static void show_prog_maps(int fd, u32 num_maps) 19571bb428fSJakub Kicinski { 19671bb428fSJakub Kicinski struct bpf_prog_info info = {}; 19771bb428fSJakub Kicinski __u32 len = sizeof(info); 19871bb428fSJakub Kicinski __u32 map_ids[num_maps]; 19971bb428fSJakub Kicinski unsigned int i; 20071bb428fSJakub Kicinski int err; 20171bb428fSJakub Kicinski 20271bb428fSJakub Kicinski info.nr_map_ids = num_maps; 20371bb428fSJakub Kicinski info.map_ids = ptr_to_u64(map_ids); 20471bb428fSJakub Kicinski 20571bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 20671bb428fSJakub Kicinski if (err || !info.nr_map_ids) 20771bb428fSJakub Kicinski return; 20871bb428fSJakub Kicinski 209743cc665SQuentin Monnet if (json_output) { 210743cc665SQuentin Monnet jsonw_name(json_wtr, "map_ids"); 211743cc665SQuentin Monnet jsonw_start_array(json_wtr); 212743cc665SQuentin Monnet for (i = 0; i < info.nr_map_ids; i++) 213743cc665SQuentin Monnet jsonw_uint(json_wtr, map_ids[i]); 214743cc665SQuentin Monnet jsonw_end_array(json_wtr); 215743cc665SQuentin Monnet } else { 21671bb428fSJakub Kicinski printf(" map_ids "); 21771bb428fSJakub Kicinski for (i = 0; i < info.nr_map_ids; i++) 21871bb428fSJakub Kicinski printf("%u%s", map_ids[i], 21971bb428fSJakub Kicinski i == info.nr_map_ids - 1 ? "" : ","); 22071bb428fSJakub Kicinski } 22171bb428fSJakub Kicinski } 22271bb428fSJakub Kicinski 223743cc665SQuentin Monnet static void print_prog_json(struct bpf_prog_info *info, int fd) 224743cc665SQuentin Monnet { 225743cc665SQuentin Monnet char *memlock; 226743cc665SQuentin Monnet 227743cc665SQuentin Monnet jsonw_start_object(json_wtr); 228743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "id", info->id); 229743cc665SQuentin Monnet if (info->type < ARRAY_SIZE(prog_type_name)) 230743cc665SQuentin Monnet jsonw_string_field(json_wtr, "type", 231743cc665SQuentin Monnet prog_type_name[info->type]); 23271bb428fSJakub Kicinski else 233743cc665SQuentin Monnet jsonw_uint_field(json_wtr, "type", info->type); 23471bb428fSJakub Kicinski 235743cc665SQuentin Monnet if (*info->name) 236743cc665SQuentin Monnet jsonw_string_field(json_wtr, "name", info->name); 23771bb428fSJakub Kicinski 238743cc665SQuentin Monnet jsonw_name(json_wtr, "tag"); 239743cc665SQuentin Monnet jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"", 240743cc665SQuentin Monnet info->tag[0], info->tag[1], info->tag[2], info->tag[3], 241743cc665SQuentin Monnet info->tag[4], info->tag[5], info->tag[6], info->tag[7]); 24271bb428fSJakub Kicinski 2439b984a20SJiri Olsa jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible); 2449b984a20SJiri Olsa 24552262210SJakub Kicinski print_dev_json(info->ifindex, info->netns_dev, info->netns_ino); 24652262210SJakub Kicinski 247743cc665SQuentin Monnet if (info->load_time) { 24871bb428fSJakub Kicinski char buf[32]; 24971bb428fSJakub Kicinski 250743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 25171bb428fSJakub Kicinski 25271bb428fSJakub Kicinski /* Piggy back on load_time, since 0 uid is a valid one */ 253a3fe1f6fSQuentin Monnet jsonw_name(json_wtr, "loaded_at"); 254a3fe1f6fSQuentin Monnet jsonw_printf(json_wtr, "%s", 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 2754990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 2764990f1f4SPrashant Bhole struct pinned_obj *obj; 2774990f1f4SPrashant Bhole 2784990f1f4SPrashant Bhole jsonw_name(json_wtr, "pinned"); 2794990f1f4SPrashant Bhole jsonw_start_array(json_wtr); 2804990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 2814990f1f4SPrashant Bhole if (obj->id == info->id) 2824990f1f4SPrashant Bhole jsonw_string(json_wtr, obj->path); 2834990f1f4SPrashant Bhole } 2844990f1f4SPrashant Bhole jsonw_end_array(json_wtr); 2854990f1f4SPrashant Bhole } 2864990f1f4SPrashant 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, ""); 30552262210SJakub Kicinski print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino); 3069b984a20SJiri Olsa printf("%s", info->gpl_compatible ? " gpl" : ""); 307743cc665SQuentin Monnet printf("\n"); 308743cc665SQuentin Monnet 309743cc665SQuentin Monnet if (info->load_time) { 310743cc665SQuentin Monnet char buf[32]; 311743cc665SQuentin Monnet 312743cc665SQuentin Monnet print_boot_time(info->load_time, buf, sizeof(buf)); 313743cc665SQuentin Monnet 314743cc665SQuentin Monnet /* Piggy back on load_time, since 0 uid is a valid one */ 315743cc665SQuentin Monnet printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid); 316743cc665SQuentin Monnet } 317743cc665SQuentin Monnet 318743cc665SQuentin Monnet printf("\txlated %uB", info->xlated_prog_len); 319743cc665SQuentin Monnet 320743cc665SQuentin Monnet if (info->jited_prog_len) 321743cc665SQuentin Monnet printf(" jited %uB", info->jited_prog_len); 32271bb428fSJakub Kicinski else 32371bb428fSJakub Kicinski printf(" not jited"); 32471bb428fSJakub Kicinski 32571bb428fSJakub Kicinski memlock = get_fdinfo(fd, "memlock"); 32671bb428fSJakub Kicinski if (memlock) 32771bb428fSJakub Kicinski printf(" memlock %sB", memlock); 32871bb428fSJakub Kicinski free(memlock); 32971bb428fSJakub Kicinski 330743cc665SQuentin Monnet if (info->nr_map_ids) 331743cc665SQuentin Monnet show_prog_maps(fd, info->nr_map_ids); 33271bb428fSJakub Kicinski 3334990f1f4SPrashant Bhole if (!hash_empty(prog_table.table)) { 3344990f1f4SPrashant Bhole struct pinned_obj *obj; 3354990f1f4SPrashant Bhole 3364990f1f4SPrashant Bhole printf("\n"); 3374990f1f4SPrashant Bhole hash_for_each_possible(prog_table.table, obj, hash, info->id) { 3384990f1f4SPrashant Bhole if (obj->id == info->id) 3394990f1f4SPrashant Bhole printf("\tpinned %s\n", obj->path); 3404990f1f4SPrashant Bhole } 3414990f1f4SPrashant Bhole } 3424990f1f4SPrashant Bhole 34371bb428fSJakub Kicinski printf("\n"); 344743cc665SQuentin Monnet } 345743cc665SQuentin Monnet 346743cc665SQuentin Monnet static int show_prog(int fd) 347743cc665SQuentin Monnet { 348743cc665SQuentin Monnet struct bpf_prog_info info = {}; 349743cc665SQuentin Monnet __u32 len = sizeof(info); 350743cc665SQuentin Monnet int err; 351743cc665SQuentin Monnet 352743cc665SQuentin Monnet err = bpf_obj_get_info_by_fd(fd, &info, &len); 353743cc665SQuentin Monnet if (err) { 3549a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 355743cc665SQuentin Monnet return -1; 356743cc665SQuentin Monnet } 357743cc665SQuentin Monnet 358743cc665SQuentin Monnet if (json_output) 359743cc665SQuentin Monnet print_prog_json(&info, fd); 360743cc665SQuentin Monnet else 361743cc665SQuentin Monnet print_prog_plain(&info, fd); 36271bb428fSJakub Kicinski 36371bb428fSJakub Kicinski return 0; 36471bb428fSJakub Kicinski } 36571bb428fSJakub Kicinski 36671bb428fSJakub Kicinski static int do_show(int argc, char **argv) 367743cc665SQuentin Monnet { 368743cc665SQuentin Monnet __u32 id = 0; 36971bb428fSJakub Kicinski int err; 37071bb428fSJakub Kicinski int fd; 37171bb428fSJakub Kicinski 372c541b734SPrashant Bhole if (show_pinned) 3734990f1f4SPrashant Bhole build_pinned_obj_table(&prog_table, BPF_OBJ_PROG); 3744990f1f4SPrashant Bhole 37571bb428fSJakub Kicinski if (argc == 2) { 37671bb428fSJakub Kicinski fd = prog_parse_fd(&argc, &argv); 37771bb428fSJakub Kicinski if (fd < 0) 37871bb428fSJakub Kicinski return -1; 37971bb428fSJakub Kicinski 38071bb428fSJakub Kicinski return show_prog(fd); 38171bb428fSJakub Kicinski } 38271bb428fSJakub Kicinski 38371bb428fSJakub Kicinski if (argc) 38471bb428fSJakub Kicinski return BAD_ARG(); 38571bb428fSJakub Kicinski 386743cc665SQuentin Monnet if (json_output) 387743cc665SQuentin Monnet jsonw_start_array(json_wtr); 38871bb428fSJakub Kicinski while (true) { 38971bb428fSJakub Kicinski err = bpf_prog_get_next_id(id, &id); 39071bb428fSJakub Kicinski if (err) { 3911739c26dSQuentin Monnet if (errno == ENOENT) { 3921739c26dSQuentin Monnet err = 0; 39371bb428fSJakub Kicinski break; 3941739c26dSQuentin Monnet } 3959a5ab8bfSQuentin Monnet p_err("can't get next program: %s%s", strerror(errno), 3969a5ab8bfSQuentin Monnet errno == EINVAL ? " -- kernel too old?" : ""); 397743cc665SQuentin Monnet err = -1; 398743cc665SQuentin Monnet break; 39971bb428fSJakub Kicinski } 40071bb428fSJakub Kicinski 40171bb428fSJakub Kicinski fd = bpf_prog_get_fd_by_id(id); 40271bb428fSJakub Kicinski if (fd < 0) { 4038207c6ddSJakub Kicinski if (errno == ENOENT) 4048207c6ddSJakub Kicinski continue; 4059a5ab8bfSQuentin Monnet p_err("can't get prog by id (%u): %s", 40671bb428fSJakub Kicinski id, strerror(errno)); 407743cc665SQuentin Monnet err = -1; 408743cc665SQuentin Monnet break; 40971bb428fSJakub Kicinski } 41071bb428fSJakub Kicinski 41171bb428fSJakub Kicinski err = show_prog(fd); 41271bb428fSJakub Kicinski close(fd); 41371bb428fSJakub Kicinski if (err) 414743cc665SQuentin Monnet break; 41571bb428fSJakub Kicinski } 41671bb428fSJakub Kicinski 417743cc665SQuentin Monnet if (json_output) 418743cc665SQuentin Monnet jsonw_end_array(json_wtr); 419743cc665SQuentin Monnet 420743cc665SQuentin Monnet return err; 42171bb428fSJakub Kicinski } 42271bb428fSJakub Kicinski 42371bb428fSJakub Kicinski static int do_dump(int argc, char **argv) 42471bb428fSJakub Kicinski { 425f84192eeSSandipan Das unsigned long *func_ksyms = NULL; 42671bb428fSJakub Kicinski struct bpf_prog_info info = {}; 427f7f62c71SSandipan Das unsigned int *func_lens = NULL; 428f84192eeSSandipan Das unsigned int nr_func_ksyms; 429f7f62c71SSandipan Das unsigned int nr_func_lens; 4307105e828SDaniel Borkmann struct dump_data dd = {}; 43171bb428fSJakub Kicinski __u32 len = sizeof(info); 43271bb428fSJakub Kicinski unsigned int buf_size; 43371bb428fSJakub Kicinski char *filepath = NULL; 43471bb428fSJakub Kicinski bool opcodes = false; 435b6c1cedbSJiong Wang bool visual = false; 43671bb428fSJakub Kicinski unsigned char *buf; 43771bb428fSJakub Kicinski __u32 *member_len; 43871bb428fSJakub Kicinski __u64 *member_ptr; 43971bb428fSJakub Kicinski ssize_t n; 44071bb428fSJakub Kicinski int err; 44171bb428fSJakub Kicinski int fd; 44271bb428fSJakub Kicinski 44371bb428fSJakub Kicinski if (is_prefix(*argv, "jited")) { 44471bb428fSJakub Kicinski member_len = &info.jited_prog_len; 44571bb428fSJakub Kicinski member_ptr = &info.jited_prog_insns; 44671bb428fSJakub Kicinski } else if (is_prefix(*argv, "xlated")) { 44771bb428fSJakub Kicinski member_len = &info.xlated_prog_len; 44871bb428fSJakub Kicinski member_ptr = &info.xlated_prog_insns; 44971bb428fSJakub Kicinski } else { 4509a5ab8bfSQuentin Monnet p_err("expected 'xlated' or 'jited', got: %s", *argv); 45171bb428fSJakub Kicinski return -1; 45271bb428fSJakub Kicinski } 45371bb428fSJakub Kicinski NEXT_ARG(); 45471bb428fSJakub Kicinski 45571bb428fSJakub Kicinski if (argc < 2) 45671bb428fSJakub Kicinski usage(); 45771bb428fSJakub Kicinski 45871bb428fSJakub Kicinski fd = prog_parse_fd(&argc, &argv); 45971bb428fSJakub Kicinski if (fd < 0) 46071bb428fSJakub Kicinski return -1; 46171bb428fSJakub Kicinski 46271bb428fSJakub Kicinski if (is_prefix(*argv, "file")) { 46371bb428fSJakub Kicinski NEXT_ARG(); 46471bb428fSJakub Kicinski if (!argc) { 4659a5ab8bfSQuentin Monnet p_err("expected file path"); 46671bb428fSJakub Kicinski return -1; 46771bb428fSJakub Kicinski } 46871bb428fSJakub Kicinski 46971bb428fSJakub Kicinski filepath = *argv; 47071bb428fSJakub Kicinski NEXT_ARG(); 47171bb428fSJakub Kicinski } else if (is_prefix(*argv, "opcodes")) { 47271bb428fSJakub Kicinski opcodes = true; 47371bb428fSJakub Kicinski NEXT_ARG(); 474b6c1cedbSJiong Wang } else if (is_prefix(*argv, "visual")) { 475b6c1cedbSJiong Wang visual = true; 476b6c1cedbSJiong Wang NEXT_ARG(); 47771bb428fSJakub Kicinski } 47871bb428fSJakub Kicinski 47971bb428fSJakub Kicinski if (argc) { 48071bb428fSJakub Kicinski usage(); 48171bb428fSJakub Kicinski return -1; 48271bb428fSJakub Kicinski } 48371bb428fSJakub Kicinski 48471bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 48571bb428fSJakub Kicinski if (err) { 4869a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 48771bb428fSJakub Kicinski return -1; 48871bb428fSJakub Kicinski } 48971bb428fSJakub Kicinski 49071bb428fSJakub Kicinski if (!*member_len) { 4919a5ab8bfSQuentin Monnet p_info("no instructions returned"); 49271bb428fSJakub Kicinski close(fd); 49371bb428fSJakub Kicinski return 0; 49471bb428fSJakub Kicinski } 49571bb428fSJakub Kicinski 49671bb428fSJakub Kicinski buf_size = *member_len; 49771bb428fSJakub Kicinski 49871bb428fSJakub Kicinski buf = malloc(buf_size); 49971bb428fSJakub Kicinski if (!buf) { 5009a5ab8bfSQuentin Monnet p_err("mem alloc failed"); 50171bb428fSJakub Kicinski close(fd); 50271bb428fSJakub Kicinski return -1; 50371bb428fSJakub Kicinski } 50471bb428fSJakub Kicinski 505f84192eeSSandipan Das nr_func_ksyms = info.nr_jited_ksyms; 506f84192eeSSandipan Das if (nr_func_ksyms) { 507f84192eeSSandipan Das func_ksyms = malloc(nr_func_ksyms * sizeof(__u64)); 508f84192eeSSandipan Das if (!func_ksyms) { 509f84192eeSSandipan Das p_err("mem alloc failed"); 510f84192eeSSandipan Das close(fd); 511f84192eeSSandipan Das goto err_free; 512f84192eeSSandipan Das } 513f84192eeSSandipan Das } 514f84192eeSSandipan Das 515f7f62c71SSandipan Das nr_func_lens = info.nr_jited_func_lens; 516f7f62c71SSandipan Das if (nr_func_lens) { 517f7f62c71SSandipan Das func_lens = malloc(nr_func_lens * sizeof(__u32)); 518f7f62c71SSandipan Das if (!func_lens) { 519f7f62c71SSandipan Das p_err("mem alloc failed"); 520f7f62c71SSandipan Das close(fd); 521f7f62c71SSandipan Das goto err_free; 522f7f62c71SSandipan Das } 523f7f62c71SSandipan Das } 524f7f62c71SSandipan Das 52571bb428fSJakub Kicinski memset(&info, 0, sizeof(info)); 52671bb428fSJakub Kicinski 52771bb428fSJakub Kicinski *member_ptr = ptr_to_u64(buf); 52871bb428fSJakub Kicinski *member_len = buf_size; 529f84192eeSSandipan Das info.jited_ksyms = ptr_to_u64(func_ksyms); 530f84192eeSSandipan Das info.nr_jited_ksyms = nr_func_ksyms; 531f7f62c71SSandipan Das info.jited_func_lens = ptr_to_u64(func_lens); 532f7f62c71SSandipan Das info.nr_jited_func_lens = nr_func_lens; 53371bb428fSJakub Kicinski 53471bb428fSJakub Kicinski err = bpf_obj_get_info_by_fd(fd, &info, &len); 53571bb428fSJakub Kicinski close(fd); 53671bb428fSJakub Kicinski if (err) { 5379a5ab8bfSQuentin Monnet p_err("can't get prog info: %s", strerror(errno)); 53871bb428fSJakub Kicinski goto err_free; 53971bb428fSJakub Kicinski } 54071bb428fSJakub Kicinski 54171bb428fSJakub Kicinski if (*member_len > buf_size) { 5429a5ab8bfSQuentin Monnet p_err("too many instructions returned"); 54371bb428fSJakub Kicinski goto err_free; 54471bb428fSJakub Kicinski } 54571bb428fSJakub Kicinski 546f84192eeSSandipan Das if (info.nr_jited_ksyms > nr_func_ksyms) { 547f84192eeSSandipan Das p_err("too many addresses returned"); 548f84192eeSSandipan Das goto err_free; 549f84192eeSSandipan Das } 550f84192eeSSandipan Das 551f7f62c71SSandipan Das if (info.nr_jited_func_lens > nr_func_lens) { 552f7f62c71SSandipan Das p_err("too many values returned"); 553f7f62c71SSandipan Das goto err_free; 554f7f62c71SSandipan Das } 555f7f62c71SSandipan Das 5567105e828SDaniel Borkmann if ((member_len == &info.jited_prog_len && 5577105e828SDaniel Borkmann info.jited_prog_insns == 0) || 5587105e828SDaniel Borkmann (member_len == &info.xlated_prog_len && 5597105e828SDaniel Borkmann info.xlated_prog_insns == 0)) { 5607105e828SDaniel Borkmann p_err("error retrieving insn dump: kernel.kptr_restrict set?"); 5617105e828SDaniel Borkmann goto err_free; 5627105e828SDaniel Borkmann } 5637105e828SDaniel Borkmann 56471bb428fSJakub Kicinski if (filepath) { 56571bb428fSJakub Kicinski fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600); 56671bb428fSJakub Kicinski if (fd < 0) { 5679a5ab8bfSQuentin Monnet p_err("can't open file %s: %s", filepath, 56871bb428fSJakub Kicinski strerror(errno)); 56971bb428fSJakub Kicinski goto err_free; 57071bb428fSJakub Kicinski } 57171bb428fSJakub Kicinski 57271bb428fSJakub Kicinski n = write(fd, buf, *member_len); 57371bb428fSJakub Kicinski close(fd); 57471bb428fSJakub Kicinski if (n != *member_len) { 5759a5ab8bfSQuentin Monnet p_err("error writing output file: %s", 57671bb428fSJakub Kicinski n < 0 ? strerror(errno) : "short write"); 57771bb428fSJakub Kicinski goto err_free; 57871bb428fSJakub Kicinski } 57952c84d36SQuentin Monnet 58052c84d36SQuentin Monnet if (json_output) 58152c84d36SQuentin Monnet jsonw_null(json_wtr); 5823197239dSJiong Wang } else if (member_len == &info.jited_prog_len) { 583e6593596SJiong Wang const char *name = NULL; 584e6593596SJiong Wang 585e6593596SJiong Wang if (info.ifindex) { 586e6593596SJiong Wang name = ifindex_to_bfd_name_ns(info.ifindex, 587e6593596SJiong Wang info.netns_dev, 588e6593596SJiong Wang info.netns_ino); 589e6593596SJiong Wang if (!name) 590e6593596SJiong Wang goto err_free; 591e6593596SJiong Wang } 592e6593596SJiong Wang 593f7f62c71SSandipan Das if (info.nr_jited_func_lens && info.jited_func_lens) { 594f7f62c71SSandipan Das struct kernel_sym *sym = NULL; 595f7f62c71SSandipan Das char sym_name[SYM_MAX_NAME]; 596f7f62c71SSandipan Das unsigned char *img = buf; 597f7f62c71SSandipan Das __u64 *ksyms = NULL; 598f7f62c71SSandipan Das __u32 *lens; 599f7f62c71SSandipan Das __u32 i; 600f7f62c71SSandipan Das 601f7f62c71SSandipan Das if (info.nr_jited_ksyms) { 602f7f62c71SSandipan Das kernel_syms_load(&dd); 603f7f62c71SSandipan Das ksyms = (__u64 *) info.jited_ksyms; 604f7f62c71SSandipan Das } 605f7f62c71SSandipan Das 606f7f62c71SSandipan Das if (json_output) 607f7f62c71SSandipan Das jsonw_start_array(json_wtr); 608f7f62c71SSandipan Das 609f7f62c71SSandipan Das lens = (__u32 *) info.jited_func_lens; 610f7f62c71SSandipan Das for (i = 0; i < info.nr_jited_func_lens; i++) { 611f7f62c71SSandipan Das if (ksyms) { 612f7f62c71SSandipan Das sym = kernel_syms_search(&dd, ksyms[i]); 613f7f62c71SSandipan Das if (sym) 614f7f62c71SSandipan Das sprintf(sym_name, "%s", sym->name); 615f7f62c71SSandipan Das else 616f7f62c71SSandipan Das sprintf(sym_name, "0x%016llx", ksyms[i]); 617f7f62c71SSandipan Das } else { 618f7f62c71SSandipan Das strcpy(sym_name, "unknown"); 619f7f62c71SSandipan Das } 620f7f62c71SSandipan Das 621f7f62c71SSandipan Das if (json_output) { 622f7f62c71SSandipan Das jsonw_start_object(json_wtr); 623f7f62c71SSandipan Das jsonw_name(json_wtr, "name"); 624f7f62c71SSandipan Das jsonw_string(json_wtr, sym_name); 625f7f62c71SSandipan Das jsonw_name(json_wtr, "insns"); 626f7f62c71SSandipan Das } else { 627f7f62c71SSandipan Das printf("%s:\n", sym_name); 628f7f62c71SSandipan Das } 629f7f62c71SSandipan Das 630f7f62c71SSandipan Das disasm_print_insn(img, lens[i], opcodes, name); 631f7f62c71SSandipan Das img += lens[i]; 632f7f62c71SSandipan Das 633f7f62c71SSandipan Das if (json_output) 634f7f62c71SSandipan Das jsonw_end_object(json_wtr); 635f7f62c71SSandipan Das else 636f7f62c71SSandipan Das printf("\n"); 637f7f62c71SSandipan Das } 638f7f62c71SSandipan Das 639f7f62c71SSandipan Das if (json_output) 640f7f62c71SSandipan Das jsonw_end_array(json_wtr); 641f7f62c71SSandipan Das } else { 642e6593596SJiong Wang disasm_print_insn(buf, *member_len, opcodes, name); 643f7f62c71SSandipan Das } 644b6c1cedbSJiong Wang } else if (visual) { 645b6c1cedbSJiong Wang if (json_output) 646b6c1cedbSJiong Wang jsonw_null(json_wtr); 647b6c1cedbSJiong Wang else 648b6c1cedbSJiong Wang dump_xlated_cfg(buf, *member_len); 6497105e828SDaniel Borkmann } else { 6507105e828SDaniel Borkmann kernel_syms_load(&dd); 651f84192eeSSandipan Das dd.nr_jited_ksyms = info.nr_jited_ksyms; 652f84192eeSSandipan Das dd.jited_ksyms = (__u64 *) info.jited_ksyms; 653f84192eeSSandipan Das 654f05e2c32SQuentin Monnet if (json_output) 6557105e828SDaniel Borkmann dump_xlated_json(&dd, buf, *member_len, opcodes); 656f05e2c32SQuentin Monnet else 6577105e828SDaniel Borkmann dump_xlated_plain(&dd, buf, *member_len, opcodes); 6587105e828SDaniel Borkmann kernel_syms_destroy(&dd); 6597105e828SDaniel Borkmann } 66071bb428fSJakub Kicinski 66171bb428fSJakub Kicinski free(buf); 662f84192eeSSandipan Das free(func_ksyms); 663f7f62c71SSandipan Das free(func_lens); 66471bb428fSJakub Kicinski return 0; 66571bb428fSJakub Kicinski 66671bb428fSJakub Kicinski err_free: 66771bb428fSJakub Kicinski free(buf); 668f84192eeSSandipan Das free(func_ksyms); 669f7f62c71SSandipan Das free(func_lens); 67071bb428fSJakub Kicinski return -1; 67171bb428fSJakub Kicinski } 67271bb428fSJakub Kicinski 67371bb428fSJakub Kicinski static int do_pin(int argc, char **argv) 67471bb428fSJakub Kicinski { 675004b45c0SQuentin Monnet int err; 676004b45c0SQuentin Monnet 677004b45c0SQuentin Monnet err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id); 678004b45c0SQuentin Monnet if (!err && json_output) 679004b45c0SQuentin Monnet jsonw_null(json_wtr); 680004b45c0SQuentin Monnet return err; 68171bb428fSJakub Kicinski } 68271bb428fSJakub Kicinski 68349a086c2SRoman Gushchin static int do_load(int argc, char **argv) 68449a086c2SRoman Gushchin { 685*ba6dd679SJakub Kicinski struct bpf_prog_load_attr attr = { 686*ba6dd679SJakub Kicinski .prog_type = BPF_PROG_TYPE_UNSPEC, 687*ba6dd679SJakub Kicinski }; 6888d1fc3deSJakub Kicinski const char *objfile, *pinfile; 68949a086c2SRoman Gushchin struct bpf_object *obj; 69049a086c2SRoman Gushchin int prog_fd; 69149a086c2SRoman Gushchin 6928d1fc3deSJakub Kicinski if (!REQ_ARGS(2)) 6938d1fc3deSJakub Kicinski return -1; 6948d1fc3deSJakub Kicinski objfile = GET_ARG(); 6958d1fc3deSJakub Kicinski pinfile = GET_ARG(); 69649a086c2SRoman Gushchin 697*ba6dd679SJakub Kicinski while (argc) { 698*ba6dd679SJakub Kicinski if (is_prefix(*argv, "dev")) { 699*ba6dd679SJakub Kicinski NEXT_ARG(); 700*ba6dd679SJakub Kicinski 701*ba6dd679SJakub Kicinski if (attr.ifindex) { 702*ba6dd679SJakub Kicinski p_err("offload device already specified"); 703*ba6dd679SJakub Kicinski return -1; 704*ba6dd679SJakub Kicinski } 705*ba6dd679SJakub Kicinski if (!REQ_ARGS(1)) 706*ba6dd679SJakub Kicinski return -1; 707*ba6dd679SJakub Kicinski 708*ba6dd679SJakub Kicinski attr.ifindex = if_nametoindex(*argv); 709*ba6dd679SJakub Kicinski if (!attr.ifindex) { 710*ba6dd679SJakub Kicinski p_err("unrecognized netdevice '%s': %s", 711*ba6dd679SJakub Kicinski *argv, strerror(errno)); 712*ba6dd679SJakub Kicinski return -1; 713*ba6dd679SJakub Kicinski } 714*ba6dd679SJakub Kicinski NEXT_ARG(); 715*ba6dd679SJakub Kicinski } else { 716*ba6dd679SJakub Kicinski p_err("expected no more arguments or 'dev', got: '%s'?", 717*ba6dd679SJakub Kicinski *argv); 718*ba6dd679SJakub Kicinski return -1; 719*ba6dd679SJakub Kicinski } 720*ba6dd679SJakub Kicinski } 721*ba6dd679SJakub Kicinski 722*ba6dd679SJakub Kicinski attr.file = objfile; 723*ba6dd679SJakub Kicinski 724*ba6dd679SJakub Kicinski if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) { 725b4fac96dSJakub Kicinski p_err("failed to load program"); 72649a086c2SRoman Gushchin return -1; 72749a086c2SRoman Gushchin } 72849a086c2SRoman Gushchin 7298d1fc3deSJakub Kicinski if (do_pin_fd(prog_fd, pinfile)) 730bfee71fbSJakub Kicinski goto err_close_obj; 73149a086c2SRoman Gushchin 73249a086c2SRoman Gushchin if (json_output) 73349a086c2SRoman Gushchin jsonw_null(json_wtr); 73449a086c2SRoman Gushchin 735bfee71fbSJakub Kicinski bpf_object__close(obj); 736bfee71fbSJakub Kicinski 73749a086c2SRoman Gushchin return 0; 738bfee71fbSJakub Kicinski 739bfee71fbSJakub Kicinski err_close_obj: 740bfee71fbSJakub Kicinski bpf_object__close(obj); 741bfee71fbSJakub Kicinski return -1; 74249a086c2SRoman Gushchin } 74349a086c2SRoman Gushchin 74471bb428fSJakub Kicinski static int do_help(int argc, char **argv) 74571bb428fSJakub Kicinski { 746004b45c0SQuentin Monnet if (json_output) { 747004b45c0SQuentin Monnet jsonw_null(json_wtr); 748004b45c0SQuentin Monnet return 0; 749004b45c0SQuentin Monnet } 750004b45c0SQuentin Monnet 75171bb428fSJakub Kicinski fprintf(stderr, 7526ebe6dbdSJakub Kicinski "Usage: %s %s { show | list } [PROG]\n" 753b6c1cedbSJiong Wang " %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n" 7548dfbc6d1SQuentin Monnet " %s %s dump jited PROG [{ file FILE | opcodes }]\n" 75571bb428fSJakub Kicinski " %s %s pin PROG FILE\n" 756*ba6dd679SJakub Kicinski " %s %s load OBJ FILE [dev NAME]\n" 75771bb428fSJakub Kicinski " %s %s help\n" 75871bb428fSJakub Kicinski "\n" 75971bb428fSJakub Kicinski " " HELP_SPEC_PROGRAM "\n" 7600641c3c8SQuentin Monnet " " HELP_SPEC_OPTIONS "\n" 76171bb428fSJakub Kicinski "", 76271bb428fSJakub Kicinski bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2], 76349a086c2SRoman Gushchin bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]); 76471bb428fSJakub Kicinski 76571bb428fSJakub Kicinski return 0; 76671bb428fSJakub Kicinski } 76771bb428fSJakub Kicinski 76871bb428fSJakub Kicinski static const struct cmd cmds[] = { 76971bb428fSJakub Kicinski { "show", do_show }, 7706ebe6dbdSJakub Kicinski { "list", do_show }, 7719f606179SQuentin Monnet { "help", do_help }, 77271bb428fSJakub Kicinski { "dump", do_dump }, 77371bb428fSJakub Kicinski { "pin", do_pin }, 77449a086c2SRoman Gushchin { "load", do_load }, 77571bb428fSJakub Kicinski { 0 } 77671bb428fSJakub Kicinski }; 77771bb428fSJakub Kicinski 77871bb428fSJakub Kicinski int do_prog(int argc, char **argv) 77971bb428fSJakub Kicinski { 78071bb428fSJakub Kicinski return cmd_select(cmds, argc, argv, do_help); 78171bb428fSJakub Kicinski } 782