162306a36Sopenharmony_ci// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 262306a36Sopenharmony_ci/* Copyright (c) 2019 Netronome Systems, Inc. */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <errno.h> 562306a36Sopenharmony_ci#include <fcntl.h> 662306a36Sopenharmony_ci#include <string.h> 762306a36Sopenharmony_ci#include <stdlib.h> 862306a36Sopenharmony_ci#include <unistd.h> 962306a36Sopenharmony_ci#include <net/if.h> 1062306a36Sopenharmony_ci#include <sys/utsname.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/btf.h> 1362306a36Sopenharmony_ci#include <linux/filter.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/version.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "bpf.h" 1862306a36Sopenharmony_ci#include "libbpf.h" 1962306a36Sopenharmony_ci#include "libbpf_internal.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* On Ubuntu LINUX_VERSION_CODE doesn't correspond to info.release, 2262306a36Sopenharmony_ci * but Ubuntu provides /proc/version_signature file, as described at 2362306a36Sopenharmony_ci * https://ubuntu.com/kernel, with an example contents below, which we 2462306a36Sopenharmony_ci * can use to get a proper LINUX_VERSION_CODE. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Ubuntu 5.4.0-12.15-generic 5.4.8 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * In the above, 5.4.8 is what kernel is actually expecting, while 2962306a36Sopenharmony_ci * uname() call will return 5.4.0 in info.release. 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_cistatic __u32 get_ubuntu_kernel_version(void) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci const char *ubuntu_kver_file = "/proc/version_signature"; 3462306a36Sopenharmony_ci __u32 major, minor, patch; 3562306a36Sopenharmony_ci int ret; 3662306a36Sopenharmony_ci FILE *f; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (faccessat(AT_FDCWD, ubuntu_kver_file, R_OK, AT_EACCESS) != 0) 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci f = fopen(ubuntu_kver_file, "re"); 4262306a36Sopenharmony_ci if (!f) 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci ret = fscanf(f, "%*s %*s %u.%u.%u\n", &major, &minor, &patch); 4662306a36Sopenharmony_ci fclose(f); 4762306a36Sopenharmony_ci if (ret != 3) 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return KERNEL_VERSION(major, minor, patch); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* On Debian LINUX_VERSION_CODE doesn't correspond to info.release. 5462306a36Sopenharmony_ci * Instead, it is provided in info.version. An example content of 5562306a36Sopenharmony_ci * Debian 10 looks like the below. 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * utsname::release 4.19.0-22-amd64 5862306a36Sopenharmony_ci * utsname::version #1 SMP Debian 4.19.260-1 (2022-09-29) 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * In the above, 4.19.260 is what kernel is actually expecting, while 6162306a36Sopenharmony_ci * uname() call will return 4.19.0 in info.release. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistatic __u32 get_debian_kernel_version(struct utsname *info) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci __u32 major, minor, patch; 6662306a36Sopenharmony_ci char *p; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci p = strstr(info->version, "Debian "); 6962306a36Sopenharmony_ci if (!p) { 7062306a36Sopenharmony_ci /* This is not a Debian kernel. */ 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (sscanf(p, "Debian %u.%u.%u", &major, &minor, &patch) != 3) 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return KERNEL_VERSION(major, minor, patch); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci__u32 get_kernel_version(void) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci __u32 major, minor, patch, version; 8362306a36Sopenharmony_ci struct utsname info; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* Check if this is an Ubuntu kernel. */ 8662306a36Sopenharmony_ci version = get_ubuntu_kernel_version(); 8762306a36Sopenharmony_ci if (version != 0) 8862306a36Sopenharmony_ci return version; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci uname(&info); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* Check if this is a Debian kernel. */ 9362306a36Sopenharmony_ci version = get_debian_kernel_version(&info); 9462306a36Sopenharmony_ci if (version != 0) 9562306a36Sopenharmony_ci return version; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3) 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return KERNEL_VERSION(major, minor, patch); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int probe_prog_load(enum bpf_prog_type prog_type, 10462306a36Sopenharmony_ci const struct bpf_insn *insns, size_t insns_cnt, 10562306a36Sopenharmony_ci char *log_buf, size_t log_buf_sz) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci LIBBPF_OPTS(bpf_prog_load_opts, opts, 10862306a36Sopenharmony_ci .log_buf = log_buf, 10962306a36Sopenharmony_ci .log_size = log_buf_sz, 11062306a36Sopenharmony_ci .log_level = log_buf ? 1 : 0, 11162306a36Sopenharmony_ci ); 11262306a36Sopenharmony_ci int fd, err, exp_err = 0; 11362306a36Sopenharmony_ci const char *exp_msg = NULL; 11462306a36Sopenharmony_ci char buf[4096]; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci switch (prog_type) { 11762306a36Sopenharmony_ci case BPF_PROG_TYPE_CGROUP_SOCK_ADDR: 11862306a36Sopenharmony_ci opts.expected_attach_type = BPF_CGROUP_INET4_CONNECT; 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci case BPF_PROG_TYPE_CGROUP_SOCKOPT: 12162306a36Sopenharmony_ci opts.expected_attach_type = BPF_CGROUP_GETSOCKOPT; 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci case BPF_PROG_TYPE_SK_LOOKUP: 12462306a36Sopenharmony_ci opts.expected_attach_type = BPF_SK_LOOKUP; 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci case BPF_PROG_TYPE_KPROBE: 12762306a36Sopenharmony_ci opts.kern_version = get_kernel_version(); 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case BPF_PROG_TYPE_LIRC_MODE2: 13062306a36Sopenharmony_ci opts.expected_attach_type = BPF_LIRC_MODE2; 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci case BPF_PROG_TYPE_TRACING: 13362306a36Sopenharmony_ci case BPF_PROG_TYPE_LSM: 13462306a36Sopenharmony_ci opts.log_buf = buf; 13562306a36Sopenharmony_ci opts.log_size = sizeof(buf); 13662306a36Sopenharmony_ci opts.log_level = 1; 13762306a36Sopenharmony_ci if (prog_type == BPF_PROG_TYPE_TRACING) 13862306a36Sopenharmony_ci opts.expected_attach_type = BPF_TRACE_FENTRY; 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci opts.expected_attach_type = BPF_MODIFY_RETURN; 14162306a36Sopenharmony_ci opts.attach_btf_id = 1; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci exp_err = -EINVAL; 14462306a36Sopenharmony_ci exp_msg = "attach_btf_id 1 is not a function"; 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci case BPF_PROG_TYPE_EXT: 14762306a36Sopenharmony_ci opts.log_buf = buf; 14862306a36Sopenharmony_ci opts.log_size = sizeof(buf); 14962306a36Sopenharmony_ci opts.log_level = 1; 15062306a36Sopenharmony_ci opts.attach_btf_id = 1; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci exp_err = -EINVAL; 15362306a36Sopenharmony_ci exp_msg = "Cannot replace kernel functions"; 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci case BPF_PROG_TYPE_SYSCALL: 15662306a36Sopenharmony_ci opts.prog_flags = BPF_F_SLEEPABLE; 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci case BPF_PROG_TYPE_STRUCT_OPS: 15962306a36Sopenharmony_ci exp_err = -524; /* -ENOTSUPP */ 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case BPF_PROG_TYPE_UNSPEC: 16262306a36Sopenharmony_ci case BPF_PROG_TYPE_SOCKET_FILTER: 16362306a36Sopenharmony_ci case BPF_PROG_TYPE_SCHED_CLS: 16462306a36Sopenharmony_ci case BPF_PROG_TYPE_SCHED_ACT: 16562306a36Sopenharmony_ci case BPF_PROG_TYPE_TRACEPOINT: 16662306a36Sopenharmony_ci case BPF_PROG_TYPE_XDP: 16762306a36Sopenharmony_ci case BPF_PROG_TYPE_PERF_EVENT: 16862306a36Sopenharmony_ci case BPF_PROG_TYPE_CGROUP_SKB: 16962306a36Sopenharmony_ci case BPF_PROG_TYPE_CGROUP_SOCK: 17062306a36Sopenharmony_ci case BPF_PROG_TYPE_LWT_IN: 17162306a36Sopenharmony_ci case BPF_PROG_TYPE_LWT_OUT: 17262306a36Sopenharmony_ci case BPF_PROG_TYPE_LWT_XMIT: 17362306a36Sopenharmony_ci case BPF_PROG_TYPE_SOCK_OPS: 17462306a36Sopenharmony_ci case BPF_PROG_TYPE_SK_SKB: 17562306a36Sopenharmony_ci case BPF_PROG_TYPE_CGROUP_DEVICE: 17662306a36Sopenharmony_ci case BPF_PROG_TYPE_SK_MSG: 17762306a36Sopenharmony_ci case BPF_PROG_TYPE_RAW_TRACEPOINT: 17862306a36Sopenharmony_ci case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE: 17962306a36Sopenharmony_ci case BPF_PROG_TYPE_LWT_SEG6LOCAL: 18062306a36Sopenharmony_ci case BPF_PROG_TYPE_SK_REUSEPORT: 18162306a36Sopenharmony_ci case BPF_PROG_TYPE_FLOW_DISSECTOR: 18262306a36Sopenharmony_ci case BPF_PROG_TYPE_CGROUP_SYSCTL: 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci case BPF_PROG_TYPE_NETFILTER: 18562306a36Sopenharmony_ci opts.expected_attach_type = BPF_NETFILTER; 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci default: 18862306a36Sopenharmony_ci return -EOPNOTSUPP; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insns_cnt, &opts); 19262306a36Sopenharmony_ci err = -errno; 19362306a36Sopenharmony_ci if (fd >= 0) 19462306a36Sopenharmony_ci close(fd); 19562306a36Sopenharmony_ci if (exp_err) { 19662306a36Sopenharmony_ci if (fd >= 0 || err != exp_err) 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci if (exp_msg && !strstr(buf, exp_msg)) 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci return 1; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci return fd >= 0 ? 1 : 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ciint libbpf_probe_bpf_prog_type(enum bpf_prog_type prog_type, const void *opts) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct bpf_insn insns[] = { 20862306a36Sopenharmony_ci BPF_MOV64_IMM(BPF_REG_0, 0), 20962306a36Sopenharmony_ci BPF_EXIT_INSN() 21062306a36Sopenharmony_ci }; 21162306a36Sopenharmony_ci const size_t insn_cnt = ARRAY_SIZE(insns); 21262306a36Sopenharmony_ci int ret; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (opts) 21562306a36Sopenharmony_ci return libbpf_err(-EINVAL); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ret = probe_prog_load(prog_type, insns, insn_cnt, NULL, 0); 21862306a36Sopenharmony_ci return libbpf_err(ret); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ciint libbpf__load_raw_btf(const char *raw_types, size_t types_len, 22262306a36Sopenharmony_ci const char *str_sec, size_t str_len) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct btf_header hdr = { 22562306a36Sopenharmony_ci .magic = BTF_MAGIC, 22662306a36Sopenharmony_ci .version = BTF_VERSION, 22762306a36Sopenharmony_ci .hdr_len = sizeof(struct btf_header), 22862306a36Sopenharmony_ci .type_len = types_len, 22962306a36Sopenharmony_ci .str_off = types_len, 23062306a36Sopenharmony_ci .str_len = str_len, 23162306a36Sopenharmony_ci }; 23262306a36Sopenharmony_ci int btf_fd, btf_len; 23362306a36Sopenharmony_ci __u8 *raw_btf; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci btf_len = hdr.hdr_len + hdr.type_len + hdr.str_len; 23662306a36Sopenharmony_ci raw_btf = malloc(btf_len); 23762306a36Sopenharmony_ci if (!raw_btf) 23862306a36Sopenharmony_ci return -ENOMEM; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci memcpy(raw_btf, &hdr, sizeof(hdr)); 24162306a36Sopenharmony_ci memcpy(raw_btf + hdr.hdr_len, raw_types, hdr.type_len); 24262306a36Sopenharmony_ci memcpy(raw_btf + hdr.hdr_len + hdr.type_len, str_sec, hdr.str_len); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci btf_fd = bpf_btf_load(raw_btf, btf_len, NULL); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci free(raw_btf); 24762306a36Sopenharmony_ci return btf_fd; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int load_local_storage_btf(void) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci const char strs[] = "\0bpf_spin_lock\0val\0cnt\0l"; 25362306a36Sopenharmony_ci /* struct bpf_spin_lock { 25462306a36Sopenharmony_ci * int val; 25562306a36Sopenharmony_ci * }; 25662306a36Sopenharmony_ci * struct val { 25762306a36Sopenharmony_ci * int cnt; 25862306a36Sopenharmony_ci * struct bpf_spin_lock l; 25962306a36Sopenharmony_ci * }; 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_ci __u32 types[] = { 26262306a36Sopenharmony_ci /* int */ 26362306a36Sopenharmony_ci BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ 26462306a36Sopenharmony_ci /* struct bpf_spin_lock */ /* [2] */ 26562306a36Sopenharmony_ci BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), 4), 26662306a36Sopenharmony_ci BTF_MEMBER_ENC(15, 1, 0), /* int val; */ 26762306a36Sopenharmony_ci /* struct val */ /* [3] */ 26862306a36Sopenharmony_ci BTF_TYPE_ENC(15, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 2), 8), 26962306a36Sopenharmony_ci BTF_MEMBER_ENC(19, 1, 0), /* int cnt; */ 27062306a36Sopenharmony_ci BTF_MEMBER_ENC(23, 2, 32),/* struct bpf_spin_lock l; */ 27162306a36Sopenharmony_ci }; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return libbpf__load_raw_btf((char *)types, sizeof(types), 27462306a36Sopenharmony_ci strs, sizeof(strs)); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic int probe_map_create(enum bpf_map_type map_type) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci LIBBPF_OPTS(bpf_map_create_opts, opts); 28062306a36Sopenharmony_ci int key_size, value_size, max_entries; 28162306a36Sopenharmony_ci __u32 btf_key_type_id = 0, btf_value_type_id = 0; 28262306a36Sopenharmony_ci int fd = -1, btf_fd = -1, fd_inner = -1, exp_err = 0, err = 0; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci key_size = sizeof(__u32); 28562306a36Sopenharmony_ci value_size = sizeof(__u32); 28662306a36Sopenharmony_ci max_entries = 1; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci switch (map_type) { 28962306a36Sopenharmony_ci case BPF_MAP_TYPE_STACK_TRACE: 29062306a36Sopenharmony_ci value_size = sizeof(__u64); 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci case BPF_MAP_TYPE_LPM_TRIE: 29362306a36Sopenharmony_ci key_size = sizeof(__u64); 29462306a36Sopenharmony_ci value_size = sizeof(__u64); 29562306a36Sopenharmony_ci opts.map_flags = BPF_F_NO_PREALLOC; 29662306a36Sopenharmony_ci break; 29762306a36Sopenharmony_ci case BPF_MAP_TYPE_CGROUP_STORAGE: 29862306a36Sopenharmony_ci case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE: 29962306a36Sopenharmony_ci key_size = sizeof(struct bpf_cgroup_storage_key); 30062306a36Sopenharmony_ci value_size = sizeof(__u64); 30162306a36Sopenharmony_ci max_entries = 0; 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci case BPF_MAP_TYPE_QUEUE: 30462306a36Sopenharmony_ci case BPF_MAP_TYPE_STACK: 30562306a36Sopenharmony_ci key_size = 0; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci case BPF_MAP_TYPE_SK_STORAGE: 30862306a36Sopenharmony_ci case BPF_MAP_TYPE_INODE_STORAGE: 30962306a36Sopenharmony_ci case BPF_MAP_TYPE_TASK_STORAGE: 31062306a36Sopenharmony_ci case BPF_MAP_TYPE_CGRP_STORAGE: 31162306a36Sopenharmony_ci btf_key_type_id = 1; 31262306a36Sopenharmony_ci btf_value_type_id = 3; 31362306a36Sopenharmony_ci value_size = 8; 31462306a36Sopenharmony_ci max_entries = 0; 31562306a36Sopenharmony_ci opts.map_flags = BPF_F_NO_PREALLOC; 31662306a36Sopenharmony_ci btf_fd = load_local_storage_btf(); 31762306a36Sopenharmony_ci if (btf_fd < 0) 31862306a36Sopenharmony_ci return btf_fd; 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci case BPF_MAP_TYPE_RINGBUF: 32162306a36Sopenharmony_ci case BPF_MAP_TYPE_USER_RINGBUF: 32262306a36Sopenharmony_ci key_size = 0; 32362306a36Sopenharmony_ci value_size = 0; 32462306a36Sopenharmony_ci max_entries = sysconf(_SC_PAGE_SIZE); 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci case BPF_MAP_TYPE_STRUCT_OPS: 32762306a36Sopenharmony_ci /* we'll get -ENOTSUPP for invalid BTF type ID for struct_ops */ 32862306a36Sopenharmony_ci opts.btf_vmlinux_value_type_id = 1; 32962306a36Sopenharmony_ci exp_err = -524; /* -ENOTSUPP */ 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci case BPF_MAP_TYPE_BLOOM_FILTER: 33262306a36Sopenharmony_ci key_size = 0; 33362306a36Sopenharmony_ci max_entries = 1; 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci case BPF_MAP_TYPE_HASH: 33662306a36Sopenharmony_ci case BPF_MAP_TYPE_ARRAY: 33762306a36Sopenharmony_ci case BPF_MAP_TYPE_PROG_ARRAY: 33862306a36Sopenharmony_ci case BPF_MAP_TYPE_PERF_EVENT_ARRAY: 33962306a36Sopenharmony_ci case BPF_MAP_TYPE_PERCPU_HASH: 34062306a36Sopenharmony_ci case BPF_MAP_TYPE_PERCPU_ARRAY: 34162306a36Sopenharmony_ci case BPF_MAP_TYPE_CGROUP_ARRAY: 34262306a36Sopenharmony_ci case BPF_MAP_TYPE_LRU_HASH: 34362306a36Sopenharmony_ci case BPF_MAP_TYPE_LRU_PERCPU_HASH: 34462306a36Sopenharmony_ci case BPF_MAP_TYPE_ARRAY_OF_MAPS: 34562306a36Sopenharmony_ci case BPF_MAP_TYPE_HASH_OF_MAPS: 34662306a36Sopenharmony_ci case BPF_MAP_TYPE_DEVMAP: 34762306a36Sopenharmony_ci case BPF_MAP_TYPE_DEVMAP_HASH: 34862306a36Sopenharmony_ci case BPF_MAP_TYPE_SOCKMAP: 34962306a36Sopenharmony_ci case BPF_MAP_TYPE_CPUMAP: 35062306a36Sopenharmony_ci case BPF_MAP_TYPE_XSKMAP: 35162306a36Sopenharmony_ci case BPF_MAP_TYPE_SOCKHASH: 35262306a36Sopenharmony_ci case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY: 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci case BPF_MAP_TYPE_UNSPEC: 35562306a36Sopenharmony_ci default: 35662306a36Sopenharmony_ci return -EOPNOTSUPP; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS || 36062306a36Sopenharmony_ci map_type == BPF_MAP_TYPE_HASH_OF_MAPS) { 36162306a36Sopenharmony_ci fd_inner = bpf_map_create(BPF_MAP_TYPE_HASH, NULL, 36262306a36Sopenharmony_ci sizeof(__u32), sizeof(__u32), 1, NULL); 36362306a36Sopenharmony_ci if (fd_inner < 0) 36462306a36Sopenharmony_ci goto cleanup; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci opts.inner_map_fd = fd_inner; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (btf_fd >= 0) { 37062306a36Sopenharmony_ci opts.btf_fd = btf_fd; 37162306a36Sopenharmony_ci opts.btf_key_type_id = btf_key_type_id; 37262306a36Sopenharmony_ci opts.btf_value_type_id = btf_value_type_id; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci fd = bpf_map_create(map_type, NULL, key_size, value_size, max_entries, &opts); 37662306a36Sopenharmony_ci err = -errno; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cicleanup: 37962306a36Sopenharmony_ci if (fd >= 0) 38062306a36Sopenharmony_ci close(fd); 38162306a36Sopenharmony_ci if (fd_inner >= 0) 38262306a36Sopenharmony_ci close(fd_inner); 38362306a36Sopenharmony_ci if (btf_fd >= 0) 38462306a36Sopenharmony_ci close(btf_fd); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (exp_err) 38762306a36Sopenharmony_ci return fd < 0 && err == exp_err ? 1 : 0; 38862306a36Sopenharmony_ci else 38962306a36Sopenharmony_ci return fd >= 0 ? 1 : 0; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ciint libbpf_probe_bpf_map_type(enum bpf_map_type map_type, const void *opts) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci int ret; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (opts) 39762306a36Sopenharmony_ci return libbpf_err(-EINVAL); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci ret = probe_map_create(map_type); 40062306a36Sopenharmony_ci return libbpf_err(ret); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ciint libbpf_probe_bpf_helper(enum bpf_prog_type prog_type, enum bpf_func_id helper_id, 40462306a36Sopenharmony_ci const void *opts) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct bpf_insn insns[] = { 40762306a36Sopenharmony_ci BPF_EMIT_CALL((__u32)helper_id), 40862306a36Sopenharmony_ci BPF_EXIT_INSN(), 40962306a36Sopenharmony_ci }; 41062306a36Sopenharmony_ci const size_t insn_cnt = ARRAY_SIZE(insns); 41162306a36Sopenharmony_ci char buf[4096]; 41262306a36Sopenharmony_ci int ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (opts) 41562306a36Sopenharmony_ci return libbpf_err(-EINVAL); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* we can't successfully load all prog types to check for BPF helper 41862306a36Sopenharmony_ci * support, so bail out with -EOPNOTSUPP error 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci switch (prog_type) { 42162306a36Sopenharmony_ci case BPF_PROG_TYPE_TRACING: 42262306a36Sopenharmony_ci case BPF_PROG_TYPE_EXT: 42362306a36Sopenharmony_ci case BPF_PROG_TYPE_LSM: 42462306a36Sopenharmony_ci case BPF_PROG_TYPE_STRUCT_OPS: 42562306a36Sopenharmony_ci return -EOPNOTSUPP; 42662306a36Sopenharmony_ci default: 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci buf[0] = '\0'; 43162306a36Sopenharmony_ci ret = probe_prog_load(prog_type, insns, insn_cnt, buf, sizeof(buf)); 43262306a36Sopenharmony_ci if (ret < 0) 43362306a36Sopenharmony_ci return libbpf_err(ret); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* If BPF verifier doesn't recognize BPF helper ID (enum bpf_func_id) 43662306a36Sopenharmony_ci * at all, it will emit something like "invalid func unknown#181". 43762306a36Sopenharmony_ci * If BPF verifier recognizes BPF helper but it's not supported for 43862306a36Sopenharmony_ci * given BPF program type, it will emit "unknown func bpf_sys_bpf#166". 43962306a36Sopenharmony_ci * In both cases, provided combination of BPF program type and BPF 44062306a36Sopenharmony_ci * helper is not supported by the kernel. 44162306a36Sopenharmony_ci * In all other cases, probe_prog_load() above will either succeed (e.g., 44262306a36Sopenharmony_ci * because BPF helper happens to accept no input arguments or it 44362306a36Sopenharmony_ci * accepts one input argument and initial PTR_TO_CTX is fine for 44462306a36Sopenharmony_ci * that), or we'll get some more specific BPF verifier error about 44562306a36Sopenharmony_ci * some unsatisfied conditions. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_ci if (ret == 0 && (strstr(buf, "invalid func ") || strstr(buf, "unknown func "))) 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci return 1; /* assume supported */ 45062306a36Sopenharmony_ci} 451