Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Daniel Borkmann says: ==================== pull-request: bpf-next 2020-11-14 1) Add BTF generation for kernel modules and extend BTF infra in kernel e.g. support for split BTF loading and validation, from Andrii Nakryiko. 2) Support for pointers beyond pkt_end to recognize LLVM generated patterns on inlined branch conditions, from Alexei Starovoitov. 3) Implements bpf_local_storage for task_struct for BPF LSM, from KP Singh. 4) Enable FENTRY/FEXIT/RAW_TP tracing program to use the bpf_sk_storage infra, from Martin KaFai Lau. 5) Add XDP bulk APIs that introduce a defer/flush mechanism to optimize the XDP_REDIRECT path, from Lorenzo Bianconi. 6) Fix a potential (although rather theoretical) deadlock of hashtab in NMI context, from Song Liu. 7) Fixes for cross and out-of-tree build of bpftool and runqslower allowing build for different target archs on same source tree, from Jean-Philippe Brucker. 8) Fix error path in htab_map_alloc() triggered from syzbot, from Eric Dumazet. 9) Move functionality from test_tcpbpf_user into the test_progs framework so it can run in BPF CI, from Alexander Duyck. 10) Lift hashtab key_size limit to be larger than MAX_BPF_STACK, from Florian Lehner. Note that for the fix from Song we have seen a sparse report on context imbalance which requires changes in sparse itself for proper annotation detection where this is currently being discussed on linux-sparse among developers [0]. Once we have more clarification/guidance after their fix, Song will follow-up. [0] https://lore.kernel.org/linux-sparse/CAHk-=wh4bx8A8dHnX612MsDO13st6uzAz1mJ1PaHHVevJx_ZCw@mail.gmail.com/T/ https://lore.kernel.org/linux-sparse/20201109221345.uklbp3lzgq6g42zb@ltop.local/T/ * git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next: (66 commits) net: mlx5: Add xdp tx return bulking support net: mvpp2: Add xdp tx return bulking support net: mvneta: Add xdp tx return bulking support net: page_pool: Add bulk support for ptr_ring net: xdp: Introduce bulking for xdp tx return path bpf: Expose bpf_d_path helper to sleepable LSM hooks bpf: Augment the set of sleepable LSM hooks bpf: selftest: Use bpf_sk_storage in FENTRY/FEXIT/RAW_TP bpf: Allow using bpf_sk_storage in FENTRY/FEXIT/RAW_TP bpf: Rename some functions in bpf_sk_storage bpf: Folding omem_charge() into sk_storage_charge() selftests/bpf: Add asm tests for pkt vs pkt_end comparison. selftests/bpf: Add skb_pkt_end test bpf: Support for pointers beyond pkt_end. tools/bpf: Always run the *-clean recipes tools/bpf: Add bootstrap/ to .gitignore bpf: Fix NULL dereference in bpf_task_storage tools/bpftool: Fix build slowdown tools/runqslower: Build bpftool using HOSTCC tools/runqslower: Enable out-of-tree build ... ==================== Link: https://lore.kernel.org/r/20201114020819.29584-1-daniel@iogearbox.net Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
*.d
|
||||
/bpftool-bootstrap
|
||||
/bootstrap/
|
||||
/bpftool
|
||||
bpftool*.8
|
||||
bpf-helpers.*
|
||||
|
||||
@@ -50,7 +50,8 @@ MAP COMMANDS
|
||||
| | **lru_percpu_hash** | **lpm_trie** | **array_of_maps** | **hash_of_maps**
|
||||
| | **devmap** | **devmap_hash** | **sockmap** | **cpumap** | **xskmap** | **sockhash**
|
||||
| | **cgroup_storage** | **reuseport_sockarray** | **percpu_cgroup_storage**
|
||||
| | **queue** | **stack** | **sk_storage** | **struct_ops** | **ringbuf** | **inode_storage** }
|
||||
| | **queue** | **stack** | **sk_storage** | **struct_ops** | **ringbuf** | **inode_storage**
|
||||
| **task_storage** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
+33
-11
@@ -19,22 +19,39 @@ BPF_DIR = $(srctree)/tools/lib/bpf/
|
||||
ifneq ($(OUTPUT),)
|
||||
LIBBPF_OUTPUT = $(OUTPUT)/libbpf/
|
||||
LIBBPF_PATH = $(LIBBPF_OUTPUT)
|
||||
BOOTSTRAP_OUTPUT = $(OUTPUT)/bootstrap/
|
||||
else
|
||||
LIBBPF_OUTPUT =
|
||||
LIBBPF_PATH = $(BPF_DIR)
|
||||
BOOTSTRAP_OUTPUT = $(CURDIR)/bootstrap/
|
||||
endif
|
||||
|
||||
LIBBPF = $(LIBBPF_PATH)libbpf.a
|
||||
LIBBPF_BOOTSTRAP_OUTPUT = $(BOOTSTRAP_OUTPUT)libbpf/
|
||||
LIBBPF_BOOTSTRAP = $(LIBBPF_BOOTSTRAP_OUTPUT)libbpf.a
|
||||
|
||||
BPFTOOL_VERSION ?= $(shell make -rR --no-print-directory -sC ../../.. kernelversion)
|
||||
ifeq ($(BPFTOOL_VERSION),)
|
||||
BPFTOOL_VERSION := $(shell make -rR --no-print-directory -sC ../../.. kernelversion)
|
||||
endif
|
||||
|
||||
$(LIBBPF): FORCE
|
||||
$(if $(LIBBPF_OUTPUT),@mkdir -p $(LIBBPF_OUTPUT))
|
||||
$(LIBBPF_OUTPUT) $(BOOTSTRAP_OUTPUT) $(LIBBPF_BOOTSTRAP_OUTPUT):
|
||||
$(QUIET_MKDIR)mkdir -p $@
|
||||
|
||||
$(LIBBPF): FORCE | $(LIBBPF_OUTPUT)
|
||||
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_OUTPUT) $(LIBBPF_OUTPUT)libbpf.a
|
||||
|
||||
$(LIBBPF)-clean:
|
||||
$(LIBBPF_BOOTSTRAP): FORCE | $(LIBBPF_BOOTSTRAP_OUTPUT)
|
||||
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_BOOTSTRAP_OUTPUT) \
|
||||
ARCH= CC=$(HOSTCC) LD=$(HOSTLD) $@
|
||||
|
||||
$(LIBBPF)-clean: FORCE | $(LIBBPF_OUTPUT)
|
||||
$(call QUIET_CLEAN, libbpf)
|
||||
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_OUTPUT) clean >/dev/null
|
||||
|
||||
$(LIBBPF_BOOTSTRAP)-clean: FORCE | $(LIBBPF_BOOTSTRAP_OUTPUT)
|
||||
$(call QUIET_CLEAN, libbpf-bootstrap)
|
||||
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(LIBBPF_BOOTSTRAP_OUTPUT) clean >/dev/null
|
||||
|
||||
prefix ?= /usr/local
|
||||
bash_compdir ?= /usr/share/bash-completion/completions
|
||||
|
||||
@@ -92,6 +109,7 @@ CFLAGS += -DCOMPAT_NEED_REALLOCARRAY
|
||||
endif
|
||||
|
||||
LIBS = $(LIBBPF) -lelf -lz
|
||||
LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz
|
||||
ifeq ($(feature-libcap), 1)
|
||||
CFLAGS += -DUSE_LIBCAP
|
||||
LIBS += -lcap
|
||||
@@ -118,9 +136,9 @@ CFLAGS += -DHAVE_LIBBFD_SUPPORT
|
||||
SRCS += $(BFD_SRCS)
|
||||
endif
|
||||
|
||||
BPFTOOL_BOOTSTRAP := $(if $(OUTPUT),$(OUTPUT)bpftool-bootstrap,./bpftool-bootstrap)
|
||||
BPFTOOL_BOOTSTRAP := $(BOOTSTRAP_OUTPUT)bpftool
|
||||
|
||||
BOOTSTRAP_OBJS = $(addprefix $(OUTPUT),main.o common.o json_writer.o gen.o btf.o)
|
||||
BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o)
|
||||
OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
|
||||
|
||||
VMLINUX_BTF_PATHS ?= $(if $(O),$(O)/vmlinux) \
|
||||
@@ -167,12 +185,16 @@ $(OUTPUT)disasm.o: $(srctree)/kernel/bpf/disasm.c
|
||||
|
||||
$(OUTPUT)feature.o: | zdep
|
||||
|
||||
$(BPFTOOL_BOOTSTRAP): $(BOOTSTRAP_OBJS) $(LIBBPF)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(BOOTSTRAP_OBJS) $(LIBS)
|
||||
$(BPFTOOL_BOOTSTRAP): $(BOOTSTRAP_OBJS) $(LIBBPF_BOOTSTRAP)
|
||||
$(QUIET_LINK)$(HOSTCC) $(CFLAGS) $(LDFLAGS) -o $@ $(BOOTSTRAP_OBJS) \
|
||||
$(LIBS_BOOTSTRAP)
|
||||
|
||||
$(OUTPUT)bpftool: $(OBJS) $(LIBBPF)
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
|
||||
|
||||
$(BOOTSTRAP_OUTPUT)%.o: %.c | $(BOOTSTRAP_OUTPUT)
|
||||
$(QUIET_CC)$(HOSTCC) $(CFLAGS) -c -MMD -o $@ $<
|
||||
|
||||
$(OUTPUT)%.o: %.c
|
||||
$(QUIET_CC)$(CC) $(CFLAGS) -c -MMD -o $@ $<
|
||||
|
||||
@@ -180,11 +202,11 @@ feature-detect-clean:
|
||||
$(call QUIET_CLEAN, feature-detect)
|
||||
$(Q)$(MAKE) -C $(srctree)/tools/build/feature/ clean >/dev/null
|
||||
|
||||
clean: $(LIBBPF)-clean feature-detect-clean
|
||||
clean: $(LIBBPF)-clean $(LIBBPF_BOOTSTRAP)-clean feature-detect-clean
|
||||
$(call QUIET_CLEAN, bpftool)
|
||||
$(Q)$(RM) -- $(OUTPUT)bpftool $(OUTPUT)*.o $(OUTPUT)*.d
|
||||
$(Q)$(RM) -- $(BPFTOOL_BOOTSTRAP) $(OUTPUT)*.skel.h $(OUTPUT)vmlinux.h
|
||||
$(Q)$(RM) -r -- $(OUTPUT)libbpf/
|
||||
$(Q)$(RM) -- $(OUTPUT)*.skel.h $(OUTPUT)vmlinux.h
|
||||
$(Q)$(RM) -r -- $(LIBBPF_OUTPUT) $(BOOTSTRAP_OUTPUT)
|
||||
$(call QUIET_CLEAN, core-gen)
|
||||
$(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.bpftool
|
||||
$(Q)$(RM) -r -- $(OUTPUT)feature/
|
||||
|
||||
@@ -705,7 +705,7 @@ _bpftool()
|
||||
hash_of_maps devmap devmap_hash sockmap cpumap \
|
||||
xskmap sockhash cgroup_storage reuseport_sockarray \
|
||||
percpu_cgroup_storage queue stack sk_storage \
|
||||
struct_ops inode_storage' -- \
|
||||
struct_ops inode_storage task_storage' -- \
|
||||
"$cur" ) )
|
||||
return 0
|
||||
;;
|
||||
|
||||
+33
-4
@@ -358,8 +358,12 @@ static int dump_btf_raw(const struct btf *btf,
|
||||
}
|
||||
} else {
|
||||
int cnt = btf__get_nr_types(btf);
|
||||
int start_id = 1;
|
||||
|
||||
for (i = 1; i <= cnt; i++) {
|
||||
if (base_btf)
|
||||
start_id = btf__get_nr_types(base_btf) + 1;
|
||||
|
||||
for (i = start_id; i <= cnt; i++) {
|
||||
t = btf__type_by_id(btf, i);
|
||||
dump_btf_type(btf, i, t);
|
||||
}
|
||||
@@ -438,7 +442,6 @@ static int do_dump(int argc, char **argv)
|
||||
return -1;
|
||||
}
|
||||
src = GET_ARG();
|
||||
|
||||
if (is_prefix(src, "map")) {
|
||||
struct bpf_map_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
@@ -499,7 +502,7 @@ static int do_dump(int argc, char **argv)
|
||||
}
|
||||
NEXT_ARG();
|
||||
} else if (is_prefix(src, "file")) {
|
||||
btf = btf__parse(*argv, NULL);
|
||||
btf = btf__parse_split(*argv, base_btf);
|
||||
if (IS_ERR(btf)) {
|
||||
err = -PTR_ERR(btf);
|
||||
btf = NULL;
|
||||
@@ -739,9 +742,14 @@ show_btf_plain(struct bpf_btf_info *info, int fd,
|
||||
struct btf_attach_table *btf_map_table)
|
||||
{
|
||||
struct btf_attach_point *obj;
|
||||
const char *name = u64_to_ptr(info->name);
|
||||
int n;
|
||||
|
||||
printf("%u: ", info->id);
|
||||
if (info->kernel_btf)
|
||||
printf("name [%s] ", name);
|
||||
else if (name && name[0])
|
||||
printf("name %s ", name);
|
||||
printf("size %uB", info->btf_size);
|
||||
|
||||
n = 0;
|
||||
@@ -768,6 +776,7 @@ show_btf_json(struct bpf_btf_info *info, int fd,
|
||||
struct btf_attach_table *btf_map_table)
|
||||
{
|
||||
struct btf_attach_point *obj;
|
||||
const char *name = u64_to_ptr(info->name);
|
||||
|
||||
jsonw_start_object(json_wtr); /* btf object */
|
||||
jsonw_uint_field(json_wtr, "id", info->id);
|
||||
@@ -793,6 +802,11 @@ show_btf_json(struct bpf_btf_info *info, int fd,
|
||||
|
||||
emit_obj_refs_json(&refs_table, info->id, json_wtr); /* pids */
|
||||
|
||||
jsonw_bool_field(json_wtr, "kernel", info->kernel_btf);
|
||||
|
||||
if (name && name[0])
|
||||
jsonw_string_field(json_wtr, "name", name);
|
||||
|
||||
jsonw_end_object(json_wtr); /* btf object */
|
||||
}
|
||||
|
||||
@@ -800,15 +814,30 @@ static int
|
||||
show_btf(int fd, struct btf_attach_table *btf_prog_table,
|
||||
struct btf_attach_table *btf_map_table)
|
||||
{
|
||||
struct bpf_btf_info info = {};
|
||||
struct bpf_btf_info info;
|
||||
__u32 len = sizeof(info);
|
||||
char name[64];
|
||||
int err;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
p_err("can't get BTF object info: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
/* if kernel support emitting BTF object name, pass name pointer */
|
||||
if (info.name_len) {
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.name_len = sizeof(name);
|
||||
info.name = ptr_to_u64(name);
|
||||
len = sizeof(info);
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
p_err("can't get BTF object info: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
show_btf_json(&info, fd, btf_prog_table, btf_map_table);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <bpf/btf.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
@@ -28,6 +29,7 @@ bool show_pinned;
|
||||
bool block_mount;
|
||||
bool verifier_logs;
|
||||
bool relaxed_maps;
|
||||
struct btf *base_btf;
|
||||
struct pinned_obj_table prog_table;
|
||||
struct pinned_obj_table map_table;
|
||||
struct pinned_obj_table link_table;
|
||||
@@ -391,6 +393,7 @@ int main(int argc, char **argv)
|
||||
{ "mapcompat", no_argument, NULL, 'm' },
|
||||
{ "nomount", no_argument, NULL, 'n' },
|
||||
{ "debug", no_argument, NULL, 'd' },
|
||||
{ "base-btf", required_argument, NULL, 'B' },
|
||||
{ 0 }
|
||||
};
|
||||
int opt, ret;
|
||||
@@ -407,7 +410,7 @@ int main(int argc, char **argv)
|
||||
hash_init(link_table.table);
|
||||
|
||||
opterr = 0;
|
||||
while ((opt = getopt_long(argc, argv, "Vhpjfmnd",
|
||||
while ((opt = getopt_long(argc, argv, "VhpjfmndB:",
|
||||
options, NULL)) >= 0) {
|
||||
switch (opt) {
|
||||
case 'V':
|
||||
@@ -441,6 +444,15 @@ int main(int argc, char **argv)
|
||||
libbpf_set_print(print_all_levels);
|
||||
verifier_logs = true;
|
||||
break;
|
||||
case 'B':
|
||||
base_btf = btf__parse(optarg, NULL);
|
||||
if (libbpf_get_error(base_btf)) {
|
||||
p_err("failed to parse base BTF at '%s': %ld\n",
|
||||
optarg, libbpf_get_error(base_btf));
|
||||
base_btf = NULL;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
p_err("unrecognized option '%s'", argv[optind - 1]);
|
||||
if (json_output)
|
||||
@@ -465,6 +477,7 @@ int main(int argc, char **argv)
|
||||
delete_pinned_obj_table(&map_table);
|
||||
delete_pinned_obj_table(&link_table);
|
||||
}
|
||||
btf__free(base_btf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ extern bool show_pids;
|
||||
extern bool block_mount;
|
||||
extern bool verifier_logs;
|
||||
extern bool relaxed_maps;
|
||||
extern struct btf *base_btf;
|
||||
extern struct pinned_obj_table prog_table;
|
||||
extern struct pinned_obj_table map_table;
|
||||
extern struct pinned_obj_table link_table;
|
||||
|
||||
@@ -51,6 +51,7 @@ const char * const map_type_name[] = {
|
||||
[BPF_MAP_TYPE_STRUCT_OPS] = "struct_ops",
|
||||
[BPF_MAP_TYPE_RINGBUF] = "ringbuf",
|
||||
[BPF_MAP_TYPE_INODE_STORAGE] = "inode_storage",
|
||||
[BPF_MAP_TYPE_TASK_STORAGE] = "task_storage",
|
||||
};
|
||||
|
||||
const size_t map_type_name_size = ARRAY_SIZE(map_type_name);
|
||||
@@ -1464,7 +1465,8 @@ static int do_help(int argc, char **argv)
|
||||
" lru_percpu_hash | lpm_trie | array_of_maps | hash_of_maps |\n"
|
||||
" devmap | devmap_hash | sockmap | cpumap | xskmap | sockhash |\n"
|
||||
" cgroup_storage | reuseport_sockarray | percpu_cgroup_storage |\n"
|
||||
" queue | stack | sk_storage | struct_ops | ringbuf | inode_storage }\n"
|
||||
" queue | stack | sk_storage | struct_ops | ringbuf | inode_storage |\n"
|
||||
" task_storage }\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, argv[-2]);
|
||||
|
||||
@@ -18,15 +18,6 @@ else
|
||||
endif
|
||||
|
||||
# always use the host compiler
|
||||
ifneq ($(LLVM),)
|
||||
HOSTAR ?= llvm-ar
|
||||
HOSTCC ?= clang
|
||||
HOSTLD ?= ld.lld
|
||||
else
|
||||
HOSTAR ?= ar
|
||||
HOSTCC ?= gcc
|
||||
HOSTLD ?= ld
|
||||
endif
|
||||
AR = $(HOSTAR)
|
||||
CC = $(HOSTCC)
|
||||
LD = $(HOSTLD)
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
||||
OUTPUT := .output
|
||||
include ../../scripts/Makefile.include
|
||||
|
||||
OUTPUT ?= $(abspath .output)/
|
||||
|
||||
CLANG ?= clang
|
||||
LLC ?= llc
|
||||
LLVM_STRIP ?= llvm-strip
|
||||
DEFAULT_BPFTOOL := $(OUTPUT)/sbin/bpftool
|
||||
BPFTOOL_OUTPUT := $(OUTPUT)bpftool/
|
||||
DEFAULT_BPFTOOL := $(BPFTOOL_OUTPUT)bpftool
|
||||
BPFTOOL ?= $(DEFAULT_BPFTOOL)
|
||||
LIBBPF_SRC := $(abspath ../../lib/bpf)
|
||||
BPFOBJ := $(OUTPUT)/libbpf.a
|
||||
BPF_INCLUDE := $(OUTPUT)
|
||||
BPFOBJ_OUTPUT := $(OUTPUT)libbpf/
|
||||
BPFOBJ := $(BPFOBJ_OUTPUT)libbpf.a
|
||||
BPF_INCLUDE := $(BPFOBJ_OUTPUT)
|
||||
INCLUDES := -I$(OUTPUT) -I$(BPF_INCLUDE) -I$(abspath ../../lib) \
|
||||
-I$(abspath ../../include/uapi)
|
||||
CFLAGS := -g -Wall
|
||||
@@ -18,13 +23,10 @@ VMLINUX_BTF_PATHS := /sys/kernel/btf/vmlinux /boot/vmlinux-$(KERNEL_REL)
|
||||
VMLINUX_BTF_PATH := $(or $(VMLINUX_BTF),$(firstword \
|
||||
$(wildcard $(VMLINUX_BTF_PATHS))))
|
||||
|
||||
abs_out := $(abspath $(OUTPUT))
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
msg =
|
||||
else
|
||||
Q = @
|
||||
msg = @printf ' %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))";
|
||||
MAKEFLAGS += --no-print-directory
|
||||
submake_extras := feature_display=0
|
||||
endif
|
||||
@@ -37,12 +39,15 @@ all: runqslower
|
||||
runqslower: $(OUTPUT)/runqslower
|
||||
|
||||
clean:
|
||||
$(call msg,CLEAN)
|
||||
$(Q)rm -rf $(OUTPUT) runqslower
|
||||
$(call QUIET_CLEAN, runqslower)
|
||||
$(Q)$(RM) -r $(BPFOBJ_OUTPUT) $(BPFTOOL_OUTPUT)
|
||||
$(Q)$(RM) $(OUTPUT)*.o $(OUTPUT)*.d
|
||||
$(Q)$(RM) $(OUTPUT)*.skel.h $(OUTPUT)vmlinux.h
|
||||
$(Q)$(RM) $(OUTPUT)runqslower
|
||||
$(Q)$(RM) -r .output
|
||||
|
||||
$(OUTPUT)/runqslower: $(OUTPUT)/runqslower.o $(BPFOBJ)
|
||||
$(call msg,BINARY,$@)
|
||||
$(Q)$(CC) $(CFLAGS) $^ -lelf -lz -o $@
|
||||
$(QUIET_LINK)$(CC) $(CFLAGS) $^ -lelf -lz -o $@
|
||||
|
||||
$(OUTPUT)/runqslower.o: runqslower.h $(OUTPUT)/runqslower.skel.h \
|
||||
$(OUTPUT)/runqslower.bpf.o
|
||||
@@ -50,36 +55,30 @@ $(OUTPUT)/runqslower.o: runqslower.h $(OUTPUT)/runqslower.skel.h \
|
||||
$(OUTPUT)/runqslower.bpf.o: $(OUTPUT)/vmlinux.h runqslower.h
|
||||
|
||||
$(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(BPFTOOL)
|
||||
$(call msg,GEN-SKEL,$@)
|
||||
$(Q)$(BPFTOOL) gen skeleton $< > $@
|
||||
$(QUIET_GEN)$(BPFTOOL) gen skeleton $< > $@
|
||||
|
||||
$(OUTPUT)/%.bpf.o: %.bpf.c $(BPFOBJ) | $(OUTPUT)
|
||||
$(call msg,BPF,$@)
|
||||
$(Q)$(CLANG) -g -O2 -target bpf $(INCLUDES) \
|
||||
$(QUIET_GEN)$(CLANG) -g -O2 -target bpf $(INCLUDES) \
|
||||
-c $(filter %.c,$^) -o $@ && \
|
||||
$(LLVM_STRIP) -g $@
|
||||
|
||||
$(OUTPUT)/%.o: %.c | $(OUTPUT)
|
||||
$(call msg,CC,$@)
|
||||
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@
|
||||
$(QUIET_CC)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@
|
||||
|
||||
$(OUTPUT):
|
||||
$(call msg,MKDIR,$@)
|
||||
$(Q)mkdir -p $(OUTPUT)
|
||||
$(OUTPUT) $(BPFOBJ_OUTPUT) $(BPFTOOL_OUTPUT):
|
||||
$(QUIET_MKDIR)mkdir -p $@
|
||||
|
||||
$(OUTPUT)/vmlinux.h: $(VMLINUX_BTF_PATH) | $(OUTPUT) $(BPFTOOL)
|
||||
$(call msg,GEN,$@)
|
||||
$(Q)if [ ! -e "$(VMLINUX_BTF_PATH)" ] ; then \
|
||||
echo "Couldn't find kernel BTF; set VMLINUX_BTF to" \
|
||||
"specify its location." >&2; \
|
||||
exit 1;\
|
||||
fi
|
||||
$(Q)$(BPFTOOL) btf dump file $(VMLINUX_BTF_PATH) format c > $@
|
||||
$(QUIET_GEN)$(BPFTOOL) btf dump file $(VMLINUX_BTF_PATH) format c > $@
|
||||
|
||||
$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)
|
||||
$(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) \
|
||||
OUTPUT=$(abspath $(dir $@))/ $(abspath $@)
|
||||
$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(BPFOBJ_OUTPUT)
|
||||
$(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) OUTPUT=$(BPFOBJ_OUTPUT) $@
|
||||
|
||||
$(DEFAULT_BPFTOOL):
|
||||
$(Q)$(MAKE) $(submake_extras) -C ../bpftool \
|
||||
prefix= OUTPUT=$(abs_out)/ DESTDIR=$(abs_out) install
|
||||
$(DEFAULT_BPFTOOL): | $(BPFTOOL_OUTPUT)
|
||||
$(Q)$(MAKE) $(submake_extras) -C ../bpftool OUTPUT=$(BPFTOOL_OUTPUT) \
|
||||
CC=$(HOSTCC) LD=$(HOSTLD)
|
||||
|
||||
@@ -15,10 +15,6 @@ endef
|
||||
$(call allow-override,CC,$(CROSS_COMPILE)gcc)
|
||||
$(call allow-override,LD,$(CROSS_COMPILE)ld)
|
||||
|
||||
HOSTCC ?= gcc
|
||||
HOSTLD ?= ld
|
||||
HOSTAR ?= ar
|
||||
|
||||
export HOSTCC HOSTLD HOSTAR
|
||||
|
||||
ifeq ($(V),1)
|
||||
|
||||
@@ -157,6 +157,7 @@ enum bpf_map_type {
|
||||
BPF_MAP_TYPE_STRUCT_OPS,
|
||||
BPF_MAP_TYPE_RINGBUF,
|
||||
BPF_MAP_TYPE_INODE_STORAGE,
|
||||
BPF_MAP_TYPE_TASK_STORAGE,
|
||||
};
|
||||
|
||||
/* Note that tracing related programs such as
|
||||
@@ -3742,6 +3743,50 @@ union bpf_attr {
|
||||
* Return
|
||||
* The helper returns **TC_ACT_REDIRECT** on success or
|
||||
* **TC_ACT_SHOT** on error.
|
||||
*
|
||||
* void *bpf_task_storage_get(struct bpf_map *map, struct task_struct *task, void *value, u64 flags)
|
||||
* Description
|
||||
* Get a bpf_local_storage from the *task*.
|
||||
*
|
||||
* Logically, it could be thought of as getting the value from
|
||||
* a *map* with *task* as the **key**. From this
|
||||
* perspective, the usage is not much different from
|
||||
* **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this
|
||||
* helper enforces the key must be an task_struct and the map must also
|
||||
* be a **BPF_MAP_TYPE_TASK_STORAGE**.
|
||||
*
|
||||
* Underneath, the value is stored locally at *task* instead of
|
||||
* the *map*. The *map* is used as the bpf-local-storage
|
||||
* "type". The bpf-local-storage "type" (i.e. the *map*) is
|
||||
* searched against all bpf_local_storage residing at *task*.
|
||||
*
|
||||
* An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be
|
||||
* used such that a new bpf_local_storage will be
|
||||
* created if one does not exist. *value* can be used
|
||||
* together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify
|
||||
* the initial value of a bpf_local_storage. If *value* is
|
||||
* **NULL**, the new bpf_local_storage will be zero initialized.
|
||||
* Return
|
||||
* A bpf_local_storage pointer is returned on success.
|
||||
*
|
||||
* **NULL** if not found or there was an error in adding
|
||||
* a new bpf_local_storage.
|
||||
*
|
||||
* long bpf_task_storage_delete(struct bpf_map *map, struct task_struct *task)
|
||||
* Description
|
||||
* Delete a bpf_local_storage from a *task*.
|
||||
* Return
|
||||
* 0 on success.
|
||||
*
|
||||
* **-ENOENT** if the bpf_local_storage cannot be found.
|
||||
*
|
||||
* struct task_struct *bpf_get_current_task_btf(void)
|
||||
* Description
|
||||
* Return a BTF pointer to the "current" task.
|
||||
* This pointer can also be used in helpers that accept an
|
||||
* *ARG_PTR_TO_BTF_ID* of type *task_struct*.
|
||||
* Return
|
||||
* Pointer to the current task.
|
||||
*/
|
||||
#define __BPF_FUNC_MAPPER(FN) \
|
||||
FN(unspec), \
|
||||
@@ -3900,6 +3945,9 @@ union bpf_attr {
|
||||
FN(bpf_per_cpu_ptr), \
|
||||
FN(bpf_this_cpu_ptr), \
|
||||
FN(redirect_peer), \
|
||||
FN(task_storage_get), \
|
||||
FN(task_storage_delete), \
|
||||
FN(get_current_task_btf), \
|
||||
/* */
|
||||
|
||||
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
|
||||
@@ -4418,6 +4466,9 @@ struct bpf_btf_info {
|
||||
__aligned_u64 btf;
|
||||
__u32 btf_size;
|
||||
__u32 id;
|
||||
__aligned_u64 name;
|
||||
__u32 name_len;
|
||||
__u32 kernel_btf;
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
struct bpf_link_info {
|
||||
|
||||
+473
-336
File diff suppressed because it is too large
Load Diff
@@ -31,11 +31,19 @@ enum btf_endianness {
|
||||
};
|
||||
|
||||
LIBBPF_API void btf__free(struct btf *btf);
|
||||
|
||||
LIBBPF_API struct btf *btf__new(const void *data, __u32 size);
|
||||
LIBBPF_API struct btf *btf__new_split(const void *data, __u32 size, struct btf *base_btf);
|
||||
LIBBPF_API struct btf *btf__new_empty(void);
|
||||
LIBBPF_API struct btf *btf__new_empty_split(struct btf *base_btf);
|
||||
|
||||
LIBBPF_API struct btf *btf__parse(const char *path, struct btf_ext **btf_ext);
|
||||
LIBBPF_API struct btf *btf__parse_split(const char *path, struct btf *base_btf);
|
||||
LIBBPF_API struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext);
|
||||
LIBBPF_API struct btf *btf__parse_elf_split(const char *path, struct btf *base_btf);
|
||||
LIBBPF_API struct btf *btf__parse_raw(const char *path);
|
||||
LIBBPF_API struct btf *btf__parse_raw_split(const char *path, struct btf *base_btf);
|
||||
|
||||
LIBBPF_API int btf__finalize_data(struct bpf_object *obj, struct btf *btf);
|
||||
LIBBPF_API int btf__load(struct btf *btf);
|
||||
LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
|
||||
|
||||
@@ -337,3 +337,12 @@ LIBBPF_0.2.0 {
|
||||
perf_buffer__consume_buffer;
|
||||
xsk_socket__create_shared;
|
||||
} LIBBPF_0.1.0;
|
||||
|
||||
LIBBPF_0.3.0 {
|
||||
global:
|
||||
btf__parse_elf_split;
|
||||
btf__parse_raw_split;
|
||||
btf__parse_split;
|
||||
btf__new_empty_split;
|
||||
btf__new_split;
|
||||
} LIBBPF_0.2.0;
|
||||
|
||||
@@ -230,6 +230,7 @@ bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
|
||||
break;
|
||||
case BPF_MAP_TYPE_SK_STORAGE:
|
||||
case BPF_MAP_TYPE_INODE_STORAGE:
|
||||
case BPF_MAP_TYPE_TASK_STORAGE:
|
||||
btf_key_type_id = 1;
|
||||
btf_value_type_id = 3;
|
||||
value_size = 8;
|
||||
|
||||
@@ -3,15 +3,6 @@ include ../scripts/Makefile.include
|
||||
include ../scripts/Makefile.arch
|
||||
|
||||
# always use the host compiler
|
||||
ifneq ($(LLVM),)
|
||||
HOSTAR ?= llvm-ar
|
||||
HOSTCC ?= clang
|
||||
HOSTLD ?= ld.lld
|
||||
else
|
||||
HOSTAR ?= ar
|
||||
HOSTCC ?= gcc
|
||||
HOSTLD ?= ld
|
||||
endif
|
||||
AR = $(HOSTAR)
|
||||
CC = $(HOSTCC)
|
||||
LD = $(HOSTLD)
|
||||
|
||||
@@ -175,10 +175,6 @@ endef
|
||||
|
||||
LD += $(EXTRA_LDFLAGS)
|
||||
|
||||
HOSTCC ?= gcc
|
||||
HOSTLD ?= ld
|
||||
HOSTAR ?= ar
|
||||
|
||||
PKG_CONFIG = $(CROSS_COMPILE)pkg-config
|
||||
LLVM_CONFIG ?= llvm-config
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ INSTALL_SCRIPT = ${INSTALL_PROGRAM}
|
||||
CROSS = #/usr/i386-linux-uclibc/usr/bin/i386-uclibc-
|
||||
CROSS_COMPILE ?= $(CROSS)
|
||||
LD = $(CC)
|
||||
HOSTCC = gcc
|
||||
|
||||
# check if compiler option is supported
|
||||
cc-supports = ${shell if $(CC) ${1} -S -o /dev/null -x c /dev/null > /dev/null 2>&1; then echo "$(1)"; fi;}
|
||||
|
||||
@@ -59,6 +59,16 @@ $(call allow-override,LD,$(CROSS_COMPILE)ld)
|
||||
$(call allow-override,CXX,$(CROSS_COMPILE)g++)
|
||||
$(call allow-override,STRIP,$(CROSS_COMPILE)strip)
|
||||
|
||||
ifneq ($(LLVM),)
|
||||
HOSTAR ?= llvm-ar
|
||||
HOSTCC ?= clang
|
||||
HOSTLD ?= ld.lld
|
||||
else
|
||||
HOSTAR ?= ar
|
||||
HOSTCC ?= gcc
|
||||
HOSTLD ?= ld
|
||||
endif
|
||||
|
||||
ifeq ($(CC_NO_CLANG), 1)
|
||||
EXTRA_WARNINGS += -Wstrict-aliasing=3
|
||||
endif
|
||||
|
||||
@@ -8,7 +8,6 @@ FEATURE-DUMP.libbpf
|
||||
fixdep
|
||||
test_dev_cgroup
|
||||
/test_progs*
|
||||
test_tcpbpf_user
|
||||
test_verifier_log
|
||||
feature
|
||||
test_sock
|
||||
|
||||
@@ -32,7 +32,7 @@ LDLIBS += -lcap -lelf -lz -lrt -lpthread
|
||||
|
||||
# Order correspond to 'make run_tests' order
|
||||
TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
|
||||
test_verifier_log test_dev_cgroup test_tcpbpf_user \
|
||||
test_verifier_log test_dev_cgroup \
|
||||
test_sock test_sockmap get_cgroup_id_user test_socket_cookie \
|
||||
test_cgroup_storage \
|
||||
test_netcnt test_tcpnotify_user test_sysctl \
|
||||
@@ -163,7 +163,6 @@ $(OUTPUT)/test_sock: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sock_addr: cgroup_helpers.c
|
||||
$(OUTPUT)/test_socket_cookie: cgroup_helpers.c
|
||||
$(OUTPUT)/test_sockmap: cgroup_helpers.c
|
||||
$(OUTPUT)/test_tcpbpf_user: cgroup_helpers.c
|
||||
$(OUTPUT)/test_tcpnotify_user: cgroup_helpers.c trace_helpers.c
|
||||
$(OUTPUT)/get_cgroup_id_user: cgroup_helpers.c
|
||||
$(OUTPUT)/test_cgroup_storage: cgroup_helpers.c
|
||||
@@ -387,7 +386,7 @@ TRUNNER_TESTS_DIR := prog_tests
|
||||
TRUNNER_BPF_PROGS_DIR := progs
|
||||
TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c \
|
||||
network_helpers.c testing_helpers.c \
|
||||
flow_dissector_load.h
|
||||
btf_helpers.c flow_dissector_load.h
|
||||
TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read \
|
||||
$(wildcard progs/btf_dump_test_case_*.c)
|
||||
TRUNNER_BPF_BUILD_RULE := CLANG_BPF_BUILD_RULE
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <bpf/btf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include "test_progs.h"
|
||||
|
||||
static const char * const btf_kind_str_mapping[] = {
|
||||
[BTF_KIND_UNKN] = "UNKNOWN",
|
||||
[BTF_KIND_INT] = "INT",
|
||||
[BTF_KIND_PTR] = "PTR",
|
||||
[BTF_KIND_ARRAY] = "ARRAY",
|
||||
[BTF_KIND_STRUCT] = "STRUCT",
|
||||
[BTF_KIND_UNION] = "UNION",
|
||||
[BTF_KIND_ENUM] = "ENUM",
|
||||
[BTF_KIND_FWD] = "FWD",
|
||||
[BTF_KIND_TYPEDEF] = "TYPEDEF",
|
||||
[BTF_KIND_VOLATILE] = "VOLATILE",
|
||||
[BTF_KIND_CONST] = "CONST",
|
||||
[BTF_KIND_RESTRICT] = "RESTRICT",
|
||||
[BTF_KIND_FUNC] = "FUNC",
|
||||
[BTF_KIND_FUNC_PROTO] = "FUNC_PROTO",
|
||||
[BTF_KIND_VAR] = "VAR",
|
||||
[BTF_KIND_DATASEC] = "DATASEC",
|
||||
};
|
||||
|
||||
static const char *btf_kind_str(__u16 kind)
|
||||
{
|
||||
if (kind > BTF_KIND_DATASEC)
|
||||
return "UNKNOWN";
|
||||
return btf_kind_str_mapping[kind];
|
||||
}
|
||||
|
||||
static const char *btf_int_enc_str(__u8 encoding)
|
||||
{
|
||||
switch (encoding) {
|
||||
case 0:
|
||||
return "(none)";
|
||||
case BTF_INT_SIGNED:
|
||||
return "SIGNED";
|
||||
case BTF_INT_CHAR:
|
||||
return "CHAR";
|
||||
case BTF_INT_BOOL:
|
||||
return "BOOL";
|
||||
default:
|
||||
return "UNKN";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *btf_var_linkage_str(__u32 linkage)
|
||||
{
|
||||
switch (linkage) {
|
||||
case BTF_VAR_STATIC:
|
||||
return "static";
|
||||
case BTF_VAR_GLOBAL_ALLOCATED:
|
||||
return "global-alloc";
|
||||
default:
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *btf_func_linkage_str(const struct btf_type *t)
|
||||
{
|
||||
switch (btf_vlen(t)) {
|
||||
case BTF_FUNC_STATIC:
|
||||
return "static";
|
||||
case BTF_FUNC_GLOBAL:
|
||||
return "global";
|
||||
case BTF_FUNC_EXTERN:
|
||||
return "extern";
|
||||
default:
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
|
||||
static const char *btf_str(const struct btf *btf, __u32 off)
|
||||
{
|
||||
if (!off)
|
||||
return "(anon)";
|
||||
return btf__str_by_offset(btf, off) ?: "(invalid)";
|
||||
}
|
||||
|
||||
int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id)
|
||||
{
|
||||
const struct btf_type *t;
|
||||
int kind, i;
|
||||
__u32 vlen;
|
||||
|
||||
t = btf__type_by_id(btf, id);
|
||||
if (!t)
|
||||
return -EINVAL;
|
||||
|
||||
vlen = btf_vlen(t);
|
||||
kind = btf_kind(t);
|
||||
|
||||
fprintf(out, "[%u] %s '%s'", id, btf_kind_str(kind), btf_str(btf, t->name_off));
|
||||
|
||||
switch (kind) {
|
||||
case BTF_KIND_INT:
|
||||
fprintf(out, " size=%u bits_offset=%u nr_bits=%u encoding=%s",
|
||||
t->size, btf_int_offset(t), btf_int_bits(t),
|
||||
btf_int_enc_str(btf_int_encoding(t)));
|
||||
break;
|
||||
case BTF_KIND_PTR:
|
||||
case BTF_KIND_CONST:
|
||||
case BTF_KIND_VOLATILE:
|
||||
case BTF_KIND_RESTRICT:
|
||||
case BTF_KIND_TYPEDEF:
|
||||
fprintf(out, " type_id=%u", t->type);
|
||||
break;
|
||||
case BTF_KIND_ARRAY: {
|
||||
const struct btf_array *arr = btf_array(t);
|
||||
|
||||
fprintf(out, " type_id=%u index_type_id=%u nr_elems=%u",
|
||||
arr->type, arr->index_type, arr->nelems);
|
||||
break;
|
||||
}
|
||||
case BTF_KIND_STRUCT:
|
||||
case BTF_KIND_UNION: {
|
||||
const struct btf_member *m = btf_members(t);
|
||||
|
||||
fprintf(out, " size=%u vlen=%u", t->size, vlen);
|
||||
for (i = 0; i < vlen; i++, m++) {
|
||||
__u32 bit_off, bit_sz;
|
||||
|
||||
bit_off = btf_member_bit_offset(t, i);
|
||||
bit_sz = btf_member_bitfield_size(t, i);
|
||||
fprintf(out, "\n\t'%s' type_id=%u bits_offset=%u",
|
||||
btf_str(btf, m->name_off), m->type, bit_off);
|
||||
if (bit_sz)
|
||||
fprintf(out, " bitfield_size=%u", bit_sz);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BTF_KIND_ENUM: {
|
||||
const struct btf_enum *v = btf_enum(t);
|
||||
|
||||
fprintf(out, " size=%u vlen=%u", t->size, vlen);
|
||||
for (i = 0; i < vlen; i++, v++) {
|
||||
fprintf(out, "\n\t'%s' val=%u",
|
||||
btf_str(btf, v->name_off), v->val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BTF_KIND_FWD:
|
||||
fprintf(out, " fwd_kind=%s", btf_kflag(t) ? "union" : "struct");
|
||||
break;
|
||||
case BTF_KIND_FUNC:
|
||||
fprintf(out, " type_id=%u linkage=%s", t->type, btf_func_linkage_str(t));
|
||||
break;
|
||||
case BTF_KIND_FUNC_PROTO: {
|
||||
const struct btf_param *p = btf_params(t);
|
||||
|
||||
fprintf(out, " ret_type_id=%u vlen=%u", t->type, vlen);
|
||||
for (i = 0; i < vlen; i++, p++) {
|
||||
fprintf(out, "\n\t'%s' type_id=%u",
|
||||
btf_str(btf, p->name_off), p->type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BTF_KIND_VAR:
|
||||
fprintf(out, " type_id=%u, linkage=%s",
|
||||
t->type, btf_var_linkage_str(btf_var(t)->linkage));
|
||||
break;
|
||||
case BTF_KIND_DATASEC: {
|
||||
const struct btf_var_secinfo *v = btf_var_secinfos(t);
|
||||
|
||||
fprintf(out, " size=%u vlen=%u", t->size, vlen);
|
||||
for (i = 0; i < vlen; i++, v++) {
|
||||
fprintf(out, "\n\ttype_id=%u offset=%u size=%u",
|
||||
v->type, v->offset, v->size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Print raw BTF type dump into a local buffer and return string pointer back.
|
||||
* Buffer *will* be overwritten by subsequent btf_type_raw_dump() calls
|
||||
*/
|
||||
const char *btf_type_raw_dump(const struct btf *btf, int type_id)
|
||||
{
|
||||
static char buf[16 * 1024];
|
||||
FILE *buf_file;
|
||||
|
||||
buf_file = fmemopen(buf, sizeof(buf) - 1, "w");
|
||||
if (!buf_file) {
|
||||
fprintf(stderr, "Failed to open memstream: %d\n", errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fprintf_btf_type_raw(buf_file, btf, type_id);
|
||||
fflush(buf_file);
|
||||
fclose(buf_file);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
int btf_validate_raw(struct btf *btf, int nr_types, const char *exp_types[])
|
||||
{
|
||||
int i;
|
||||
bool ok = true;
|
||||
|
||||
ASSERT_EQ(btf__get_nr_types(btf), nr_types, "btf_nr_types");
|
||||
|
||||
for (i = 1; i <= nr_types; i++) {
|
||||
if (!ASSERT_STREQ(btf_type_raw_dump(btf, i), exp_types[i - 1], "raw_dump"))
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void btf_dump_printf(void *ctx, const char *fmt, va_list args)
|
||||
{
|
||||
vfprintf(ctx, fmt, args);
|
||||
}
|
||||
|
||||
/* Print BTF-to-C dump into a local buffer and return string pointer back.
|
||||
* Buffer *will* be overwritten by subsequent btf_type_raw_dump() calls
|
||||
*/
|
||||
const char *btf_type_c_dump(const struct btf *btf)
|
||||
{
|
||||
static char buf[16 * 1024];
|
||||
FILE *buf_file;
|
||||
struct btf_dump *d = NULL;
|
||||
struct btf_dump_opts opts = {};
|
||||
int err, i;
|
||||
|
||||
buf_file = fmemopen(buf, sizeof(buf) - 1, "w");
|
||||
if (!buf_file) {
|
||||
fprintf(stderr, "Failed to open memstream: %d\n", errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
opts.ctx = buf_file;
|
||||
d = btf_dump__new(btf, NULL, &opts, btf_dump_printf);
|
||||
if (libbpf_get_error(d)) {
|
||||
fprintf(stderr, "Failed to create btf_dump instance: %ld\n", libbpf_get_error(d));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 1; i <= btf__get_nr_types(btf); i++) {
|
||||
err = btf_dump__dump_type(d, i);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to dump type [%d]: %d\n", i, err);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
fflush(buf_file);
|
||||
fclose(buf_file);
|
||||
return buf;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
#ifndef __BTF_HELPERS_H
|
||||
#define __BTF_HELPERS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <bpf/btf.h>
|
||||
|
||||
int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id);
|
||||
const char *btf_type_raw_dump(const struct btf *btf, int type_id);
|
||||
int btf_validate_raw(struct btf *btf, int nr_types, const char *exp_types[]);
|
||||
|
||||
#define VALIDATE_RAW_BTF(btf, raw_types...) \
|
||||
btf_validate_raw(btf, \
|
||||
sizeof((const char *[]){raw_types})/sizeof(void *),\
|
||||
(const char *[]){raw_types})
|
||||
|
||||
const char *btf_type_c_dump(const struct btf *btf);
|
||||
#endif
|
||||
@@ -6652,7 +6652,7 @@ static void do_test_dedup(unsigned int test_num)
|
||||
const void *test_btf_data, *expect_btf_data;
|
||||
const char *ret_test_next_str, *ret_expect_next_str;
|
||||
const char *test_strs, *expect_strs;
|
||||
const char *test_str_cur, *test_str_end;
|
||||
const char *test_str_cur;
|
||||
const char *expect_str_cur, *expect_str_end;
|
||||
unsigned int raw_btf_size;
|
||||
void *raw_btf;
|
||||
@@ -6719,12 +6719,18 @@ static void do_test_dedup(unsigned int test_num)
|
||||
goto done;
|
||||
}
|
||||
|
||||
test_str_cur = test_strs;
|
||||
test_str_end = test_strs + test_hdr->str_len;
|
||||
expect_str_cur = expect_strs;
|
||||
expect_str_end = expect_strs + expect_hdr->str_len;
|
||||
while (test_str_cur < test_str_end && expect_str_cur < expect_str_end) {
|
||||
while (expect_str_cur < expect_str_end) {
|
||||
size_t test_len, expect_len;
|
||||
int off;
|
||||
|
||||
off = btf__find_str(test_btf, expect_str_cur);
|
||||
if (CHECK(off < 0, "exp str '%s' not found: %d\n", expect_str_cur, off)) {
|
||||
err = -1;
|
||||
goto done;
|
||||
}
|
||||
test_str_cur = btf__str_by_offset(test_btf, off);
|
||||
|
||||
test_len = strlen(test_str_cur);
|
||||
expect_len = strlen(expect_str_cur);
|
||||
@@ -6741,15 +6747,8 @@ static void do_test_dedup(unsigned int test_num)
|
||||
err = -1;
|
||||
goto done;
|
||||
}
|
||||
test_str_cur += test_len + 1;
|
||||
expect_str_cur += expect_len + 1;
|
||||
}
|
||||
if (CHECK(test_str_cur != test_str_end,
|
||||
"test_str_cur:%p != test_str_end:%p",
|
||||
test_str_cur, test_str_end)) {
|
||||
err = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
test_nr_types = btf__get_nr_types(test_btf);
|
||||
expect_nr_types = btf__get_nr_types(expect_btf);
|
||||
@@ -6775,10 +6774,21 @@ static void do_test_dedup(unsigned int test_num)
|
||||
err = -1;
|
||||
goto done;
|
||||
}
|
||||
if (CHECK(memcmp((void *)test_type,
|
||||
(void *)expect_type,
|
||||
test_size),
|
||||
"type #%d: contents differ", i)) {
|
||||
if (CHECK(btf_kind(test_type) != btf_kind(expect_type),
|
||||
"type %d kind: exp %d != got %u\n",
|
||||
i, btf_kind(expect_type), btf_kind(test_type))) {
|
||||
err = -1;
|
||||
goto done;
|
||||
}
|
||||
if (CHECK(test_type->info != expect_type->info,
|
||||
"type %d info: exp %d != got %u\n",
|
||||
i, expect_type->info, test_type->info)) {
|
||||
err = -1;
|
||||
goto done;
|
||||
}
|
||||
if (CHECK(test_type->size != expect_type->size,
|
||||
"type %d size/type: exp %d != got %u\n",
|
||||
i, expect_type->size, test_type->size)) {
|
||||
err = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,325 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
#include <test_progs.h>
|
||||
#include <bpf/btf.h>
|
||||
#include "btf_helpers.h"
|
||||
|
||||
static void test_split_simple() {
|
||||
const struct btf_type *t;
|
||||
struct btf *btf1, *btf2;
|
||||
int str_off, err;
|
||||
|
||||
btf1 = btf__new_empty();
|
||||
if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
|
||||
return;
|
||||
|
||||
btf__set_pointer_size(btf1, 8); /* enforce 64-bit arch */
|
||||
|
||||
btf__add_int(btf1, "int", 4, BTF_INT_SIGNED); /* [1] int */
|
||||
btf__add_ptr(btf1, 1); /* [2] ptr to int */
|
||||
btf__add_struct(btf1, "s1", 4); /* [3] struct s1 { */
|
||||
btf__add_field(btf1, "f1", 1, 0, 0); /* int f1; */
|
||||
/* } */
|
||||
|
||||
VALIDATE_RAW_BTF(
|
||||
btf1,
|
||||
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[2] PTR '(anon)' type_id=1",
|
||||
"[3] STRUCT 's1' size=4 vlen=1\n"
|
||||
"\t'f1' type_id=1 bits_offset=0");
|
||||
|
||||
ASSERT_STREQ(btf_type_c_dump(btf1), "\
|
||||
struct s1 {\n\
|
||||
int f1;\n\
|
||||
};\n\n", "c_dump");
|
||||
|
||||
btf2 = btf__new_empty_split(btf1);
|
||||
if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
|
||||
goto cleanup;
|
||||
|
||||
/* pointer size should be "inherited" from main BTF */
|
||||
ASSERT_EQ(btf__pointer_size(btf2), 8, "inherit_ptr_sz");
|
||||
|
||||
str_off = btf__find_str(btf2, "int");
|
||||
ASSERT_NEQ(str_off, -ENOENT, "str_int_missing");
|
||||
|
||||
t = btf__type_by_id(btf2, 1);
|
||||
if (!ASSERT_OK_PTR(t, "int_type"))
|
||||
goto cleanup;
|
||||
ASSERT_EQ(btf_is_int(t), true, "int_kind");
|
||||
ASSERT_STREQ(btf__str_by_offset(btf2, t->name_off), "int", "int_name");
|
||||
|
||||
btf__add_struct(btf2, "s2", 16); /* [4] struct s2 { */
|
||||
btf__add_field(btf2, "f1", 6, 0, 0); /* struct s1 f1; */
|
||||
btf__add_field(btf2, "f2", 5, 32, 0); /* int f2; */
|
||||
btf__add_field(btf2, "f3", 2, 64, 0); /* int *f3; */
|
||||
/* } */
|
||||
|
||||
/* duplicated int */
|
||||
btf__add_int(btf2, "int", 4, BTF_INT_SIGNED); /* [5] int */
|
||||
|
||||
/* duplicated struct s1 */
|
||||
btf__add_struct(btf2, "s1", 4); /* [6] struct s1 { */
|
||||
btf__add_field(btf2, "f1", 5, 0, 0); /* int f1; */
|
||||
/* } */
|
||||
|
||||
VALIDATE_RAW_BTF(
|
||||
btf2,
|
||||
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[2] PTR '(anon)' type_id=1",
|
||||
"[3] STRUCT 's1' size=4 vlen=1\n"
|
||||
"\t'f1' type_id=1 bits_offset=0",
|
||||
"[4] STRUCT 's2' size=16 vlen=3\n"
|
||||
"\t'f1' type_id=6 bits_offset=0\n"
|
||||
"\t'f2' type_id=5 bits_offset=32\n"
|
||||
"\t'f3' type_id=2 bits_offset=64",
|
||||
"[5] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[6] STRUCT 's1' size=4 vlen=1\n"
|
||||
"\t'f1' type_id=5 bits_offset=0");
|
||||
|
||||
ASSERT_STREQ(btf_type_c_dump(btf2), "\
|
||||
struct s1 {\n\
|
||||
int f1;\n\
|
||||
};\n\
|
||||
\n\
|
||||
struct s1___2 {\n\
|
||||
int f1;\n\
|
||||
};\n\
|
||||
\n\
|
||||
struct s2 {\n\
|
||||
struct s1___2 f1;\n\
|
||||
int f2;\n\
|
||||
int *f3;\n\
|
||||
};\n\n", "c_dump");
|
||||
|
||||
err = btf__dedup(btf2, NULL, NULL);
|
||||
if (!ASSERT_OK(err, "btf_dedup"))
|
||||
goto cleanup;
|
||||
|
||||
VALIDATE_RAW_BTF(
|
||||
btf2,
|
||||
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[2] PTR '(anon)' type_id=1",
|
||||
"[3] STRUCT 's1' size=4 vlen=1\n"
|
||||
"\t'f1' type_id=1 bits_offset=0",
|
||||
"[4] STRUCT 's2' size=16 vlen=3\n"
|
||||
"\t'f1' type_id=3 bits_offset=0\n"
|
||||
"\t'f2' type_id=1 bits_offset=32\n"
|
||||
"\t'f3' type_id=2 bits_offset=64");
|
||||
|
||||
ASSERT_STREQ(btf_type_c_dump(btf2), "\
|
||||
struct s1 {\n\
|
||||
int f1;\n\
|
||||
};\n\
|
||||
\n\
|
||||
struct s2 {\n\
|
||||
struct s1 f1;\n\
|
||||
int f2;\n\
|
||||
int *f3;\n\
|
||||
};\n\n", "c_dump");
|
||||
|
||||
cleanup:
|
||||
btf__free(btf2);
|
||||
btf__free(btf1);
|
||||
}
|
||||
|
||||
static void test_split_fwd_resolve() {
|
||||
struct btf *btf1, *btf2;
|
||||
int err;
|
||||
|
||||
btf1 = btf__new_empty();
|
||||
if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
|
||||
return;
|
||||
|
||||
btf__set_pointer_size(btf1, 8); /* enforce 64-bit arch */
|
||||
|
||||
btf__add_int(btf1, "int", 4, BTF_INT_SIGNED); /* [1] int */
|
||||
btf__add_ptr(btf1, 4); /* [2] ptr to struct s1 */
|
||||
btf__add_ptr(btf1, 5); /* [3] ptr to struct s2 */
|
||||
btf__add_struct(btf1, "s1", 16); /* [4] struct s1 { */
|
||||
btf__add_field(btf1, "f1", 2, 0, 0); /* struct s1 *f1; */
|
||||
btf__add_field(btf1, "f2", 3, 64, 0); /* struct s2 *f2; */
|
||||
/* } */
|
||||
btf__add_struct(btf1, "s2", 4); /* [5] struct s2 { */
|
||||
btf__add_field(btf1, "f1", 1, 0, 0); /* int f1; */
|
||||
/* } */
|
||||
|
||||
VALIDATE_RAW_BTF(
|
||||
btf1,
|
||||
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[2] PTR '(anon)' type_id=4",
|
||||
"[3] PTR '(anon)' type_id=5",
|
||||
"[4] STRUCT 's1' size=16 vlen=2\n"
|
||||
"\t'f1' type_id=2 bits_offset=0\n"
|
||||
"\t'f2' type_id=3 bits_offset=64",
|
||||
"[5] STRUCT 's2' size=4 vlen=1\n"
|
||||
"\t'f1' type_id=1 bits_offset=0");
|
||||
|
||||
btf2 = btf__new_empty_split(btf1);
|
||||
if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
|
||||
goto cleanup;
|
||||
|
||||
btf__add_int(btf2, "int", 4, BTF_INT_SIGNED); /* [6] int */
|
||||
btf__add_ptr(btf2, 10); /* [7] ptr to struct s1 */
|
||||
btf__add_fwd(btf2, "s2", BTF_FWD_STRUCT); /* [8] fwd for struct s2 */
|
||||
btf__add_ptr(btf2, 8); /* [9] ptr to fwd struct s2 */
|
||||
btf__add_struct(btf2, "s1", 16); /* [10] struct s1 { */
|
||||
btf__add_field(btf2, "f1", 7, 0, 0); /* struct s1 *f1; */
|
||||
btf__add_field(btf2, "f2", 9, 64, 0); /* struct s2 *f2; */
|
||||
/* } */
|
||||
|
||||
VALIDATE_RAW_BTF(
|
||||
btf2,
|
||||
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[2] PTR '(anon)' type_id=4",
|
||||
"[3] PTR '(anon)' type_id=5",
|
||||
"[4] STRUCT 's1' size=16 vlen=2\n"
|
||||
"\t'f1' type_id=2 bits_offset=0\n"
|
||||
"\t'f2' type_id=3 bits_offset=64",
|
||||
"[5] STRUCT 's2' size=4 vlen=1\n"
|
||||
"\t'f1' type_id=1 bits_offset=0",
|
||||
"[6] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[7] PTR '(anon)' type_id=10",
|
||||
"[8] FWD 's2' fwd_kind=struct",
|
||||
"[9] PTR '(anon)' type_id=8",
|
||||
"[10] STRUCT 's1' size=16 vlen=2\n"
|
||||
"\t'f1' type_id=7 bits_offset=0\n"
|
||||
"\t'f2' type_id=9 bits_offset=64");
|
||||
|
||||
err = btf__dedup(btf2, NULL, NULL);
|
||||
if (!ASSERT_OK(err, "btf_dedup"))
|
||||
goto cleanup;
|
||||
|
||||
VALIDATE_RAW_BTF(
|
||||
btf2,
|
||||
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[2] PTR '(anon)' type_id=4",
|
||||
"[3] PTR '(anon)' type_id=5",
|
||||
"[4] STRUCT 's1' size=16 vlen=2\n"
|
||||
"\t'f1' type_id=2 bits_offset=0\n"
|
||||
"\t'f2' type_id=3 bits_offset=64",
|
||||
"[5] STRUCT 's2' size=4 vlen=1\n"
|
||||
"\t'f1' type_id=1 bits_offset=0");
|
||||
|
||||
cleanup:
|
||||
btf__free(btf2);
|
||||
btf__free(btf1);
|
||||
}
|
||||
|
||||
static void test_split_struct_duped() {
|
||||
struct btf *btf1, *btf2;
|
||||
int err;
|
||||
|
||||
btf1 = btf__new_empty();
|
||||
if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
|
||||
return;
|
||||
|
||||
btf__set_pointer_size(btf1, 8); /* enforce 64-bit arch */
|
||||
|
||||
btf__add_int(btf1, "int", 4, BTF_INT_SIGNED); /* [1] int */
|
||||
btf__add_ptr(btf1, 5); /* [2] ptr to struct s1 */
|
||||
btf__add_fwd(btf1, "s2", BTF_FWD_STRUCT); /* [3] fwd for struct s2 */
|
||||
btf__add_ptr(btf1, 3); /* [4] ptr to fwd struct s2 */
|
||||
btf__add_struct(btf1, "s1", 16); /* [5] struct s1 { */
|
||||
btf__add_field(btf1, "f1", 2, 0, 0); /* struct s1 *f1; */
|
||||
btf__add_field(btf1, "f2", 4, 64, 0); /* struct s2 *f2; */
|
||||
/* } */
|
||||
|
||||
VALIDATE_RAW_BTF(
|
||||
btf1,
|
||||
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[2] PTR '(anon)' type_id=5",
|
||||
"[3] FWD 's2' fwd_kind=struct",
|
||||
"[4] PTR '(anon)' type_id=3",
|
||||
"[5] STRUCT 's1' size=16 vlen=2\n"
|
||||
"\t'f1' type_id=2 bits_offset=0\n"
|
||||
"\t'f2' type_id=4 bits_offset=64");
|
||||
|
||||
btf2 = btf__new_empty_split(btf1);
|
||||
if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
|
||||
goto cleanup;
|
||||
|
||||
btf__add_int(btf2, "int", 4, BTF_INT_SIGNED); /* [6] int */
|
||||
btf__add_ptr(btf2, 10); /* [7] ptr to struct s1 */
|
||||
btf__add_fwd(btf2, "s2", BTF_FWD_STRUCT); /* [8] fwd for struct s2 */
|
||||
btf__add_ptr(btf2, 11); /* [9] ptr to struct s2 */
|
||||
btf__add_struct(btf2, "s1", 16); /* [10] struct s1 { */
|
||||
btf__add_field(btf2, "f1", 7, 0, 0); /* struct s1 *f1; */
|
||||
btf__add_field(btf2, "f2", 9, 64, 0); /* struct s2 *f2; */
|
||||
/* } */
|
||||
btf__add_struct(btf2, "s2", 40); /* [11] struct s2 { */
|
||||
btf__add_field(btf2, "f1", 7, 0, 0); /* struct s1 *f1; */
|
||||
btf__add_field(btf2, "f2", 9, 64, 0); /* struct s2 *f2; */
|
||||
btf__add_field(btf2, "f3", 6, 128, 0); /* int f3; */
|
||||
btf__add_field(btf2, "f4", 10, 192, 0); /* struct s1 f4; */
|
||||
/* } */
|
||||
btf__add_ptr(btf2, 8); /* [12] ptr to fwd struct s2 */
|
||||
btf__add_struct(btf2, "s3", 8); /* [13] struct s3 { */
|
||||
btf__add_field(btf2, "f1", 12, 0, 0); /* struct s2 *f1; (fwd) */
|
||||
/* } */
|
||||
|
||||
VALIDATE_RAW_BTF(
|
||||
btf2,
|
||||
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[2] PTR '(anon)' type_id=5",
|
||||
"[3] FWD 's2' fwd_kind=struct",
|
||||
"[4] PTR '(anon)' type_id=3",
|
||||
"[5] STRUCT 's1' size=16 vlen=2\n"
|
||||
"\t'f1' type_id=2 bits_offset=0\n"
|
||||
"\t'f2' type_id=4 bits_offset=64",
|
||||
"[6] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[7] PTR '(anon)' type_id=10",
|
||||
"[8] FWD 's2' fwd_kind=struct",
|
||||
"[9] PTR '(anon)' type_id=11",
|
||||
"[10] STRUCT 's1' size=16 vlen=2\n"
|
||||
"\t'f1' type_id=7 bits_offset=0\n"
|
||||
"\t'f2' type_id=9 bits_offset=64",
|
||||
"[11] STRUCT 's2' size=40 vlen=4\n"
|
||||
"\t'f1' type_id=7 bits_offset=0\n"
|
||||
"\t'f2' type_id=9 bits_offset=64\n"
|
||||
"\t'f3' type_id=6 bits_offset=128\n"
|
||||
"\t'f4' type_id=10 bits_offset=192",
|
||||
"[12] PTR '(anon)' type_id=8",
|
||||
"[13] STRUCT 's3' size=8 vlen=1\n"
|
||||
"\t'f1' type_id=12 bits_offset=0");
|
||||
|
||||
err = btf__dedup(btf2, NULL, NULL);
|
||||
if (!ASSERT_OK(err, "btf_dedup"))
|
||||
goto cleanup;
|
||||
|
||||
VALIDATE_RAW_BTF(
|
||||
btf2,
|
||||
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
|
||||
"[2] PTR '(anon)' type_id=5",
|
||||
"[3] FWD 's2' fwd_kind=struct",
|
||||
"[4] PTR '(anon)' type_id=3",
|
||||
"[5] STRUCT 's1' size=16 vlen=2\n"
|
||||
"\t'f1' type_id=2 bits_offset=0\n"
|
||||
"\t'f2' type_id=4 bits_offset=64",
|
||||
"[6] PTR '(anon)' type_id=8",
|
||||
"[7] PTR '(anon)' type_id=9",
|
||||
"[8] STRUCT 's1' size=16 vlen=2\n"
|
||||
"\t'f1' type_id=6 bits_offset=0\n"
|
||||
"\t'f2' type_id=7 bits_offset=64",
|
||||
"[9] STRUCT 's2' size=40 vlen=4\n"
|
||||
"\t'f1' type_id=6 bits_offset=0\n"
|
||||
"\t'f2' type_id=7 bits_offset=64\n"
|
||||
"\t'f3' type_id=1 bits_offset=128\n"
|
||||
"\t'f4' type_id=8 bits_offset=192",
|
||||
"[10] STRUCT 's3' size=8 vlen=1\n"
|
||||
"\t'f1' type_id=7 bits_offset=0");
|
||||
|
||||
cleanup:
|
||||
btf__free(btf2);
|
||||
btf__free(btf1);
|
||||
}
|
||||
|
||||
void test_btf_dedup_split()
|
||||
{
|
||||
if (test__start_subtest("split_simple"))
|
||||
test_split_simple();
|
||||
if (test__start_subtest("split_struct_duped"))
|
||||
test_split_struct_duped();
|
||||
if (test__start_subtest("split_fwd_resolve"))
|
||||
test_split_fwd_resolve();
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
#include "test_btf_skc_cls_ingress.skel.h"
|
||||
|
||||
static struct test_btf_skc_cls_ingress *skel;
|
||||
struct sockaddr_in6 srv_sa6;
|
||||
static struct sockaddr_in6 srv_sa6;
|
||||
static __u32 duration;
|
||||
|
||||
#define PROG_PIN_FILE "/sys/fs/bpf/btf_skc_cls_ingress"
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
#include <test_progs.h>
|
||||
#include <bpf/btf.h>
|
||||
|
||||
static char *dump_buf;
|
||||
static size_t dump_buf_sz;
|
||||
static FILE *dump_buf_file;
|
||||
|
||||
static void btf_dump_printf(void *ctx, const char *fmt, va_list args)
|
||||
{
|
||||
vfprintf(ctx, fmt, args);
|
||||
}
|
||||
|
||||
void test_btf_split() {
|
||||
struct btf_dump_opts opts;
|
||||
struct btf_dump *d = NULL;
|
||||
const struct btf_type *t;
|
||||
struct btf *btf1, *btf2;
|
||||
int str_off, i, err;
|
||||
|
||||
btf1 = btf__new_empty();
|
||||
if (!ASSERT_OK_PTR(btf1, "empty_main_btf"))
|
||||
return;
|
||||
|
||||
btf__set_pointer_size(btf1, 8); /* enforce 64-bit arch */
|
||||
|
||||
btf__add_int(btf1, "int", 4, BTF_INT_SIGNED); /* [1] int */
|
||||
btf__add_ptr(btf1, 1); /* [2] ptr to int */
|
||||
|
||||
btf__add_struct(btf1, "s1", 4); /* [3] struct s1 { */
|
||||
btf__add_field(btf1, "f1", 1, 0, 0); /* int f1; */
|
||||
/* } */
|
||||
|
||||
btf2 = btf__new_empty_split(btf1);
|
||||
if (!ASSERT_OK_PTR(btf2, "empty_split_btf"))
|
||||
goto cleanup;
|
||||
|
||||
/* pointer size should be "inherited" from main BTF */
|
||||
ASSERT_EQ(btf__pointer_size(btf2), 8, "inherit_ptr_sz");
|
||||
|
||||
str_off = btf__find_str(btf2, "int");
|
||||
ASSERT_NEQ(str_off, -ENOENT, "str_int_missing");
|
||||
|
||||
t = btf__type_by_id(btf2, 1);
|
||||
if (!ASSERT_OK_PTR(t, "int_type"))
|
||||
goto cleanup;
|
||||
ASSERT_EQ(btf_is_int(t), true, "int_kind");
|
||||
ASSERT_STREQ(btf__str_by_offset(btf2, t->name_off), "int", "int_name");
|
||||
|
||||
btf__add_struct(btf2, "s2", 16); /* [4] struct s2 { */
|
||||
btf__add_field(btf2, "f1", 3, 0, 0); /* struct s1 f1; */
|
||||
btf__add_field(btf2, "f2", 1, 32, 0); /* int f2; */
|
||||
btf__add_field(btf2, "f3", 2, 64, 0); /* int *f3; */
|
||||
/* } */
|
||||
|
||||
t = btf__type_by_id(btf1, 4);
|
||||
ASSERT_NULL(t, "split_type_in_main");
|
||||
|
||||
t = btf__type_by_id(btf2, 4);
|
||||
if (!ASSERT_OK_PTR(t, "split_struct_type"))
|
||||
goto cleanup;
|
||||
ASSERT_EQ(btf_is_struct(t), true, "split_struct_kind");
|
||||
ASSERT_EQ(btf_vlen(t), 3, "split_struct_vlen");
|
||||
ASSERT_STREQ(btf__str_by_offset(btf2, t->name_off), "s2", "split_struct_name");
|
||||
|
||||
/* BTF-to-C dump of split BTF */
|
||||
dump_buf_file = open_memstream(&dump_buf, &dump_buf_sz);
|
||||
if (!ASSERT_OK_PTR(dump_buf_file, "dump_memstream"))
|
||||
return;
|
||||
opts.ctx = dump_buf_file;
|
||||
d = btf_dump__new(btf2, NULL, &opts, btf_dump_printf);
|
||||
if (!ASSERT_OK_PTR(d, "btf_dump__new"))
|
||||
goto cleanup;
|
||||
for (i = 1; i <= btf__get_nr_types(btf2); i++) {
|
||||
err = btf_dump__dump_type(d, i);
|
||||
ASSERT_OK(err, "dump_type_ok");
|
||||
}
|
||||
fflush(dump_buf_file);
|
||||
dump_buf[dump_buf_sz] = 0; /* some libc implementations don't do this */
|
||||
ASSERT_STREQ(dump_buf,
|
||||
"struct s1 {\n"
|
||||
" int f1;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"struct s2 {\n"
|
||||
" struct s1 f1;\n"
|
||||
" int f2;\n"
|
||||
" int *f3;\n"
|
||||
"};\n\n", "c_dump");
|
||||
|
||||
cleanup:
|
||||
if (dump_buf_file)
|
||||
fclose(dump_buf_file);
|
||||
free(dump_buf);
|
||||
btf_dump__free(d);
|
||||
btf__free(btf1);
|
||||
btf__free(btf2);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
#include <test_progs.h>
|
||||
#include <bpf/btf.h>
|
||||
#include "btf_helpers.h"
|
||||
|
||||
static int duration = 0;
|
||||
|
||||
@@ -39,6 +40,8 @@ void test_btf_write() {
|
||||
ASSERT_EQ(t->size, 4, "int_sz");
|
||||
ASSERT_EQ(btf_int_encoding(t), BTF_INT_SIGNED, "int_enc");
|
||||
ASSERT_EQ(btf_int_bits(t), 32, "int_bits");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 1),
|
||||
"[1] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED", "raw_dump");
|
||||
|
||||
/* invalid int size */
|
||||
id = btf__add_int(btf, "bad sz int", 7, 0);
|
||||
@@ -59,24 +62,32 @@ void test_btf_write() {
|
||||
t = btf__type_by_id(btf, 2);
|
||||
ASSERT_EQ(btf_kind(t), BTF_KIND_PTR, "ptr_kind");
|
||||
ASSERT_EQ(t->type, 1, "ptr_type");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 2),
|
||||
"[2] PTR '(anon)' type_id=1", "raw_dump");
|
||||
|
||||
id = btf__add_const(btf, 5); /* points forward to restrict */
|
||||
ASSERT_EQ(id, 3, "const_id");
|
||||
t = btf__type_by_id(btf, 3);
|
||||
ASSERT_EQ(btf_kind(t), BTF_KIND_CONST, "const_kind");
|
||||
ASSERT_EQ(t->type, 5, "const_type");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 3),
|
||||
"[3] CONST '(anon)' type_id=5", "raw_dump");
|
||||
|
||||
id = btf__add_volatile(btf, 3);
|
||||
ASSERT_EQ(id, 4, "volatile_id");
|
||||
t = btf__type_by_id(btf, 4);
|
||||
ASSERT_EQ(btf_kind(t), BTF_KIND_VOLATILE, "volatile_kind");
|
||||
ASSERT_EQ(t->type, 3, "volatile_type");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 4),
|
||||
"[4] VOLATILE '(anon)' type_id=3", "raw_dump");
|
||||
|
||||
id = btf__add_restrict(btf, 4);
|
||||
ASSERT_EQ(id, 5, "restrict_id");
|
||||
t = btf__type_by_id(btf, 5);
|
||||
ASSERT_EQ(btf_kind(t), BTF_KIND_RESTRICT, "restrict_kind");
|
||||
ASSERT_EQ(t->type, 4, "restrict_type");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 5),
|
||||
"[5] RESTRICT '(anon)' type_id=4", "raw_dump");
|
||||
|
||||
/* ARRAY */
|
||||
id = btf__add_array(btf, 1, 2, 10); /* int *[10] */
|
||||
@@ -86,6 +97,8 @@ void test_btf_write() {
|
||||
ASSERT_EQ(btf_array(t)->index_type, 1, "array_index_type");
|
||||
ASSERT_EQ(btf_array(t)->type, 2, "array_elem_type");
|
||||
ASSERT_EQ(btf_array(t)->nelems, 10, "array_nelems");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 6),
|
||||
"[6] ARRAY '(anon)' type_id=2 index_type_id=1 nr_elems=10", "raw_dump");
|
||||
|
||||
/* STRUCT */
|
||||
err = btf__add_field(btf, "field", 1, 0, 0);
|
||||
@@ -113,6 +126,10 @@ void test_btf_write() {
|
||||
ASSERT_EQ(m->type, 1, "f2_type");
|
||||
ASSERT_EQ(btf_member_bit_offset(t, 1), 32, "f2_bit_off");
|
||||
ASSERT_EQ(btf_member_bitfield_size(t, 1), 16, "f2_bit_sz");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 7),
|
||||
"[7] STRUCT 's1' size=8 vlen=2\n"
|
||||
"\t'f1' type_id=1 bits_offset=0\n"
|
||||
"\t'f2' type_id=1 bits_offset=32 bitfield_size=16", "raw_dump");
|
||||
|
||||
/* UNION */
|
||||
id = btf__add_union(btf, "u1", 8);
|
||||
@@ -136,6 +153,9 @@ void test_btf_write() {
|
||||
ASSERT_EQ(m->type, 1, "f1_type");
|
||||
ASSERT_EQ(btf_member_bit_offset(t, 0), 0, "f1_bit_off");
|
||||
ASSERT_EQ(btf_member_bitfield_size(t, 0), 16, "f1_bit_sz");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 8),
|
||||
"[8] UNION 'u1' size=8 vlen=1\n"
|
||||
"\t'f1' type_id=1 bits_offset=0 bitfield_size=16", "raw_dump");
|
||||
|
||||
/* ENUM */
|
||||
id = btf__add_enum(btf, "e1", 4);
|
||||
@@ -156,6 +176,10 @@ void test_btf_write() {
|
||||
v = btf_enum(t) + 1;
|
||||
ASSERT_STREQ(btf__str_by_offset(btf, v->name_off), "v2", "v2_name");
|
||||
ASSERT_EQ(v->val, 2, "v2_val");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 9),
|
||||
"[9] ENUM 'e1' size=4 vlen=2\n"
|
||||
"\t'v1' val=1\n"
|
||||
"\t'v2' val=2", "raw_dump");
|
||||
|
||||
/* FWDs */
|
||||
id = btf__add_fwd(btf, "struct_fwd", BTF_FWD_STRUCT);
|
||||
@@ -164,6 +188,8 @@ void test_btf_write() {
|
||||
ASSERT_STREQ(btf__str_by_offset(btf, t->name_off), "struct_fwd", "fwd_name");
|
||||
ASSERT_EQ(btf_kind(t), BTF_KIND_FWD, "fwd_kind");
|
||||
ASSERT_EQ(btf_kflag(t), 0, "fwd_kflag");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 10),
|
||||
"[10] FWD 'struct_fwd' fwd_kind=struct", "raw_dump");
|
||||
|
||||
id = btf__add_fwd(btf, "union_fwd", BTF_FWD_UNION);
|
||||
ASSERT_EQ(id, 11, "union_fwd_id");
|
||||
@@ -171,6 +197,8 @@ void test_btf_write() {
|
||||
ASSERT_STREQ(btf__str_by_offset(btf, t->name_off), "union_fwd", "fwd_name");
|
||||
ASSERT_EQ(btf_kind(t), BTF_KIND_FWD, "fwd_kind");
|
||||
ASSERT_EQ(btf_kflag(t), 1, "fwd_kflag");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 11),
|
||||
"[11] FWD 'union_fwd' fwd_kind=union", "raw_dump");
|
||||
|
||||
id = btf__add_fwd(btf, "enum_fwd", BTF_FWD_ENUM);
|
||||
ASSERT_EQ(id, 12, "enum_fwd_id");
|
||||
@@ -179,6 +207,8 @@ void test_btf_write() {
|
||||
ASSERT_EQ(btf_kind(t), BTF_KIND_ENUM, "enum_fwd_kind");
|
||||
ASSERT_EQ(btf_vlen(t), 0, "enum_fwd_kind");
|
||||
ASSERT_EQ(t->size, 4, "enum_fwd_sz");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 12),
|
||||
"[12] ENUM 'enum_fwd' size=4 vlen=0", "raw_dump");
|
||||
|
||||
/* TYPEDEF */
|
||||
id = btf__add_typedef(btf, "typedef1", 1);
|
||||
@@ -187,6 +217,8 @@ void test_btf_write() {
|
||||
ASSERT_STREQ(btf__str_by_offset(btf, t->name_off), "typedef1", "typedef_name");
|
||||
ASSERT_EQ(btf_kind(t), BTF_KIND_TYPEDEF, "typedef_kind");
|
||||
ASSERT_EQ(t->type, 1, "typedef_type");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 13),
|
||||
"[13] TYPEDEF 'typedef1' type_id=1", "raw_dump");
|
||||
|
||||
/* FUNC & FUNC_PROTO */
|
||||
id = btf__add_func(btf, "func1", BTF_FUNC_GLOBAL, 15);
|
||||
@@ -196,6 +228,8 @@ void test_btf_write() {
|
||||
ASSERT_EQ(t->type, 15, "func_type");
|
||||
ASSERT_EQ(btf_kind(t), BTF_KIND_FUNC, "func_kind");
|
||||
ASSERT_EQ(btf_vlen(t), BTF_FUNC_GLOBAL, "func_vlen");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 14),
|
||||
"[14] FUNC 'func1' type_id=15 linkage=global", "raw_dump");
|
||||
|
||||
id = btf__add_func_proto(btf, 1);
|
||||
ASSERT_EQ(id, 15, "func_proto_id");
|
||||
@@ -214,6 +248,10 @@ void test_btf_write() {
|
||||
p = btf_params(t) + 1;
|
||||
ASSERT_STREQ(btf__str_by_offset(btf, p->name_off), "p2", "p2_name");
|
||||
ASSERT_EQ(p->type, 2, "p2_type");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 15),
|
||||
"[15] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
|
||||
"\t'p1' type_id=1\n"
|
||||
"\t'p2' type_id=2", "raw_dump");
|
||||
|
||||
/* VAR */
|
||||
id = btf__add_var(btf, "var1", BTF_VAR_GLOBAL_ALLOCATED, 1);
|
||||
@@ -223,6 +261,8 @@ void test_btf_write() {
|
||||
ASSERT_EQ(btf_kind(t), BTF_KIND_VAR, "var_kind");
|
||||
ASSERT_EQ(t->type, 1, "var_type");
|
||||
ASSERT_EQ(btf_var(t)->linkage, BTF_VAR_GLOBAL_ALLOCATED, "var_type");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 16),
|
||||
"[16] VAR 'var1' type_id=1, linkage=global-alloc", "raw_dump");
|
||||
|
||||
/* DATASECT */
|
||||
id = btf__add_datasec(btf, "datasec1", 12);
|
||||
@@ -239,6 +279,9 @@ void test_btf_write() {
|
||||
ASSERT_EQ(vi->type, 1, "v1_type");
|
||||
ASSERT_EQ(vi->offset, 4, "v1_off");
|
||||
ASSERT_EQ(vi->size, 8, "v1_sz");
|
||||
ASSERT_STREQ(btf_type_raw_dump(btf, 17),
|
||||
"[17] DATASEC 'datasec1' size=12 vlen=1\n"
|
||||
"\ttype_id=1 offset=4 size=8", "raw_dump");
|
||||
|
||||
btf__free(btf);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <test_progs.h>
|
||||
#include "test_hash_large_key.skel.h"
|
||||
|
||||
void test_hash_large_key(void)
|
||||
{
|
||||
int err, value = 21, duration = 0, hash_map_fd;
|
||||
struct test_hash_large_key *skel;
|
||||
|
||||
struct bigelement {
|
||||
int a;
|
||||
char b[4096];
|
||||
long long c;
|
||||
} key;
|
||||
bzero(&key, sizeof(key));
|
||||
|
||||
skel = test_hash_large_key__open_and_load();
|
||||
if (CHECK(!skel, "skel_open_and_load", "skeleton open/load failed\n"))
|
||||
return;
|
||||
|
||||
hash_map_fd = bpf_map__fd(skel->maps.hash_map);
|
||||
if (CHECK(hash_map_fd < 0, "bpf_map__fd", "failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
err = test_hash_large_key__attach(skel);
|
||||
if (CHECK(err, "attach_raw_tp", "err %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
err = bpf_map_update_elem(hash_map_fd, &key, &value, BPF_ANY);
|
||||
if (CHECK(err, "bpf_map_update_elem", "errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
|
||||
key.c = 1;
|
||||
err = bpf_map_lookup_elem(hash_map_fd, &key, &value);
|
||||
if (CHECK(err, "bpf_map_lookup_elem", "errno=%d\n", errno))
|
||||
goto cleanup;
|
||||
|
||||
CHECK_FAIL(value != 42);
|
||||
|
||||
cleanup:
|
||||
test_hash_large_key__destroy(skel);
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include "test_progs.h"
|
||||
#include "network_helpers.h"
|
||||
#include "test_sk_storage_trace_itself.skel.h"
|
||||
#include "test_sk_storage_tracing.skel.h"
|
||||
|
||||
#define LO_ADDR6 "::1"
|
||||
#define TEST_COMM "test_progs"
|
||||
|
||||
struct sk_stg {
|
||||
__u32 pid;
|
||||
__u32 last_notclose_state;
|
||||
char comm[16];
|
||||
};
|
||||
|
||||
static struct test_sk_storage_tracing *skel;
|
||||
static __u32 duration;
|
||||
static pid_t my_pid;
|
||||
|
||||
static int check_sk_stg(int sk_fd, __u32 expected_state)
|
||||
{
|
||||
struct sk_stg sk_stg;
|
||||
int err;
|
||||
|
||||
err = bpf_map_lookup_elem(bpf_map__fd(skel->maps.sk_stg_map), &sk_fd,
|
||||
&sk_stg);
|
||||
if (!ASSERT_OK(err, "map_lookup(sk_stg_map)"))
|
||||
return -1;
|
||||
|
||||
if (!ASSERT_EQ(sk_stg.last_notclose_state, expected_state,
|
||||
"last_notclose_state"))
|
||||
return -1;
|
||||
|
||||
if (!ASSERT_EQ(sk_stg.pid, my_pid, "pid"))
|
||||
return -1;
|
||||
|
||||
if (!ASSERT_STREQ(sk_stg.comm, skel->bss->task_comm, "task_comm"))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void do_test(void)
|
||||
{
|
||||
int listen_fd = -1, passive_fd = -1, active_fd = -1, value = 1, err;
|
||||
char abyte;
|
||||
|
||||
listen_fd = start_server(AF_INET6, SOCK_STREAM, LO_ADDR6, 0, 0);
|
||||
if (CHECK(listen_fd == -1, "start_server",
|
||||
"listen_fd:%d errno:%d\n", listen_fd, errno))
|
||||
return;
|
||||
|
||||
active_fd = connect_to_fd(listen_fd, 0);
|
||||
if (CHECK(active_fd == -1, "connect_to_fd", "active_fd:%d errno:%d\n",
|
||||
active_fd, errno))
|
||||
goto out;
|
||||
|
||||
err = bpf_map_update_elem(bpf_map__fd(skel->maps.del_sk_stg_map),
|
||||
&active_fd, &value, 0);
|
||||
if (!ASSERT_OK(err, "map_update(del_sk_stg_map)"))
|
||||
goto out;
|
||||
|
||||
passive_fd = accept(listen_fd, NULL, 0);
|
||||
if (CHECK(passive_fd == -1, "accept", "passive_fd:%d errno:%d\n",
|
||||
passive_fd, errno))
|
||||
goto out;
|
||||
|
||||
shutdown(active_fd, SHUT_WR);
|
||||
err = read(passive_fd, &abyte, 1);
|
||||
if (!ASSERT_OK(err, "read(passive_fd)"))
|
||||
goto out;
|
||||
|
||||
shutdown(passive_fd, SHUT_WR);
|
||||
err = read(active_fd, &abyte, 1);
|
||||
if (!ASSERT_OK(err, "read(active_fd)"))
|
||||
goto out;
|
||||
|
||||
err = bpf_map_lookup_elem(bpf_map__fd(skel->maps.del_sk_stg_map),
|
||||
&active_fd, &value);
|
||||
if (!ASSERT_ERR(err, "map_lookup(del_sk_stg_map)"))
|
||||
goto out;
|
||||
|
||||
err = check_sk_stg(listen_fd, BPF_TCP_LISTEN);
|
||||
if (!ASSERT_OK(err, "listen_fd sk_stg"))
|
||||
goto out;
|
||||
|
||||
err = check_sk_stg(active_fd, BPF_TCP_FIN_WAIT2);
|
||||
if (!ASSERT_OK(err, "active_fd sk_stg"))
|
||||
goto out;
|
||||
|
||||
err = check_sk_stg(passive_fd, BPF_TCP_LAST_ACK);
|
||||
ASSERT_OK(err, "passive_fd sk_stg");
|
||||
|
||||
out:
|
||||
if (active_fd != -1)
|
||||
close(active_fd);
|
||||
if (passive_fd != -1)
|
||||
close(passive_fd);
|
||||
if (listen_fd != -1)
|
||||
close(listen_fd);
|
||||
}
|
||||
|
||||
void test_sk_storage_tracing(void)
|
||||
{
|
||||
struct test_sk_storage_trace_itself *skel_itself;
|
||||
int err;
|
||||
|
||||
my_pid = getpid();
|
||||
|
||||
skel_itself = test_sk_storage_trace_itself__open_and_load();
|
||||
|
||||
if (!ASSERT_NULL(skel_itself, "test_sk_storage_trace_itself")) {
|
||||
test_sk_storage_trace_itself__destroy(skel_itself);
|
||||
return;
|
||||
}
|
||||
|
||||
skel = test_sk_storage_tracing__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "test_sk_storage_tracing"))
|
||||
return;
|
||||
|
||||
err = test_sk_storage_tracing__attach(skel);
|
||||
if (!ASSERT_OK(err, "test_sk_storage_tracing__attach")) {
|
||||
test_sk_storage_tracing__destroy(skel);
|
||||
return;
|
||||
}
|
||||
|
||||
do_test();
|
||||
|
||||
test_sk_storage_tracing__destroy(skel);
|
||||
}
|
||||
@@ -18,12 +18,12 @@
|
||||
#define LO_ADDR6 "::1"
|
||||
#define CG_NAME "/tcpbpf-hdr-opt-test"
|
||||
|
||||
struct bpf_test_option exp_passive_estab_in;
|
||||
struct bpf_test_option exp_active_estab_in;
|
||||
struct bpf_test_option exp_passive_fin_in;
|
||||
struct bpf_test_option exp_active_fin_in;
|
||||
struct hdr_stg exp_passive_hdr_stg;
|
||||
struct hdr_stg exp_active_hdr_stg = { .active = true, };
|
||||
static struct bpf_test_option exp_passive_estab_in;
|
||||
static struct bpf_test_option exp_active_estab_in;
|
||||
static struct bpf_test_option exp_passive_fin_in;
|
||||
static struct bpf_test_option exp_active_fin_in;
|
||||
static struct hdr_stg exp_passive_hdr_stg;
|
||||
static struct hdr_stg exp_active_hdr_stg = { .active = true, };
|
||||
|
||||
static struct test_misc_tcp_hdr_options *misc_skel;
|
||||
static struct test_tcp_hdr_options *skel;
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
#include <network_helpers.h>
|
||||
|
||||
#include "test_tcpbpf.h"
|
||||
#include "test_tcpbpf_kern.skel.h"
|
||||
|
||||
#define LO_ADDR6 "::1"
|
||||
#define CG_NAME "/tcpbpf-user-test"
|
||||
|
||||
static __u32 duration;
|
||||
|
||||
static void verify_result(struct tcpbpf_globals *result)
|
||||
{
|
||||
__u32 expected_events = ((1 << BPF_SOCK_OPS_TIMEOUT_INIT) |
|
||||
(1 << BPF_SOCK_OPS_RWND_INIT) |
|
||||
(1 << BPF_SOCK_OPS_TCP_CONNECT_CB) |
|
||||
(1 << BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB) |
|
||||
(1 << BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB) |
|
||||
(1 << BPF_SOCK_OPS_NEEDS_ECN) |
|
||||
(1 << BPF_SOCK_OPS_STATE_CB) |
|
||||
(1 << BPF_SOCK_OPS_TCP_LISTEN_CB));
|
||||
|
||||
/* check global map */
|
||||
CHECK(expected_events != result->event_map, "event_map",
|
||||
"unexpected event_map: actual 0x%08x != expected 0x%08x\n",
|
||||
result->event_map, expected_events);
|
||||
|
||||
ASSERT_EQ(result->bytes_received, 501, "bytes_received");
|
||||
ASSERT_EQ(result->bytes_acked, 1002, "bytes_acked");
|
||||
ASSERT_EQ(result->data_segs_in, 1, "data_segs_in");
|
||||
ASSERT_EQ(result->data_segs_out, 1, "data_segs_out");
|
||||
ASSERT_EQ(result->bad_cb_test_rv, 0x80, "bad_cb_test_rv");
|
||||
ASSERT_EQ(result->good_cb_test_rv, 0, "good_cb_test_rv");
|
||||
ASSERT_EQ(result->num_listen, 1, "num_listen");
|
||||
|
||||
/* 3 comes from one listening socket + both ends of the connection */
|
||||
ASSERT_EQ(result->num_close_events, 3, "num_close_events");
|
||||
|
||||
/* check setsockopt for SAVE_SYN */
|
||||
ASSERT_EQ(result->tcp_save_syn, 0, "tcp_save_syn");
|
||||
|
||||
/* check getsockopt for SAVED_SYN */
|
||||
ASSERT_EQ(result->tcp_saved_syn, 1, "tcp_saved_syn");
|
||||
}
|
||||
|
||||
static void run_test(struct tcpbpf_globals *result)
|
||||
{
|
||||
int listen_fd = -1, cli_fd = -1, accept_fd = -1;
|
||||
char buf[1000];
|
||||
int err = -1;
|
||||
int i, rv;
|
||||
|
||||
listen_fd = start_server(AF_INET6, SOCK_STREAM, LO_ADDR6, 0, 0);
|
||||
if (CHECK(listen_fd == -1, "start_server", "listen_fd:%d errno:%d\n",
|
||||
listen_fd, errno))
|
||||
goto done;
|
||||
|
||||
cli_fd = connect_to_fd(listen_fd, 0);
|
||||
if (CHECK(cli_fd == -1, "connect_to_fd(listen_fd)",
|
||||
"cli_fd:%d errno:%d\n", cli_fd, errno))
|
||||
goto done;
|
||||
|
||||
accept_fd = accept(listen_fd, NULL, NULL);
|
||||
if (CHECK(accept_fd == -1, "accept(listen_fd)",
|
||||
"accept_fd:%d errno:%d\n", accept_fd, errno))
|
||||
goto done;
|
||||
|
||||
/* Send 1000B of '+'s from cli_fd -> accept_fd */
|
||||
for (i = 0; i < 1000; i++)
|
||||
buf[i] = '+';
|
||||
|
||||
rv = send(cli_fd, buf, 1000, 0);
|
||||
if (CHECK(rv != 1000, "send(cli_fd)", "rv:%d errno:%d\n", rv, errno))
|
||||
goto done;
|
||||
|
||||
rv = recv(accept_fd, buf, 1000, 0);
|
||||
if (CHECK(rv != 1000, "recv(accept_fd)", "rv:%d errno:%d\n", rv, errno))
|
||||
goto done;
|
||||
|
||||
/* Send 500B of '.'s from accept_fd ->cli_fd */
|
||||
for (i = 0; i < 500; i++)
|
||||
buf[i] = '.';
|
||||
|
||||
rv = send(accept_fd, buf, 500, 0);
|
||||
if (CHECK(rv != 500, "send(accept_fd)", "rv:%d errno:%d\n", rv, errno))
|
||||
goto done;
|
||||
|
||||
rv = recv(cli_fd, buf, 500, 0);
|
||||
if (CHECK(rv != 500, "recv(cli_fd)", "rv:%d errno:%d\n", rv, errno))
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* shutdown accept first to guarantee correct ordering for
|
||||
* bytes_received and bytes_acked when we go to verify the results.
|
||||
*/
|
||||
shutdown(accept_fd, SHUT_WR);
|
||||
err = recv(cli_fd, buf, 1, 0);
|
||||
if (CHECK(err, "recv(cli_fd) for fin", "err:%d errno:%d\n", err, errno))
|
||||
goto done;
|
||||
|
||||
shutdown(cli_fd, SHUT_WR);
|
||||
err = recv(accept_fd, buf, 1, 0);
|
||||
CHECK(err, "recv(accept_fd) for fin", "err:%d errno:%d\n", err, errno);
|
||||
done:
|
||||
if (accept_fd != -1)
|
||||
close(accept_fd);
|
||||
if (cli_fd != -1)
|
||||
close(cli_fd);
|
||||
if (listen_fd != -1)
|
||||
close(listen_fd);
|
||||
|
||||
if (!err)
|
||||
verify_result(result);
|
||||
}
|
||||
|
||||
void test_tcpbpf_user(void)
|
||||
{
|
||||
struct test_tcpbpf_kern *skel;
|
||||
int cg_fd = -1;
|
||||
|
||||
skel = test_tcpbpf_kern__open_and_load();
|
||||
if (CHECK(!skel, "open and load skel", "failed"))
|
||||
return;
|
||||
|
||||
cg_fd = test__join_cgroup(CG_NAME);
|
||||
if (CHECK(cg_fd < 0, "test__join_cgroup(" CG_NAME ")",
|
||||
"cg_fd:%d errno:%d", cg_fd, errno))
|
||||
goto err;
|
||||
|
||||
skel->links.bpf_testcb = bpf_program__attach_cgroup(skel->progs.bpf_testcb, cg_fd);
|
||||
if (!ASSERT_OK_PTR(skel->links.bpf_testcb, "attach_cgroup(bpf_testcb)"))
|
||||
goto err;
|
||||
|
||||
run_test(&skel->bss->global);
|
||||
|
||||
err:
|
||||
if (cg_fd != -1)
|
||||
close(cg_fd);
|
||||
test_tcpbpf_kern__destroy(skel);
|
||||
}
|
||||
@@ -4,30 +4,165 @@
|
||||
* Copyright (C) 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <asm-generic/errno-base.h>
|
||||
#include <sys/stat.h>
|
||||
#include <test_progs.h>
|
||||
#include <linux/limits.h>
|
||||
|
||||
#include "local_storage.skel.h"
|
||||
#include "network_helpers.h"
|
||||
|
||||
int create_and_unlink_file(void)
|
||||
#ifndef __NR_pidfd_open
|
||||
#define __NR_pidfd_open 434
|
||||
#endif
|
||||
|
||||
static inline int sys_pidfd_open(pid_t pid, unsigned int flags)
|
||||
{
|
||||
char fname[PATH_MAX] = "/tmp/fileXXXXXX";
|
||||
int fd;
|
||||
return syscall(__NR_pidfd_open, pid, flags);
|
||||
}
|
||||
|
||||
fd = mkstemp(fname);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, int fd_out,
|
||||
loff_t *off_out, size_t len,
|
||||
unsigned int flags)
|
||||
{
|
||||
return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out,
|
||||
len, flags);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
unlink(fname);
|
||||
return 0;
|
||||
static unsigned int duration;
|
||||
|
||||
#define TEST_STORAGE_VALUE 0xbeefdead
|
||||
|
||||
struct storage {
|
||||
void *inode;
|
||||
unsigned int value;
|
||||
/* Lock ensures that spin locked versions of local stoage operations
|
||||
* also work, most operations in this tests are still single threaded
|
||||
*/
|
||||
struct bpf_spin_lock lock;
|
||||
};
|
||||
|
||||
/* Copies an rm binary to a temp file. dest is a mkstemp template */
|
||||
static int copy_rm(char *dest)
|
||||
{
|
||||
int fd_in, fd_out = -1, ret = 0;
|
||||
struct stat stat;
|
||||
|
||||
fd_in = open("/bin/rm", O_RDONLY);
|
||||
if (fd_in < 0)
|
||||
return -errno;
|
||||
|
||||
fd_out = mkstemp(dest);
|
||||
if (fd_out < 0) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = fstat(fd_in, &stat);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = copy_file_range(fd_in, NULL, fd_out, NULL, stat.st_size, 0);
|
||||
if (ret == -1) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Set executable permission on the copied file */
|
||||
ret = chmod(dest, 0100);
|
||||
if (ret == -1)
|
||||
ret = -errno;
|
||||
|
||||
out:
|
||||
close(fd_in);
|
||||
close(fd_out);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Fork and exec the provided rm binary and return the exit code of the
|
||||
* forked process and its pid.
|
||||
*/
|
||||
static int run_self_unlink(int *monitored_pid, const char *rm_path)
|
||||
{
|
||||
int child_pid, child_status, ret;
|
||||
int null_fd;
|
||||
|
||||
child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
null_fd = open("/dev/null", O_WRONLY);
|
||||
dup2(null_fd, STDOUT_FILENO);
|
||||
dup2(null_fd, STDERR_FILENO);
|
||||
close(null_fd);
|
||||
|
||||
*monitored_pid = getpid();
|
||||
/* Use the copied /usr/bin/rm to delete itself
|
||||
* /tmp/copy_of_rm /tmp/copy_of_rm.
|
||||
*/
|
||||
ret = execlp(rm_path, rm_path, rm_path, NULL);
|
||||
if (ret)
|
||||
exit(errno);
|
||||
} else if (child_pid > 0) {
|
||||
waitpid(child_pid, &child_status, 0);
|
||||
return WEXITSTATUS(child_status);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static bool check_syscall_operations(int map_fd, int obj_fd)
|
||||
{
|
||||
struct storage val = { .value = TEST_STORAGE_VALUE, .lock = { 0 } },
|
||||
lookup_val = { .value = 0, .lock = { 0 } };
|
||||
int err;
|
||||
|
||||
/* Looking up an existing element should fail initially */
|
||||
err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val,
|
||||
BPF_F_LOCK);
|
||||
if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem",
|
||||
"err:%d errno:%d\n", err, errno))
|
||||
return false;
|
||||
|
||||
/* Create a new element */
|
||||
err = bpf_map_update_elem(map_fd, &obj_fd, &val,
|
||||
BPF_NOEXIST | BPF_F_LOCK);
|
||||
if (CHECK(err < 0, "bpf_map_update_elem", "err:%d errno:%d\n", err,
|
||||
errno))
|
||||
return false;
|
||||
|
||||
/* Lookup the newly created element */
|
||||
err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val,
|
||||
BPF_F_LOCK);
|
||||
if (CHECK(err < 0, "bpf_map_lookup_elem", "err:%d errno:%d", err,
|
||||
errno))
|
||||
return false;
|
||||
|
||||
/* Check the value of the newly created element */
|
||||
if (CHECK(lookup_val.value != val.value, "bpf_map_lookup_elem",
|
||||
"value got = %x errno:%d", lookup_val.value, val.value))
|
||||
return false;
|
||||
|
||||
err = bpf_map_delete_elem(map_fd, &obj_fd);
|
||||
if (CHECK(err, "bpf_map_delete_elem()", "err:%d errno:%d\n", err,
|
||||
errno))
|
||||
return false;
|
||||
|
||||
/* The lookup should fail, now that the element has been deleted */
|
||||
err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val,
|
||||
BPF_F_LOCK);
|
||||
if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem",
|
||||
"err:%d errno:%d\n", err, errno))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void test_test_local_storage(void)
|
||||
{
|
||||
char tmp_exec_path[PATH_MAX] = "/tmp/copy_of_rmXXXXXX";
|
||||
int err, serv_sk = -1, task_fd = -1, rm_fd = -1;
|
||||
struct local_storage *skel = NULL;
|
||||
int err, duration = 0, serv_sk = -1;
|
||||
|
||||
skel = local_storage__open_and_load();
|
||||
if (CHECK(!skel, "skel_load", "lsm skeleton failed\n"))
|
||||
@@ -37,11 +172,45 @@ void test_test_local_storage(void)
|
||||
if (CHECK(err, "attach", "lsm attach failed: %d\n", err))
|
||||
goto close_prog;
|
||||
|
||||
task_fd = sys_pidfd_open(getpid(), 0);
|
||||
if (CHECK(task_fd < 0, "pidfd_open",
|
||||
"failed to get pidfd err:%d, errno:%d", task_fd, errno))
|
||||
goto close_prog;
|
||||
|
||||
if (!check_syscall_operations(bpf_map__fd(skel->maps.task_storage_map),
|
||||
task_fd))
|
||||
goto close_prog;
|
||||
|
||||
err = copy_rm(tmp_exec_path);
|
||||
if (CHECK(err < 0, "copy_rm", "err %d errno %d\n", err, errno))
|
||||
goto close_prog;
|
||||
|
||||
rm_fd = open(tmp_exec_path, O_RDONLY);
|
||||
if (CHECK(rm_fd < 0, "open", "failed to open %s err:%d, errno:%d",
|
||||
tmp_exec_path, rm_fd, errno))
|
||||
goto close_prog;
|
||||
|
||||
if (!check_syscall_operations(bpf_map__fd(skel->maps.inode_storage_map),
|
||||
rm_fd))
|
||||
goto close_prog;
|
||||
|
||||
/* Sets skel->bss->monitored_pid to the pid of the forked child
|
||||
* forks a child process that executes tmp_exec_path and tries to
|
||||
* unlink its executable. This operation should be denied by the loaded
|
||||
* LSM program.
|
||||
*/
|
||||
err = run_self_unlink(&skel->bss->monitored_pid, tmp_exec_path);
|
||||
if (CHECK(err != EPERM, "run_self_unlink", "err %d want EPERM\n", err))
|
||||
goto close_prog_unlink;
|
||||
|
||||
/* Set the process being monitored to be the current process */
|
||||
skel->bss->monitored_pid = getpid();
|
||||
|
||||
err = create_and_unlink_file();
|
||||
if (CHECK(err < 0, "exec_cmd", "err %d errno %d\n", err, errno))
|
||||
goto close_prog;
|
||||
/* Remove the temporary created executable */
|
||||
err = unlink(tmp_exec_path);
|
||||
if (CHECK(err != 0, "unlink", "unable to unlink %s: %d", tmp_exec_path,
|
||||
errno))
|
||||
goto close_prog_unlink;
|
||||
|
||||
CHECK(skel->data->inode_storage_result != 0, "inode_storage_result",
|
||||
"inode_local_storage not set\n");
|
||||
@@ -53,8 +222,15 @@ void test_test_local_storage(void)
|
||||
CHECK(skel->data->sk_storage_result != 0, "sk_storage_result",
|
||||
"sk_local_storage not set\n");
|
||||
|
||||
close(serv_sk);
|
||||
if (!check_syscall_operations(bpf_map__fd(skel->maps.sk_storage_map),
|
||||
serv_sk))
|
||||
goto close_prog;
|
||||
|
||||
close_prog_unlink:
|
||||
unlink(tmp_exec_path);
|
||||
close_prog:
|
||||
close(serv_sk);
|
||||
close(rm_fd);
|
||||
close(task_fd);
|
||||
local_storage__destroy(skel);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
#include <test_progs.h>
|
||||
#include <network_helpers.h>
|
||||
#include "skb_pkt_end.skel.h"
|
||||
|
||||
static int sanity_run(struct bpf_program *prog)
|
||||
{
|
||||
__u32 duration, retval;
|
||||
int err, prog_fd;
|
||||
|
||||
prog_fd = bpf_program__fd(prog);
|
||||
err = bpf_prog_test_run(prog_fd, 1, &pkt_v4, sizeof(pkt_v4),
|
||||
NULL, NULL, &retval, &duration);
|
||||
if (CHECK(err || retval != 123, "test_run",
|
||||
"err %d errno %d retval %d duration %d\n",
|
||||
err, errno, retval, duration))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_test_skb_pkt_end(void)
|
||||
{
|
||||
struct skb_pkt_end *skb_pkt_end_skel = NULL;
|
||||
__u32 duration = 0;
|
||||
int err;
|
||||
|
||||
skb_pkt_end_skel = skb_pkt_end__open_and_load();
|
||||
if (CHECK(!skb_pkt_end_skel, "skb_pkt_end_skel_load", "skb_pkt_end skeleton failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
err = skb_pkt_end__attach(skb_pkt_end_skel);
|
||||
if (CHECK(err, "skb_pkt_end_attach", "skb_pkt_end attach failed: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
if (sanity_run(skb_pkt_end_skel->progs.main_prog))
|
||||
goto cleanup;
|
||||
|
||||
cleanup:
|
||||
skb_pkt_end__destroy(skb_pkt_end_skel);
|
||||
}
|
||||
@@ -4,9 +4,8 @@
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <errno.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <stdbool.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
@@ -18,60 +17,68 @@ int monitored_pid = 0;
|
||||
int inode_storage_result = -1;
|
||||
int sk_storage_result = -1;
|
||||
|
||||
struct dummy_storage {
|
||||
struct local_storage {
|
||||
struct inode *exec_inode;
|
||||
__u32 value;
|
||||
struct bpf_spin_lock lock;
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_INODE_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, struct dummy_storage);
|
||||
__type(value, struct local_storage);
|
||||
} inode_storage_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_CLONE);
|
||||
__type(key, int);
|
||||
__type(value, struct dummy_storage);
|
||||
__type(value, struct local_storage);
|
||||
} sk_storage_map SEC(".maps");
|
||||
|
||||
/* TODO Use vmlinux.h once BTF pruning for embedded types is fixed.
|
||||
*/
|
||||
struct sock {} __attribute__((preserve_access_index));
|
||||
struct sockaddr {} __attribute__((preserve_access_index));
|
||||
struct socket {
|
||||
struct sock *sk;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct inode {} __attribute__((preserve_access_index));
|
||||
struct dentry {
|
||||
struct inode *d_inode;
|
||||
} __attribute__((preserve_access_index));
|
||||
struct file {
|
||||
struct inode *f_inode;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_TASK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, struct local_storage);
|
||||
} task_storage_map SEC(".maps");
|
||||
|
||||
SEC("lsm/inode_unlink")
|
||||
int BPF_PROG(unlink_hook, struct inode *dir, struct dentry *victim)
|
||||
{
|
||||
__u32 pid = bpf_get_current_pid_tgid() >> 32;
|
||||
struct dummy_storage *storage;
|
||||
struct local_storage *storage;
|
||||
bool is_self_unlink;
|
||||
int err;
|
||||
|
||||
if (pid != monitored_pid)
|
||||
return 0;
|
||||
|
||||
storage = bpf_task_storage_get(&task_storage_map,
|
||||
bpf_get_current_task_btf(), 0, 0);
|
||||
if (storage) {
|
||||
/* Don't let an executable delete itself */
|
||||
bpf_spin_lock(&storage->lock);
|
||||
is_self_unlink = storage->exec_inode == victim->d_inode;
|
||||
bpf_spin_unlock(&storage->lock);
|
||||
if (is_self_unlink)
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
storage = bpf_inode_storage_get(&inode_storage_map, victim->d_inode, 0,
|
||||
BPF_SK_STORAGE_GET_F_CREATE);
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (!storage)
|
||||
return 0;
|
||||
|
||||
if (storage->value == DUMMY_STORAGE_VALUE)
|
||||
bpf_spin_lock(&storage->lock);
|
||||
if (storage->value != DUMMY_STORAGE_VALUE)
|
||||
inode_storage_result = -1;
|
||||
bpf_spin_unlock(&storage->lock);
|
||||
|
||||
inode_storage_result =
|
||||
bpf_inode_storage_delete(&inode_storage_map, victim->d_inode);
|
||||
err = bpf_inode_storage_delete(&inode_storage_map, victim->d_inode);
|
||||
if (!err)
|
||||
inode_storage_result = err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -81,20 +88,26 @@ int BPF_PROG(socket_bind, struct socket *sock, struct sockaddr *address,
|
||||
int addrlen)
|
||||
{
|
||||
__u32 pid = bpf_get_current_pid_tgid() >> 32;
|
||||
struct dummy_storage *storage;
|
||||
struct local_storage *storage;
|
||||
int err;
|
||||
|
||||
if (pid != monitored_pid)
|
||||
return 0;
|
||||
|
||||
storage = bpf_sk_storage_get(&sk_storage_map, sock->sk, 0,
|
||||
BPF_SK_STORAGE_GET_F_CREATE);
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (!storage)
|
||||
return 0;
|
||||
|
||||
if (storage->value == DUMMY_STORAGE_VALUE)
|
||||
bpf_spin_lock(&storage->lock);
|
||||
if (storage->value != DUMMY_STORAGE_VALUE)
|
||||
sk_storage_result = -1;
|
||||
bpf_spin_unlock(&storage->lock);
|
||||
|
||||
err = bpf_sk_storage_delete(&sk_storage_map, sock->sk);
|
||||
if (!err)
|
||||
sk_storage_result = err;
|
||||
|
||||
sk_storage_result = bpf_sk_storage_delete(&sk_storage_map, sock->sk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -103,17 +116,19 @@ int BPF_PROG(socket_post_create, struct socket *sock, int family, int type,
|
||||
int protocol, int kern)
|
||||
{
|
||||
__u32 pid = bpf_get_current_pid_tgid() >> 32;
|
||||
struct dummy_storage *storage;
|
||||
struct local_storage *storage;
|
||||
|
||||
if (pid != monitored_pid)
|
||||
return 0;
|
||||
|
||||
storage = bpf_sk_storage_get(&sk_storage_map, sock->sk, 0,
|
||||
BPF_SK_STORAGE_GET_F_CREATE);
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (!storage)
|
||||
return 0;
|
||||
|
||||
bpf_spin_lock(&storage->lock);
|
||||
storage->value = DUMMY_STORAGE_VALUE;
|
||||
bpf_spin_unlock(&storage->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -122,7 +137,7 @@ SEC("lsm/file_open")
|
||||
int BPF_PROG(file_open, struct file *file)
|
||||
{
|
||||
__u32 pid = bpf_get_current_pid_tgid() >> 32;
|
||||
struct dummy_storage *storage;
|
||||
struct local_storage *storage;
|
||||
|
||||
if (pid != monitored_pid)
|
||||
return 0;
|
||||
@@ -131,10 +146,30 @@ int BPF_PROG(file_open, struct file *file)
|
||||
return 0;
|
||||
|
||||
storage = bpf_inode_storage_get(&inode_storage_map, file->f_inode, 0,
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (!storage)
|
||||
return 0;
|
||||
|
||||
bpf_spin_lock(&storage->lock);
|
||||
storage->value = DUMMY_STORAGE_VALUE;
|
||||
bpf_spin_unlock(&storage->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This uses the local storage to remember the inode of the binary that a
|
||||
* process was originally executing.
|
||||
*/
|
||||
SEC("lsm/bprm_committed_creds")
|
||||
void BPF_PROG(exec, struct linux_binprm *bprm)
|
||||
{
|
||||
struct local_storage *storage;
|
||||
|
||||
storage = bpf_task_storage_get(&task_storage_map,
|
||||
bpf_get_current_task_btf(), 0,
|
||||
BPF_LOCAL_STORAGE_GET_F_CREATE);
|
||||
if (storage) {
|
||||
bpf_spin_lock(&storage->lock);
|
||||
storage->exec_inode = bprm->file->f_inode;
|
||||
bpf_spin_unlock(&storage->lock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define BPF_NO_PRESERVE_ACCESS_INDEX
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
#define NULL 0
|
||||
#define INLINE __always_inline
|
||||
|
||||
#define skb_shorter(skb, len) ((void *)(long)(skb)->data + (len) > (void *)(long)skb->data_end)
|
||||
|
||||
#define ETH_IPV4_TCP_SIZE (14 + sizeof(struct iphdr) + sizeof(struct tcphdr))
|
||||
|
||||
static INLINE struct iphdr *get_iphdr(struct __sk_buff *skb)
|
||||
{
|
||||
struct iphdr *ip = NULL;
|
||||
struct ethhdr *eth;
|
||||
|
||||
if (skb_shorter(skb, ETH_IPV4_TCP_SIZE))
|
||||
goto out;
|
||||
|
||||
eth = (void *)(long)skb->data;
|
||||
ip = (void *)(eth + 1);
|
||||
|
||||
out:
|
||||
return ip;
|
||||
}
|
||||
|
||||
SEC("classifier/cls")
|
||||
int main_prog(struct __sk_buff *skb)
|
||||
{
|
||||
struct iphdr *ip = NULL;
|
||||
struct tcphdr *tcp;
|
||||
__u8 proto = 0;
|
||||
|
||||
if (!(ip = get_iphdr(skb)))
|
||||
goto out;
|
||||
|
||||
proto = ip->protocol;
|
||||
|
||||
if (proto != IPPROTO_TCP)
|
||||
goto out;
|
||||
|
||||
tcp = (void*)(ip + 1);
|
||||
if (tcp->dest != 0)
|
||||
goto out;
|
||||
if (!tcp)
|
||||
goto out;
|
||||
|
||||
return tcp->urg_ptr;
|
||||
out:
|
||||
return -1;
|
||||
}
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 2);
|
||||
__type(key, struct bigelement);
|
||||
__type(value, __u32);
|
||||
} hash_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, __u32);
|
||||
__type(value, struct bigelement);
|
||||
} key_map SEC(".maps");
|
||||
|
||||
struct bigelement {
|
||||
int a;
|
||||
char b[4096];
|
||||
long long c;
|
||||
};
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int bpf_hash_large_key_test(void *ctx)
|
||||
{
|
||||
int zero = 0, err = 1, value = 42;
|
||||
struct bigelement *key;
|
||||
|
||||
key = bpf_map_lookup_elem(&key_map, &zero);
|
||||
if (!key)
|
||||
return 0;
|
||||
|
||||
key->c = 1;
|
||||
if (bpf_map_update_elem(&hash_map, key, &value, BPF_ANY))
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, int);
|
||||
} sk_stg_map SEC(".maps");
|
||||
|
||||
SEC("fentry/bpf_sk_storage_free")
|
||||
int BPF_PROG(trace_bpf_sk_storage_free, struct sock *sk)
|
||||
{
|
||||
int *value;
|
||||
|
||||
value = bpf_sk_storage_get(&sk_stg_map, sk, 0,
|
||||
BPF_SK_STORAGE_GET_F_CREATE);
|
||||
|
||||
if (value)
|
||||
*value = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
struct sk_stg {
|
||||
__u32 pid;
|
||||
__u32 last_notclose_state;
|
||||
char comm[16];
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, struct sk_stg);
|
||||
} sk_stg_map SEC(".maps");
|
||||
|
||||
/* Testing delete */
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||
__type(key, int);
|
||||
__type(value, int);
|
||||
} del_sk_stg_map SEC(".maps");
|
||||
|
||||
char task_comm[16] = "";
|
||||
|
||||
SEC("tp_btf/inet_sock_set_state")
|
||||
int BPF_PROG(trace_inet_sock_set_state, struct sock *sk, int oldstate,
|
||||
int newstate)
|
||||
{
|
||||
struct sk_stg *stg;
|
||||
|
||||
if (newstate == BPF_TCP_CLOSE)
|
||||
return 0;
|
||||
|
||||
stg = bpf_sk_storage_get(&sk_stg_map, sk, 0,
|
||||
BPF_SK_STORAGE_GET_F_CREATE);
|
||||
if (!stg)
|
||||
return 0;
|
||||
|
||||
stg->last_notclose_state = newstate;
|
||||
|
||||
bpf_sk_storage_delete(&del_sk_stg_map, sk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_task_info(struct sock *sk)
|
||||
{
|
||||
struct task_struct *task;
|
||||
struct sk_stg *stg;
|
||||
|
||||
stg = bpf_sk_storage_get(&sk_stg_map, sk, 0,
|
||||
BPF_SK_STORAGE_GET_F_CREATE);
|
||||
if (!stg)
|
||||
return;
|
||||
|
||||
stg->pid = bpf_get_current_pid_tgid();
|
||||
|
||||
task = (struct task_struct *)bpf_get_current_task();
|
||||
bpf_core_read_str(&stg->comm, sizeof(stg->comm), &task->comm);
|
||||
bpf_core_read_str(&task_comm, sizeof(task_comm), &task->comm);
|
||||
}
|
||||
|
||||
SEC("fentry/inet_csk_listen_start")
|
||||
int BPF_PROG(trace_inet_csk_listen_start, struct sock *sk, int backlog)
|
||||
{
|
||||
set_task_info(sk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("fentry/tcp_connect")
|
||||
int BPF_PROG(trace_tcp_connect, struct sock *sk)
|
||||
{
|
||||
set_task_info(sk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("fexit/inet_csk_accept")
|
||||
int BPF_PROG(inet_csk_accept, struct sock *sk, int flags, int *err, bool kern,
|
||||
struct sock *accepted_sk)
|
||||
{
|
||||
set_task_info(accepted_sk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
@@ -14,40 +14,7 @@
|
||||
#include <bpf/bpf_endian.h>
|
||||
#include "test_tcpbpf.h"
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 4);
|
||||
__type(key, __u32);
|
||||
__type(value, struct tcpbpf_globals);
|
||||
} global_map SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 2);
|
||||
__type(key, __u32);
|
||||
__type(value, int);
|
||||
} sockopt_results SEC(".maps");
|
||||
|
||||
static inline void update_event_map(int event)
|
||||
{
|
||||
__u32 key = 0;
|
||||
struct tcpbpf_globals g, *gp;
|
||||
|
||||
gp = bpf_map_lookup_elem(&global_map, &key);
|
||||
if (gp == NULL) {
|
||||
struct tcpbpf_globals g = {0};
|
||||
|
||||
g.event_map |= (1 << event);
|
||||
bpf_map_update_elem(&global_map, &key, &g,
|
||||
BPF_ANY);
|
||||
} else {
|
||||
g = *gp;
|
||||
g.event_map |= (1 << event);
|
||||
bpf_map_update_elem(&global_map, &key, &g,
|
||||
BPF_ANY);
|
||||
}
|
||||
}
|
||||
|
||||
struct tcpbpf_globals global = {};
|
||||
int _version SEC("version") = 1;
|
||||
|
||||
SEC("sockops")
|
||||
@@ -105,29 +72,15 @@ int bpf_testcb(struct bpf_sock_ops *skops)
|
||||
|
||||
op = (int) skops->op;
|
||||
|
||||
update_event_map(op);
|
||||
global.event_map |= (1 << op);
|
||||
|
||||
switch (op) {
|
||||
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
|
||||
/* Test failure to set largest cb flag (assumes not defined) */
|
||||
bad_call_rv = bpf_sock_ops_cb_flags_set(skops, 0x80);
|
||||
global.bad_cb_test_rv = bpf_sock_ops_cb_flags_set(skops, 0x80);
|
||||
/* Set callback */
|
||||
good_call_rv = bpf_sock_ops_cb_flags_set(skops,
|
||||
global.good_cb_test_rv = bpf_sock_ops_cb_flags_set(skops,
|
||||
BPF_SOCK_OPS_STATE_CB_FLAG);
|
||||
/* Update results */
|
||||
{
|
||||
__u32 key = 0;
|
||||
struct tcpbpf_globals g, *gp;
|
||||
|
||||
gp = bpf_map_lookup_elem(&global_map, &key);
|
||||
if (!gp)
|
||||
break;
|
||||
g = *gp;
|
||||
g.bad_cb_test_rv = bad_call_rv;
|
||||
g.good_cb_test_rv = good_call_rv;
|
||||
bpf_map_update_elem(&global_map, &key, &g,
|
||||
BPF_ANY);
|
||||
}
|
||||
break;
|
||||
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
|
||||
skops->sk_txhash = 0x12345f;
|
||||
@@ -143,10 +96,8 @@ int bpf_testcb(struct bpf_sock_ops *skops)
|
||||
|
||||
thdr = (struct tcphdr *)(header + offset);
|
||||
v = thdr->syn;
|
||||
__u32 key = 1;
|
||||
|
||||
bpf_map_update_elem(&sockopt_results, &key, &v,
|
||||
BPF_ANY);
|
||||
global.tcp_saved_syn = v;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -156,25 +107,16 @@ int bpf_testcb(struct bpf_sock_ops *skops)
|
||||
break;
|
||||
case BPF_SOCK_OPS_STATE_CB:
|
||||
if (skops->args[1] == BPF_TCP_CLOSE) {
|
||||
__u32 key = 0;
|
||||
struct tcpbpf_globals g, *gp;
|
||||
|
||||
gp = bpf_map_lookup_elem(&global_map, &key);
|
||||
if (!gp)
|
||||
break;
|
||||
g = *gp;
|
||||
if (skops->args[0] == BPF_TCP_LISTEN) {
|
||||
g.num_listen++;
|
||||
global.num_listen++;
|
||||
} else {
|
||||
g.total_retrans = skops->total_retrans;
|
||||
g.data_segs_in = skops->data_segs_in;
|
||||
g.data_segs_out = skops->data_segs_out;
|
||||
g.bytes_received = skops->bytes_received;
|
||||
g.bytes_acked = skops->bytes_acked;
|
||||
global.total_retrans = skops->total_retrans;
|
||||
global.data_segs_in = skops->data_segs_in;
|
||||
global.data_segs_out = skops->data_segs_out;
|
||||
global.bytes_received = skops->bytes_received;
|
||||
global.bytes_acked = skops->bytes_acked;
|
||||
}
|
||||
g.num_close_events++;
|
||||
bpf_map_update_elem(&global_map, &key, &g,
|
||||
BPF_ANY);
|
||||
global.num_close_events++;
|
||||
}
|
||||
break;
|
||||
case BPF_SOCK_OPS_TCP_LISTEN_CB:
|
||||
@@ -182,9 +124,7 @@ int bpf_testcb(struct bpf_sock_ops *skops)
|
||||
v = bpf_setsockopt(skops, IPPROTO_TCP, TCP_SAVE_SYN,
|
||||
&save_syn, sizeof(save_syn));
|
||||
/* Update global map w/ result of setsock opt */
|
||||
__u32 key = 0;
|
||||
|
||||
bpf_map_update_elem(&sockopt_results, &key, &v, BPF_ANY);
|
||||
global.tcp_save_syn = v;
|
||||
break;
|
||||
default:
|
||||
rv = -1;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/pkt_cls.h>
|
||||
#include <linux/erspan.h>
|
||||
@@ -528,12 +527,11 @@ int _ipip_set_tunnel(struct __sk_buff *skb)
|
||||
struct bpf_tunnel_key key = {};
|
||||
void *data = (void *)(long)skb->data;
|
||||
struct iphdr *iph = data;
|
||||
struct tcphdr *tcp = data + sizeof(*iph);
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
int ret;
|
||||
|
||||
/* single length check */
|
||||
if (data + sizeof(*iph) + sizeof(*tcp) > data_end) {
|
||||
if (data + sizeof(*iph) > data_end) {
|
||||
ERROR(1);
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
@@ -541,16 +539,6 @@ int _ipip_set_tunnel(struct __sk_buff *skb)
|
||||
key.tunnel_ttl = 64;
|
||||
if (iph->protocol == IPPROTO_ICMP) {
|
||||
key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
|
||||
} else {
|
||||
if (iph->protocol != IPPROTO_TCP || iph->ihl != 5)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (tcp->dest == bpf_htons(5200))
|
||||
key.remote_ipv4 = 0xac100164; /* 172.16.1.100 */
|
||||
else if (tcp->dest == bpf_htons(5201))
|
||||
key.remote_ipv4 = 0xac100165; /* 172.16.1.101 */
|
||||
else
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key), 0);
|
||||
@@ -585,19 +573,20 @@ int _ipip6_set_tunnel(struct __sk_buff *skb)
|
||||
struct bpf_tunnel_key key = {};
|
||||
void *data = (void *)(long)skb->data;
|
||||
struct iphdr *iph = data;
|
||||
struct tcphdr *tcp = data + sizeof(*iph);
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
int ret;
|
||||
|
||||
/* single length check */
|
||||
if (data + sizeof(*iph) + sizeof(*tcp) > data_end) {
|
||||
if (data + sizeof(*iph) > data_end) {
|
||||
ERROR(1);
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
__builtin_memset(&key, 0x0, sizeof(key));
|
||||
key.remote_ipv6[3] = bpf_htonl(0x11); /* ::11 */
|
||||
key.tunnel_ttl = 64;
|
||||
if (iph->protocol == IPPROTO_ICMP) {
|
||||
key.remote_ipv6[3] = bpf_htonl(0x11); /* ::11 */
|
||||
}
|
||||
|
||||
ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
|
||||
BPF_F_TUNINFO_IPV6);
|
||||
@@ -634,35 +623,18 @@ int _ip6ip6_set_tunnel(struct __sk_buff *skb)
|
||||
struct bpf_tunnel_key key = {};
|
||||
void *data = (void *)(long)skb->data;
|
||||
struct ipv6hdr *iph = data;
|
||||
struct tcphdr *tcp = data + sizeof(*iph);
|
||||
void *data_end = (void *)(long)skb->data_end;
|
||||
int ret;
|
||||
|
||||
/* single length check */
|
||||
if (data + sizeof(*iph) + sizeof(*tcp) > data_end) {
|
||||
if (data + sizeof(*iph) > data_end) {
|
||||
ERROR(1);
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
key.remote_ipv6[0] = bpf_htonl(0x2401db00);
|
||||
key.tunnel_ttl = 64;
|
||||
|
||||
if (iph->nexthdr == 58 /* NEXTHDR_ICMP */) {
|
||||
key.remote_ipv6[3] = bpf_htonl(1);
|
||||
} else {
|
||||
if (iph->nexthdr != 6 /* NEXTHDR_TCP */) {
|
||||
ERROR(iph->nexthdr);
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
if (tcp->dest == bpf_htons(5200)) {
|
||||
key.remote_ipv6[3] = bpf_htonl(1);
|
||||
} else if (tcp->dest == bpf_htons(5201)) {
|
||||
key.remote_ipv6[3] = bpf_htonl(2);
|
||||
} else {
|
||||
ERROR(tcp->dest);
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
key.remote_ipv6[3] = bpf_htonl(0x11); /* ::11 */
|
||||
}
|
||||
|
||||
ret = bpf_skb_set_tunnel_key(skb, &key, sizeof(key),
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
|
||||
import sys, os, os.path, getopt
|
||||
import socket, time
|
||||
import subprocess
|
||||
import select
|
||||
|
||||
def read(sock, n):
|
||||
buf = b''
|
||||
while len(buf) < n:
|
||||
rem = n - len(buf)
|
||||
try: s = sock.recv(rem)
|
||||
except (socket.error) as e: return b''
|
||||
buf += s
|
||||
return buf
|
||||
|
||||
def send(sock, s):
|
||||
total = len(s)
|
||||
count = 0
|
||||
while count < total:
|
||||
try: n = sock.send(s)
|
||||
except (socket.error) as e: n = 0
|
||||
if n == 0:
|
||||
return count;
|
||||
count += n
|
||||
return count
|
||||
|
||||
|
||||
serverPort = int(sys.argv[1])
|
||||
|
||||
# create active socket
|
||||
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
try:
|
||||
sock.connect(('::1', serverPort))
|
||||
except socket.error as e:
|
||||
sys.exit(1)
|
||||
|
||||
buf = b''
|
||||
n = 0
|
||||
while n < 1000:
|
||||
buf += b'+'
|
||||
n += 1
|
||||
|
||||
sock.settimeout(1);
|
||||
n = send(sock, buf)
|
||||
n = read(sock, 500)
|
||||
sys.exit(0)
|
||||
@@ -1,80 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
|
||||
import sys, os, os.path, getopt
|
||||
import socket, time
|
||||
import subprocess
|
||||
import select
|
||||
|
||||
def read(sock, n):
|
||||
buf = b''
|
||||
while len(buf) < n:
|
||||
rem = n - len(buf)
|
||||
try: s = sock.recv(rem)
|
||||
except (socket.error) as e: return b''
|
||||
buf += s
|
||||
return buf
|
||||
|
||||
def send(sock, s):
|
||||
total = len(s)
|
||||
count = 0
|
||||
while count < total:
|
||||
try: n = sock.send(s)
|
||||
except (socket.error) as e: n = 0
|
||||
if n == 0:
|
||||
return count;
|
||||
count += n
|
||||
return count
|
||||
|
||||
|
||||
SERVER_PORT = 12877
|
||||
MAX_PORTS = 2
|
||||
|
||||
serverPort = SERVER_PORT
|
||||
serverSocket = None
|
||||
|
||||
# create passive socket
|
||||
serverSocket = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
|
||||
try: serverSocket.bind(('::1', 0))
|
||||
except socket.error as msg:
|
||||
print('bind fails: ' + str(msg))
|
||||
|
||||
sn = serverSocket.getsockname()
|
||||
serverPort = sn[1]
|
||||
|
||||
cmdStr = ("./tcp_client.py %d &") % (serverPort)
|
||||
os.system(cmdStr)
|
||||
|
||||
buf = b''
|
||||
n = 0
|
||||
while n < 500:
|
||||
buf += b'.'
|
||||
n += 1
|
||||
|
||||
serverSocket.listen(MAX_PORTS)
|
||||
readList = [serverSocket]
|
||||
|
||||
while True:
|
||||
readyRead, readyWrite, inError = \
|
||||
select.select(readList, [], [], 2)
|
||||
|
||||
if len(readyRead) > 0:
|
||||
waitCount = 0
|
||||
for sock in readyRead:
|
||||
if sock == serverSocket:
|
||||
(clientSocket, address) = serverSocket.accept()
|
||||
address = str(address[0])
|
||||
readList.append(clientSocket)
|
||||
else:
|
||||
sock.settimeout(1);
|
||||
s = read(sock, 1000)
|
||||
n = send(sock, buf)
|
||||
sock.close()
|
||||
serverSocket.close()
|
||||
sys.exit(0)
|
||||
else:
|
||||
print('Select timeout!')
|
||||
sys.exit(1)
|
||||
@@ -1223,9 +1223,10 @@ out_map_in_map:
|
||||
|
||||
static void test_map_large(void)
|
||||
{
|
||||
|
||||
struct bigkey {
|
||||
int a;
|
||||
char b[116];
|
||||
char b[4096];
|
||||
long long c;
|
||||
} key;
|
||||
int fd, i, value;
|
||||
|
||||
@@ -141,6 +141,17 @@ extern int test__join_cgroup(const char *path);
|
||||
___ok; \
|
||||
})
|
||||
|
||||
#define ASSERT_NEQ(actual, expected, name) ({ \
|
||||
static int duration = 0; \
|
||||
typeof(actual) ___act = (actual); \
|
||||
typeof(expected) ___exp = (expected); \
|
||||
bool ___ok = ___act != ___exp; \
|
||||
CHECK(!___ok, (name), \
|
||||
"unexpected %s: actual %lld == expected %lld\n", \
|
||||
(name), (long long)(___act), (long long)(___exp)); \
|
||||
___ok; \
|
||||
})
|
||||
|
||||
#define ASSERT_STREQ(actual, expected, name) ({ \
|
||||
static int duration = 0; \
|
||||
const char *___act = actual; \
|
||||
|
||||
@@ -14,5 +14,7 @@ struct tcpbpf_globals {
|
||||
__u64 bytes_acked;
|
||||
__u32 num_listen;
|
||||
__u32 num_close_events;
|
||||
__u32 tcp_save_syn;
|
||||
__u32 tcp_saved_syn;
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <sys/types.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
#include "bpf_rlimit.h"
|
||||
#include "bpf_util.h"
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
#include "test_tcpbpf.h"
|
||||
|
||||
/* 3 comes from one listening socket + both ends of the connection */
|
||||
#define EXPECTED_CLOSE_EVENTS 3
|
||||
|
||||
#define EXPECT_EQ(expected, actual, fmt) \
|
||||
do { \
|
||||
if ((expected) != (actual)) { \
|
||||
printf(" Value of: " #actual "\n" \
|
||||
" Actual: %" fmt "\n" \
|
||||
" Expected: %" fmt "\n", \
|
||||
(actual), (expected)); \
|
||||
ret--; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
int verify_result(const struct tcpbpf_globals *result)
|
||||
{
|
||||
__u32 expected_events;
|
||||
int ret = 0;
|
||||
|
||||
expected_events = ((1 << BPF_SOCK_OPS_TIMEOUT_INIT) |
|
||||
(1 << BPF_SOCK_OPS_RWND_INIT) |
|
||||
(1 << BPF_SOCK_OPS_TCP_CONNECT_CB) |
|
||||
(1 << BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB) |
|
||||
(1 << BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB) |
|
||||
(1 << BPF_SOCK_OPS_NEEDS_ECN) |
|
||||
(1 << BPF_SOCK_OPS_STATE_CB) |
|
||||
(1 << BPF_SOCK_OPS_TCP_LISTEN_CB));
|
||||
|
||||
EXPECT_EQ(expected_events, result->event_map, "#" PRIx32);
|
||||
EXPECT_EQ(501ULL, result->bytes_received, "llu");
|
||||
EXPECT_EQ(1002ULL, result->bytes_acked, "llu");
|
||||
EXPECT_EQ(1, result->data_segs_in, PRIu32);
|
||||
EXPECT_EQ(1, result->data_segs_out, PRIu32);
|
||||
EXPECT_EQ(0x80, result->bad_cb_test_rv, PRIu32);
|
||||
EXPECT_EQ(0, result->good_cb_test_rv, PRIu32);
|
||||
EXPECT_EQ(1, result->num_listen, PRIu32);
|
||||
EXPECT_EQ(EXPECTED_CLOSE_EVENTS, result->num_close_events, PRIu32);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int verify_sockopt_result(int sock_map_fd)
|
||||
{
|
||||
__u32 key = 0;
|
||||
int ret = 0;
|
||||
int res;
|
||||
int rv;
|
||||
|
||||
/* check setsockopt for SAVE_SYN */
|
||||
rv = bpf_map_lookup_elem(sock_map_fd, &key, &res);
|
||||
EXPECT_EQ(0, rv, "d");
|
||||
EXPECT_EQ(0, res, "d");
|
||||
key = 1;
|
||||
/* check getsockopt for SAVED_SYN */
|
||||
rv = bpf_map_lookup_elem(sock_map_fd, &key, &res);
|
||||
EXPECT_EQ(0, rv, "d");
|
||||
EXPECT_EQ(1, res, "d");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bpf_find_map(const char *test, struct bpf_object *obj,
|
||||
const char *name)
|
||||
{
|
||||
struct bpf_map *map;
|
||||
|
||||
map = bpf_object__find_map_by_name(obj, name);
|
||||
if (!map) {
|
||||
printf("%s:FAIL:map '%s' not found\n", test, name);
|
||||
return -1;
|
||||
}
|
||||
return bpf_map__fd(map);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *file = "test_tcpbpf_kern.o";
|
||||
int prog_fd, map_fd, sock_map_fd;
|
||||
struct tcpbpf_globals g = {0};
|
||||
const char *cg_path = "/foo";
|
||||
int error = EXIT_FAILURE;
|
||||
struct bpf_object *obj;
|
||||
int cg_fd = -1;
|
||||
int retry = 10;
|
||||
__u32 key = 0;
|
||||
int rv;
|
||||
|
||||
cg_fd = cgroup_setup_and_join(cg_path);
|
||||
if (cg_fd < 0)
|
||||
goto err;
|
||||
|
||||
if (bpf_prog_load(file, BPF_PROG_TYPE_SOCK_OPS, &obj, &prog_fd)) {
|
||||
printf("FAILED: load_bpf_file failed for: %s\n", file);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rv = bpf_prog_attach(prog_fd, cg_fd, BPF_CGROUP_SOCK_OPS, 0);
|
||||
if (rv) {
|
||||
printf("FAILED: bpf_prog_attach: %d (%s)\n",
|
||||
error, strerror(errno));
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (system("./tcp_server.py")) {
|
||||
printf("FAILED: TCP server\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
map_fd = bpf_find_map(__func__, obj, "global_map");
|
||||
if (map_fd < 0)
|
||||
goto err;
|
||||
|
||||
sock_map_fd = bpf_find_map(__func__, obj, "sockopt_results");
|
||||
if (sock_map_fd < 0)
|
||||
goto err;
|
||||
|
||||
retry_lookup:
|
||||
rv = bpf_map_lookup_elem(map_fd, &key, &g);
|
||||
if (rv != 0) {
|
||||
printf("FAILED: bpf_map_lookup_elem returns %d\n", rv);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (g.num_close_events != EXPECTED_CLOSE_EVENTS && retry--) {
|
||||
printf("Unexpected number of close events (%d), retrying!\n",
|
||||
g.num_close_events);
|
||||
usleep(100);
|
||||
goto retry_lookup;
|
||||
}
|
||||
|
||||
if (verify_result(&g)) {
|
||||
printf("FAILED: Wrong stats\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (verify_sockopt_result(sock_map_fd)) {
|
||||
printf("FAILED: Wrong sockopt stats\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
printf("PASSED!\n");
|
||||
error = 0;
|
||||
err:
|
||||
bpf_prog_detach(cg_fd, BPF_CGROUP_SOCK_OPS);
|
||||
close(cg_fd);
|
||||
cleanup_cgroup_environment();
|
||||
return error;
|
||||
}
|
||||
@@ -24,12 +24,12 @@
|
||||
# Root namespace with metadata-mode tunnel + BPF
|
||||
# Device names and addresses:
|
||||
# veth1 IP: 172.16.1.200, IPv6: 00::22 (underlay)
|
||||
# tunnel dev <type>11, ex: gre11, IPv4: 10.1.1.200 (overlay)
|
||||
# tunnel dev <type>11, ex: gre11, IPv4: 10.1.1.200, IPv6: 1::22 (overlay)
|
||||
#
|
||||
# Namespace at_ns0 with native tunnel
|
||||
# Device names and addresses:
|
||||
# veth0 IPv4: 172.16.1.100, IPv6: 00::11 (underlay)
|
||||
# tunnel dev <type>00, ex: gre00, IPv4: 10.1.1.100 (overlay)
|
||||
# tunnel dev <type>00, ex: gre00, IPv4: 10.1.1.100, IPv6: 1::11 (overlay)
|
||||
#
|
||||
#
|
||||
# End-to-end ping packet flow
|
||||
@@ -250,7 +250,7 @@ add_ipip_tunnel()
|
||||
ip addr add dev $DEV 10.1.1.200/24
|
||||
}
|
||||
|
||||
add_ipip6tnl_tunnel()
|
||||
add_ip6tnl_tunnel()
|
||||
{
|
||||
ip netns exec at_ns0 ip addr add ::11/96 dev veth0
|
||||
ip netns exec at_ns0 ip link set dev veth0 up
|
||||
@@ -262,11 +262,13 @@ add_ipip6tnl_tunnel()
|
||||
ip link add dev $DEV_NS type $TYPE \
|
||||
local ::11 remote ::22
|
||||
ip netns exec at_ns0 ip addr add dev $DEV_NS 10.1.1.100/24
|
||||
ip netns exec at_ns0 ip addr add dev $DEV_NS 1::11/96
|
||||
ip netns exec at_ns0 ip link set dev $DEV_NS up
|
||||
|
||||
# root namespace
|
||||
ip link add dev $DEV type $TYPE external
|
||||
ip addr add dev $DEV 10.1.1.200/24
|
||||
ip addr add dev $DEV 1::22/96
|
||||
ip link set dev $DEV up
|
||||
}
|
||||
|
||||
@@ -534,7 +536,7 @@ test_ipip6()
|
||||
|
||||
check $TYPE
|
||||
config_device
|
||||
add_ipip6tnl_tunnel
|
||||
add_ip6tnl_tunnel
|
||||
ip link set dev veth1 mtu 1500
|
||||
attach_bpf $DEV ipip6_set_tunnel ipip6_get_tunnel
|
||||
# underlay
|
||||
@@ -553,6 +555,34 @@ test_ipip6()
|
||||
echo -e ${GREEN}"PASS: $TYPE"${NC}
|
||||
}
|
||||
|
||||
test_ip6ip6()
|
||||
{
|
||||
TYPE=ip6tnl
|
||||
DEV_NS=ip6ip6tnl00
|
||||
DEV=ip6ip6tnl11
|
||||
ret=0
|
||||
|
||||
check $TYPE
|
||||
config_device
|
||||
add_ip6tnl_tunnel
|
||||
ip link set dev veth1 mtu 1500
|
||||
attach_bpf $DEV ip6ip6_set_tunnel ip6ip6_get_tunnel
|
||||
# underlay
|
||||
ping6 $PING_ARG ::11
|
||||
# ip6 over ip6
|
||||
ping6 $PING_ARG 1::11
|
||||
check_err $?
|
||||
ip netns exec at_ns0 ping6 $PING_ARG 1::22
|
||||
check_err $?
|
||||
cleanup
|
||||
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo -e ${RED}"FAIL: ip6$TYPE"${NC}
|
||||
return 1
|
||||
fi
|
||||
echo -e ${GREEN}"PASS: ip6$TYPE"${NC}
|
||||
}
|
||||
|
||||
setup_xfrm_tunnel()
|
||||
{
|
||||
auth=0x$(printf '1%.0s' {1..40})
|
||||
@@ -646,6 +676,7 @@ cleanup()
|
||||
ip link del veth1 2> /dev/null
|
||||
ip link del ipip11 2> /dev/null
|
||||
ip link del ipip6tnl11 2> /dev/null
|
||||
ip link del ip6ip6tnl11 2> /dev/null
|
||||
ip link del gretap11 2> /dev/null
|
||||
ip link del ip6gre11 2> /dev/null
|
||||
ip link del ip6gretap11 2> /dev/null
|
||||
@@ -742,6 +773,10 @@ bpf_tunnel_test()
|
||||
test_ipip6
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing IP6IP6 tunnel..."
|
||||
test_ip6ip6
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
echo "Testing IPSec tunnel..."
|
||||
test_xfrm_tunnel
|
||||
errors=$(( $errors + $? ))
|
||||
|
||||
@@ -1089,3 +1089,45 @@
|
||||
.errstr_unpriv = "R1 leaks addr",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"pkt > pkt_end taken check",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, // 0. r2 = *(u32 *)(r1 + data_end)
|
||||
offsetof(struct __sk_buff, data_end)),
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1, // 1. r4 = *(u32 *)(r1 + data)
|
||||
offsetof(struct __sk_buff, data)),
|
||||
BPF_MOV64_REG(BPF_REG_3, BPF_REG_4), // 2. r3 = r4
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 42), // 3. r3 += 42
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0), // 4. r1 = 0
|
||||
BPF_JMP_REG(BPF_JGT, BPF_REG_3, BPF_REG_2, 2), // 5. if r3 > r2 goto 8
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 14), // 6. r4 += 14
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_4), // 7. r1 = r4
|
||||
BPF_JMP_REG(BPF_JGT, BPF_REG_3, BPF_REG_2, 1), // 8. if r3 > r2 goto 10
|
||||
BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, 9), // 9. r2 = *(u8 *)(r1 + 9)
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0), // 10. r0 = 0
|
||||
BPF_EXIT_INSN(), // 11. exit
|
||||
},
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_SK_SKB,
|
||||
},
|
||||
{
|
||||
"pkt_end < pkt taken check",
|
||||
.insns = {
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, // 0. r2 = *(u32 *)(r1 + data_end)
|
||||
offsetof(struct __sk_buff, data_end)),
|
||||
BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_1, // 1. r4 = *(u32 *)(r1 + data)
|
||||
offsetof(struct __sk_buff, data)),
|
||||
BPF_MOV64_REG(BPF_REG_3, BPF_REG_4), // 2. r3 = r4
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 42), // 3. r3 += 42
|
||||
BPF_MOV64_IMM(BPF_REG_1, 0), // 4. r1 = 0
|
||||
BPF_JMP_REG(BPF_JGT, BPF_REG_3, BPF_REG_2, 2), // 5. if r3 > r2 goto 8
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 14), // 6. r4 += 14
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_4), // 7. r1 = r4
|
||||
BPF_JMP_REG(BPF_JLT, BPF_REG_2, BPF_REG_3, 1), // 8. if r2 < r3 goto 10
|
||||
BPF_LDX_MEM(BPF_H, BPF_REG_2, BPF_REG_1, 9), // 9. r2 = *(u8 *)(r1 + 9)
|
||||
BPF_MOV64_IMM(BPF_REG_0, 0), // 10. r0 = 0
|
||||
BPF_EXIT_INSN(), // 11. exit
|
||||
},
|
||||
.result = ACCEPT,
|
||||
.prog_type = BPF_PROG_TYPE_SK_SKB,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user