mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-25 23:35:26 +07:00
d859900c4c
This work adds BPF loader support for global data sections to libbpf. This allows to write BPF programs in more natural C-like way by being able to define global variables and const data. Back at LPC 2018 [0] we presented a first prototype which implemented support for global data sections by extending BPF syscall where union bpf_attr would get additional memory/size pair for each section passed during prog load in order to later add this base address into the ldimm64 instruction along with the user provided offset when accessing a variable. Consensus from LPC was that for proper upstream support, it would be more desirable to use maps instead of bpf_attr extension as this would allow for introspection of these sections as well as potential live updates of their content. This work follows this path by taking the following steps from loader side: 1) In bpf_object__elf_collect() step we pick up ".data", ".rodata", and ".bss" section information. 2) If present, in bpf_object__init_internal_map() we add maps to the obj's map array that corresponds to each of the present sections. Given section size and access properties can differ, a single entry array map is created with value size that is corresponding to the ELF section size of .data, .bss or .rodata. These internal maps are integrated into the normal map handling of libbpf such that when user traverses all obj maps, they can be differentiated from user-created ones via bpf_map__is_internal(). In later steps when we actually create these maps in the kernel via bpf_object__create_maps(), then for .data and .rodata sections their content is copied into the map through bpf_map_update_elem(). For .bss this is not necessary since array map is already zero-initialized by default. Additionally, for .rodata the map is frozen as read-only after setup, such that neither from program nor syscall side writes would be possible. 3) In bpf_program__collect_reloc() step, we record the corresponding map, insn index, and relocation type for the global data. 4) And last but not least in the actual relocation step in bpf_program__relocate(), we mark the ldimm64 instruction with src_reg = BPF_PSEUDO_MAP_VALUE where in the first imm field the map's file descriptor is stored as similarly done as in BPF_PSEUDO_MAP_FD, and in the second imm field (as ldimm64 is 2-insn wide) we store the access offset into the section. Given these maps have only single element ldimm64's off remains zero in both parts. 5) On kernel side, this special marked BPF_PSEUDO_MAP_VALUE load will then store the actual target address in order to have a 'map-lookup'-free access. That is, the actual map value base address + offset. The destination register in the verifier will then be marked as PTR_TO_MAP_VALUE, containing the fixed offset as reg->off and backing BPF map as reg->map_ptr. Meaning, it's treated as any other normal map value from verification side, only with efficient, direct value access instead of actual call to map lookup helper as in the typical case. Currently, only support for static global variables has been added, and libbpf rejects non-static global variables from loading. This can be lifted until we have proper semantics for how BPF will treat multi-object BPF loads. From BTF side, libbpf will set the value type id of the types corresponding to the ".bss", ".data" and ".rodata" names which LLVM will emit without the object name prefix. The key type will be left as zero, thus making use of the key-less BTF option in array maps. Simple example dump of program using globals vars in each section: # bpftool prog [...] 6784: sched_cls name load_static_dat tag a7e1291567277844 gpl loaded_at 2019-03-11T15:39:34+0000 uid 0 xlated 1776B jited 993B memlock 4096B map_ids 2238,2237,2235,2236,2239,2240 # bpftool map show id 2237 2237: array name test_glo.bss flags 0x0 key 4B value 64B max_entries 1 memlock 4096B # bpftool map show id 2235 2235: array name test_glo.data flags 0x0 key 4B value 64B max_entries 1 memlock 4096B # bpftool map show id 2236 2236: array name test_glo.rodata flags 0x80 key 4B value 96B max_entries 1 memlock 4096B # bpftool prog dump xlated id 6784 int load_static_data(struct __sk_buff * skb): ; int load_static_data(struct __sk_buff *skb) 0: (b7) r6 = 0 ; test_reloc(number, 0, &num0); 1: (63) *(u32 *)(r10 -4) = r6 2: (bf) r2 = r10 ; int load_static_data(struct __sk_buff *skb) 3: (07) r2 += -4 ; test_reloc(number, 0, &num0); 4: (18) r1 = map[id:2238] 6: (18) r3 = map[id:2237][0]+0 <-- direct addr in .bss area 8: (b7) r4 = 0 9: (85) call array_map_update_elem#100464 10: (b7) r1 = 1 ; test_reloc(number, 1, &num1); [...] ; test_reloc(string, 2, str2); 120: (18) r8 = map[id:2237][0]+16 <-- same here at offset +16 122: (18) r1 = map[id:2239] 124: (18) r3 = map[id:2237][0]+16 126: (b7) r4 = 0 127: (85) call array_map_update_elem#100464 128: (b7) r1 = 120 ; str1[5] = 'x'; 129: (73) *(u8 *)(r9 +5) = r1 ; test_reloc(string, 3, str1); 130: (b7) r1 = 3 131: (63) *(u32 *)(r10 -4) = r1 132: (b7) r9 = 3 133: (bf) r2 = r10 ; int load_static_data(struct __sk_buff *skb) 134: (07) r2 += -4 ; test_reloc(string, 3, str1); 135: (18) r1 = map[id:2239] 137: (18) r3 = map[id:2235][0]+16 <-- direct addr in .data area 139: (b7) r4 = 0 140: (85) call array_map_update_elem#100464 141: (b7) r1 = 111 ; __builtin_memcpy(&str2[2], "hello", sizeof("hello")); 142: (73) *(u8 *)(r8 +6) = r1 <-- further access based on .bss data 143: (b7) r1 = 108 144: (73) *(u8 *)(r8 +5) = r1 [...] For Cilium use-case in particular, this enables migrating configuration constants from Cilium daemon's generated header defines into global data sections such that expensive runtime recompilations with LLVM can be avoided altogether. Instead, the ELF file becomes effectively a "template", meaning, it is compiled only once (!) and the Cilium daemon will then rewrite relevant configuration data from the ELF's .data or .rodata sections directly instead of recompiling the program. The updated ELF is then loaded into the kernel and atomically replaces the existing program in the networking datapath. More info in [0]. Based upon recent fix in LLVM, commit c0db6b6bd444 ("[BPF] Don't fail for static variables"). [0] LPC 2018, BPF track, "ELF relocation for static data in BPF", http://vger.kernel.org/lpc-bpf2018.html#session-3 Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Andrii Nakryiko <andriin@fb.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
702 lines
17 KiB
C
702 lines
17 KiB
C
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
|
|
|
/*
|
|
* common eBPF ELF operations.
|
|
*
|
|
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
|
|
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
|
* Copyright (C) 2015 Huawei Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License (not later!)
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <memory.h>
|
|
#include <unistd.h>
|
|
#include <asm/unistd.h>
|
|
#include <linux/bpf.h>
|
|
#include "bpf.h"
|
|
#include "libbpf.h"
|
|
#include <errno.h>
|
|
|
|
/*
|
|
* When building perf, unistd.h is overridden. __NR_bpf is
|
|
* required to be defined explicitly.
|
|
*/
|
|
#ifndef __NR_bpf
|
|
# if defined(__i386__)
|
|
# define __NR_bpf 357
|
|
# elif defined(__x86_64__)
|
|
# define __NR_bpf 321
|
|
# elif defined(__aarch64__)
|
|
# define __NR_bpf 280
|
|
# elif defined(__sparc__)
|
|
# define __NR_bpf 349
|
|
# elif defined(__s390__)
|
|
# define __NR_bpf 351
|
|
# else
|
|
# error __NR_bpf not defined. libbpf does not support your arch.
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef min
|
|
#define min(x, y) ((x) < (y) ? (x) : (y))
|
|
#endif
|
|
|
|
static inline __u64 ptr_to_u64(const void *ptr)
|
|
{
|
|
return (__u64) (unsigned long) ptr;
|
|
}
|
|
|
|
static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
|
|
unsigned int size)
|
|
{
|
|
return syscall(__NR_bpf, cmd, attr, size);
|
|
}
|
|
|
|
static inline int sys_bpf_prog_load(union bpf_attr *attr, unsigned int size)
|
|
{
|
|
int fd;
|
|
|
|
do {
|
|
fd = sys_bpf(BPF_PROG_LOAD, attr, size);
|
|
} while (fd < 0 && errno == EAGAIN);
|
|
|
|
return fd;
|
|
}
|
|
|
|
int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr)
|
|
{
|
|
__u32 name_len = create_attr->name ? strlen(create_attr->name) : 0;
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, '\0', sizeof(attr));
|
|
|
|
attr.map_type = create_attr->map_type;
|
|
attr.key_size = create_attr->key_size;
|
|
attr.value_size = create_attr->value_size;
|
|
attr.max_entries = create_attr->max_entries;
|
|
attr.map_flags = create_attr->map_flags;
|
|
memcpy(attr.map_name, create_attr->name,
|
|
min(name_len, BPF_OBJ_NAME_LEN - 1));
|
|
attr.numa_node = create_attr->numa_node;
|
|
attr.btf_fd = create_attr->btf_fd;
|
|
attr.btf_key_type_id = create_attr->btf_key_type_id;
|
|
attr.btf_value_type_id = create_attr->btf_value_type_id;
|
|
attr.map_ifindex = create_attr->map_ifindex;
|
|
attr.inner_map_fd = create_attr->inner_map_fd;
|
|
|
|
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
|
|
int key_size, int value_size, int max_entries,
|
|
__u32 map_flags, int node)
|
|
{
|
|
struct bpf_create_map_attr map_attr = {};
|
|
|
|
map_attr.name = name;
|
|
map_attr.map_type = map_type;
|
|
map_attr.map_flags = map_flags;
|
|
map_attr.key_size = key_size;
|
|
map_attr.value_size = value_size;
|
|
map_attr.max_entries = max_entries;
|
|
if (node >= 0) {
|
|
map_attr.numa_node = node;
|
|
map_attr.map_flags |= BPF_F_NUMA_NODE;
|
|
}
|
|
|
|
return bpf_create_map_xattr(&map_attr);
|
|
}
|
|
|
|
int bpf_create_map(enum bpf_map_type map_type, int key_size,
|
|
int value_size, int max_entries, __u32 map_flags)
|
|
{
|
|
struct bpf_create_map_attr map_attr = {};
|
|
|
|
map_attr.map_type = map_type;
|
|
map_attr.map_flags = map_flags;
|
|
map_attr.key_size = key_size;
|
|
map_attr.value_size = value_size;
|
|
map_attr.max_entries = max_entries;
|
|
|
|
return bpf_create_map_xattr(&map_attr);
|
|
}
|
|
|
|
int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
|
|
int key_size, int value_size, int max_entries,
|
|
__u32 map_flags)
|
|
{
|
|
struct bpf_create_map_attr map_attr = {};
|
|
|
|
map_attr.name = name;
|
|
map_attr.map_type = map_type;
|
|
map_attr.map_flags = map_flags;
|
|
map_attr.key_size = key_size;
|
|
map_attr.value_size = value_size;
|
|
map_attr.max_entries = max_entries;
|
|
|
|
return bpf_create_map_xattr(&map_attr);
|
|
}
|
|
|
|
int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
|
|
int key_size, int inner_map_fd, int max_entries,
|
|
__u32 map_flags, int node)
|
|
{
|
|
__u32 name_len = name ? strlen(name) : 0;
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, '\0', sizeof(attr));
|
|
|
|
attr.map_type = map_type;
|
|
attr.key_size = key_size;
|
|
attr.value_size = 4;
|
|
attr.inner_map_fd = inner_map_fd;
|
|
attr.max_entries = max_entries;
|
|
attr.map_flags = map_flags;
|
|
memcpy(attr.map_name, name, min(name_len, BPF_OBJ_NAME_LEN - 1));
|
|
|
|
if (node >= 0) {
|
|
attr.map_flags |= BPF_F_NUMA_NODE;
|
|
attr.numa_node = node;
|
|
}
|
|
|
|
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
|
|
int key_size, int inner_map_fd, int max_entries,
|
|
__u32 map_flags)
|
|
{
|
|
return bpf_create_map_in_map_node(map_type, name, key_size,
|
|
inner_map_fd, max_entries, map_flags,
|
|
-1);
|
|
}
|
|
|
|
static void *
|
|
alloc_zero_tailing_info(const void *orecord, __u32 cnt,
|
|
__u32 actual_rec_size, __u32 expected_rec_size)
|
|
{
|
|
__u64 info_len = actual_rec_size * cnt;
|
|
void *info, *nrecord;
|
|
int i;
|
|
|
|
info = malloc(info_len);
|
|
if (!info)
|
|
return NULL;
|
|
|
|
/* zero out bytes kernel does not understand */
|
|
nrecord = info;
|
|
for (i = 0; i < cnt; i++) {
|
|
memcpy(nrecord, orecord, expected_rec_size);
|
|
memset(nrecord + expected_rec_size, 0,
|
|
actual_rec_size - expected_rec_size);
|
|
orecord += actual_rec_size;
|
|
nrecord += actual_rec_size;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
|
|
char *log_buf, size_t log_buf_sz)
|
|
{
|
|
void *finfo = NULL, *linfo = NULL;
|
|
union bpf_attr attr;
|
|
__u32 log_level;
|
|
__u32 name_len;
|
|
int fd;
|
|
|
|
if (!load_attr || !log_buf != !log_buf_sz)
|
|
return -EINVAL;
|
|
|
|
log_level = load_attr->log_level;
|
|
if (log_level > (4 | 2 | 1) || (log_level && !log_buf))
|
|
return -EINVAL;
|
|
|
|
name_len = load_attr->name ? strlen(load_attr->name) : 0;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.prog_type = load_attr->prog_type;
|
|
attr.expected_attach_type = load_attr->expected_attach_type;
|
|
attr.insn_cnt = (__u32)load_attr->insns_cnt;
|
|
attr.insns = ptr_to_u64(load_attr->insns);
|
|
attr.license = ptr_to_u64(load_attr->license);
|
|
|
|
attr.log_level = log_level;
|
|
if (log_level) {
|
|
attr.log_buf = ptr_to_u64(log_buf);
|
|
attr.log_size = log_buf_sz;
|
|
} else {
|
|
attr.log_buf = ptr_to_u64(NULL);
|
|
attr.log_size = 0;
|
|
}
|
|
|
|
attr.kern_version = load_attr->kern_version;
|
|
attr.prog_ifindex = load_attr->prog_ifindex;
|
|
attr.prog_btf_fd = load_attr->prog_btf_fd;
|
|
attr.func_info_rec_size = load_attr->func_info_rec_size;
|
|
attr.func_info_cnt = load_attr->func_info_cnt;
|
|
attr.func_info = ptr_to_u64(load_attr->func_info);
|
|
attr.line_info_rec_size = load_attr->line_info_rec_size;
|
|
attr.line_info_cnt = load_attr->line_info_cnt;
|
|
attr.line_info = ptr_to_u64(load_attr->line_info);
|
|
memcpy(attr.prog_name, load_attr->name,
|
|
min(name_len, BPF_OBJ_NAME_LEN - 1));
|
|
|
|
fd = sys_bpf_prog_load(&attr, sizeof(attr));
|
|
if (fd >= 0)
|
|
return fd;
|
|
|
|
/* After bpf_prog_load, the kernel may modify certain attributes
|
|
* to give user space a hint how to deal with loading failure.
|
|
* Check to see whether we can make some changes and load again.
|
|
*/
|
|
while (errno == E2BIG && (!finfo || !linfo)) {
|
|
if (!finfo && attr.func_info_cnt &&
|
|
attr.func_info_rec_size < load_attr->func_info_rec_size) {
|
|
/* try with corrected func info records */
|
|
finfo = alloc_zero_tailing_info(load_attr->func_info,
|
|
load_attr->func_info_cnt,
|
|
load_attr->func_info_rec_size,
|
|
attr.func_info_rec_size);
|
|
if (!finfo)
|
|
goto done;
|
|
|
|
attr.func_info = ptr_to_u64(finfo);
|
|
attr.func_info_rec_size = load_attr->func_info_rec_size;
|
|
} else if (!linfo && attr.line_info_cnt &&
|
|
attr.line_info_rec_size <
|
|
load_attr->line_info_rec_size) {
|
|
linfo = alloc_zero_tailing_info(load_attr->line_info,
|
|
load_attr->line_info_cnt,
|
|
load_attr->line_info_rec_size,
|
|
attr.line_info_rec_size);
|
|
if (!linfo)
|
|
goto done;
|
|
|
|
attr.line_info = ptr_to_u64(linfo);
|
|
attr.line_info_rec_size = load_attr->line_info_rec_size;
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
fd = sys_bpf_prog_load(&attr, sizeof(attr));
|
|
|
|
if (fd >= 0)
|
|
goto done;
|
|
}
|
|
|
|
if (log_level || !log_buf)
|
|
goto done;
|
|
|
|
/* Try again with log */
|
|
attr.log_buf = ptr_to_u64(log_buf);
|
|
attr.log_size = log_buf_sz;
|
|
attr.log_level = 1;
|
|
log_buf[0] = 0;
|
|
fd = sys_bpf_prog_load(&attr, sizeof(attr));
|
|
done:
|
|
free(finfo);
|
|
free(linfo);
|
|
return fd;
|
|
}
|
|
|
|
int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
|
size_t insns_cnt, const char *license,
|
|
__u32 kern_version, char *log_buf,
|
|
size_t log_buf_sz)
|
|
{
|
|
struct bpf_load_program_attr load_attr;
|
|
|
|
memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
|
|
load_attr.prog_type = type;
|
|
load_attr.expected_attach_type = 0;
|
|
load_attr.name = NULL;
|
|
load_attr.insns = insns;
|
|
load_attr.insns_cnt = insns_cnt;
|
|
load_attr.license = license;
|
|
load_attr.kern_version = kern_version;
|
|
|
|
return bpf_load_program_xattr(&load_attr, log_buf, log_buf_sz);
|
|
}
|
|
|
|
int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
|
|
size_t insns_cnt, __u32 prog_flags, const char *license,
|
|
__u32 kern_version, char *log_buf, size_t log_buf_sz,
|
|
int log_level)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.prog_type = type;
|
|
attr.insn_cnt = (__u32)insns_cnt;
|
|
attr.insns = ptr_to_u64(insns);
|
|
attr.license = ptr_to_u64(license);
|
|
attr.log_buf = ptr_to_u64(log_buf);
|
|
attr.log_size = log_buf_sz;
|
|
attr.log_level = log_level;
|
|
log_buf[0] = 0;
|
|
attr.kern_version = kern_version;
|
|
attr.prog_flags = prog_flags;
|
|
|
|
return sys_bpf_prog_load(&attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_map_update_elem(int fd, const void *key, const void *value,
|
|
__u64 flags)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.map_fd = fd;
|
|
attr.key = ptr_to_u64(key);
|
|
attr.value = ptr_to_u64(value);
|
|
attr.flags = flags;
|
|
|
|
return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_map_lookup_elem(int fd, const void *key, void *value)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.map_fd = fd;
|
|
attr.key = ptr_to_u64(key);
|
|
attr.value = ptr_to_u64(value);
|
|
|
|
return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_map_lookup_elem_flags(int fd, const void *key, void *value, __u64 flags)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.map_fd = fd;
|
|
attr.key = ptr_to_u64(key);
|
|
attr.value = ptr_to_u64(value);
|
|
attr.flags = flags;
|
|
|
|
return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_map_lookup_and_delete_elem(int fd, const void *key, void *value)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.map_fd = fd;
|
|
attr.key = ptr_to_u64(key);
|
|
attr.value = ptr_to_u64(value);
|
|
|
|
return sys_bpf(BPF_MAP_LOOKUP_AND_DELETE_ELEM, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_map_delete_elem(int fd, const void *key)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.map_fd = fd;
|
|
attr.key = ptr_to_u64(key);
|
|
|
|
return sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_map_get_next_key(int fd, const void *key, void *next_key)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.map_fd = fd;
|
|
attr.key = ptr_to_u64(key);
|
|
attr.next_key = ptr_to_u64(next_key);
|
|
|
|
return sys_bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_map_freeze(int fd)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.map_fd = fd;
|
|
|
|
return sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_obj_pin(int fd, const char *pathname)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.pathname = ptr_to_u64((void *)pathname);
|
|
attr.bpf_fd = fd;
|
|
|
|
return sys_bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_obj_get(const char *pathname)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.pathname = ptr_to_u64((void *)pathname);
|
|
|
|
return sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type,
|
|
unsigned int flags)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.target_fd = target_fd;
|
|
attr.attach_bpf_fd = prog_fd;
|
|
attr.attach_type = type;
|
|
attr.attach_flags = flags;
|
|
|
|
return sys_bpf(BPF_PROG_ATTACH, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_prog_detach(int target_fd, enum bpf_attach_type type)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.target_fd = target_fd;
|
|
attr.attach_type = type;
|
|
|
|
return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_prog_detach2(int prog_fd, int target_fd, enum bpf_attach_type type)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.target_fd = target_fd;
|
|
attr.attach_bpf_fd = prog_fd;
|
|
attr.attach_type = type;
|
|
|
|
return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
|
|
__u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt)
|
|
{
|
|
union bpf_attr attr;
|
|
int ret;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.query.target_fd = target_fd;
|
|
attr.query.attach_type = type;
|
|
attr.query.query_flags = query_flags;
|
|
attr.query.prog_cnt = *prog_cnt;
|
|
attr.query.prog_ids = ptr_to_u64(prog_ids);
|
|
|
|
ret = sys_bpf(BPF_PROG_QUERY, &attr, sizeof(attr));
|
|
if (attach_flags)
|
|
*attach_flags = attr.query.attach_flags;
|
|
*prog_cnt = attr.query.prog_cnt;
|
|
return ret;
|
|
}
|
|
|
|
int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
|
|
void *data_out, __u32 *size_out, __u32 *retval,
|
|
__u32 *duration)
|
|
{
|
|
union bpf_attr attr;
|
|
int ret;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.test.prog_fd = prog_fd;
|
|
attr.test.data_in = ptr_to_u64(data);
|
|
attr.test.data_out = ptr_to_u64(data_out);
|
|
attr.test.data_size_in = size;
|
|
attr.test.repeat = repeat;
|
|
|
|
ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
|
|
if (size_out)
|
|
*size_out = attr.test.data_size_out;
|
|
if (retval)
|
|
*retval = attr.test.retval;
|
|
if (duration)
|
|
*duration = attr.test.duration;
|
|
return ret;
|
|
}
|
|
|
|
int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr)
|
|
{
|
|
union bpf_attr attr;
|
|
int ret;
|
|
|
|
if (!test_attr->data_out && test_attr->data_size_out > 0)
|
|
return -EINVAL;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.test.prog_fd = test_attr->prog_fd;
|
|
attr.test.data_in = ptr_to_u64(test_attr->data_in);
|
|
attr.test.data_out = ptr_to_u64(test_attr->data_out);
|
|
attr.test.data_size_in = test_attr->data_size_in;
|
|
attr.test.data_size_out = test_attr->data_size_out;
|
|
attr.test.repeat = test_attr->repeat;
|
|
|
|
ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
|
|
test_attr->data_size_out = attr.test.data_size_out;
|
|
test_attr->retval = attr.test.retval;
|
|
test_attr->duration = attr.test.duration;
|
|
return ret;
|
|
}
|
|
|
|
int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
|
|
{
|
|
union bpf_attr attr;
|
|
int err;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.start_id = start_id;
|
|
|
|
err = sys_bpf(BPF_PROG_GET_NEXT_ID, &attr, sizeof(attr));
|
|
if (!err)
|
|
*next_id = attr.next_id;
|
|
|
|
return err;
|
|
}
|
|
|
|
int bpf_map_get_next_id(__u32 start_id, __u32 *next_id)
|
|
{
|
|
union bpf_attr attr;
|
|
int err;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.start_id = start_id;
|
|
|
|
err = sys_bpf(BPF_MAP_GET_NEXT_ID, &attr, sizeof(attr));
|
|
if (!err)
|
|
*next_id = attr.next_id;
|
|
|
|
return err;
|
|
}
|
|
|
|
int bpf_prog_get_fd_by_id(__u32 id)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.prog_id = id;
|
|
|
|
return sys_bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_map_get_fd_by_id(__u32 id)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.map_id = id;
|
|
|
|
return sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_btf_get_fd_by_id(__u32 id)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.btf_id = id;
|
|
|
|
return sys_bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len)
|
|
{
|
|
union bpf_attr attr;
|
|
int err;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.info.bpf_fd = prog_fd;
|
|
attr.info.info_len = *info_len;
|
|
attr.info.info = ptr_to_u64(info);
|
|
|
|
err = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
|
|
if (!err)
|
|
*info_len = attr.info.info_len;
|
|
|
|
return err;
|
|
}
|
|
|
|
int bpf_raw_tracepoint_open(const char *name, int prog_fd)
|
|
{
|
|
union bpf_attr attr;
|
|
|
|
memset(&attr, 0, sizeof(attr));
|
|
attr.raw_tracepoint.name = ptr_to_u64(name);
|
|
attr.raw_tracepoint.prog_fd = prog_fd;
|
|
|
|
return sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr));
|
|
}
|
|
|
|
int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size,
|
|
bool do_log)
|
|
{
|
|
union bpf_attr attr = {};
|
|
int fd;
|
|
|
|
attr.btf = ptr_to_u64(btf);
|
|
attr.btf_size = btf_size;
|
|
|
|
retry:
|
|
if (do_log && log_buf && log_buf_size) {
|
|
attr.btf_log_level = 1;
|
|
attr.btf_log_size = log_buf_size;
|
|
attr.btf_log_buf = ptr_to_u64(log_buf);
|
|
}
|
|
|
|
fd = sys_bpf(BPF_BTF_LOAD, &attr, sizeof(attr));
|
|
if (fd == -1 && !do_log && log_buf && log_buf_size) {
|
|
do_log = true;
|
|
goto retry;
|
|
}
|
|
|
|
return fd;
|
|
}
|
|
|
|
int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, __u32 *buf_len,
|
|
__u32 *prog_id, __u32 *fd_type, __u64 *probe_offset,
|
|
__u64 *probe_addr)
|
|
{
|
|
union bpf_attr attr = {};
|
|
int err;
|
|
|
|
attr.task_fd_query.pid = pid;
|
|
attr.task_fd_query.fd = fd;
|
|
attr.task_fd_query.flags = flags;
|
|
attr.task_fd_query.buf = ptr_to_u64(buf);
|
|
attr.task_fd_query.buf_len = *buf_len;
|
|
|
|
err = sys_bpf(BPF_TASK_FD_QUERY, &attr, sizeof(attr));
|
|
*buf_len = attr.task_fd_query.buf_len;
|
|
*prog_id = attr.task_fd_query.prog_id;
|
|
*fd_type = attr.task_fd_query.fd_type;
|
|
*probe_offset = attr.task_fd_query.probe_offset;
|
|
*probe_addr = attr.task_fd_query.probe_addr;
|
|
|
|
return err;
|
|
}
|