From: Tony Ambardar Date: Thu, 20 Aug 2020 18:46:50 +0000 (-0700) Subject: bpftools: add utility and library packages supporting eBPF usage X-Git-Tag: v21.02.0-rc1~1718 X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fopenwrt.git;a=commitdiff_plain;h=2f0d672088c39914ab9cd59b9de3d6f705cd6d5d bpftools: add utility and library packages supporting eBPF usage Add support for building bpftool and libbpf from the latest 5.8.3 kernel sources, ensuring up-to-date functionality and fixes. Both are written to be backwards compatible, which simplfies build and usage across different OpenWRT image kernels. 'bpftool' is the primary userspace tool widely used for introspection and manipulation of eBPF programs and maps. Two variants are built: a 'full' version which supports object disassembly and depends on libbfd/libopcodes (total ~500KB); and a 'minimal' version without disassembly functions and dependencies. The default 'minimal' variant is otherwise fully functional, and both are compiled using LTO for further (~30KB) size reductions. 'libbpf' provides shared/static libraries and dev files needed for building userspace programs that perform eBPF interaction. Several cross-compilation and build-failure problems are addressed by new patches and ones backported from farther upstream: * 001-libbpf-ensure-no-local-symbols-counted-in-ABI-check.patch * 002-libbpf-fix-build-failure-from-uninitialized-variable.patch * 003-bpftool-allow-passing-BPFTOOL_VERSION-to-make.patch * 004-v5.9-bpftool-use-only-ftw-for-file-tree-parsing.patch Signed-off-by: Tony Ambardar --- diff --git a/package/network/utils/bpftools/Makefile b/package/network/utils/bpftools/Makefile new file mode 100644 index 0000000000..156dc8a4fa --- /dev/null +++ b/package/network/utils/bpftools/Makefile @@ -0,0 +1,162 @@ +# +# Copyright (C) 2020 Tony Ambardar +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=bpftools +PKG_VERSION:=5.8.3 +PKG_RELEASE:=1 + +PKG_SOURCE:=linux-$(PKG_VERSION).tar.xz +PKG_SOURCE_URL:=@KERNEL/linux/kernel/v5.x +PKG_HASH:=8ea27e1ed38981f772cc6d5f16ab8dbb8052c3800ba0494bd798fbb95dcc1779 + +PKG_MAINTAINER:=Tony Ambardar + +PKG_USE_MIPS16:=0 +PKG_BUILD_PARALLEL:=1 +PKG_INSTALL:=1 + +LINUX_VERSION:=$(PKG_VERSION) +LINUX_TLD:=linux-$(LINUX_VERSION) + +BPF_FILES:= \ + kernel/bpf scripts tools/Makefile tools/bpf tools/perf/perf-sys.h \ + tools/arch tools/build tools/include tools/lib tools/scripts +TAR_OPTIONS+= \ + --transform="s;$(LINUX_TLD)/;$(PKG_NAME)-$(PKG_VERSION)/;" \ + $(addprefix $(LINUX_TLD)/,$(BPF_FILES)) + +include $(INCLUDE_DIR)/package.mk + +define Package/bpftool/Default + SECTION:=net + CATEGORY:=Network + TITLE:=bpftool - eBPF subsystem utility + LICENSE:=GPL-2.0-only OR BSD-2-Clause + URL:=http://www.kernel.org + DEPENDS:=+libelf +endef + +define Package/bpftool-minimal + $(call Package/bpftool/Default) + TITLE+= (Minimal) + VARIANT:=minimal + DEFAULT_VARIANT:=1 + PROVIDES:=bpftool + ALTERNATIVES:=200:/usr/sbin/bpftool:/usr/libexec/bpftool-minimal +endef + +define Package/bpftool-full + $(call Package/bpftool/Default) + TITLE+= (Full) + VARIANT:=full + PROVIDES:=bpftool + ALTERNATIVES:=300:/usr/sbin/bpftool:/usr/libexec/bpftool-full + DEPENDS+= +libbfd +libopcodes +endef + +define Package/bpftool-minimal/description + A tool for inspection and simple manipulation of eBPF programs and maps. +endef + +define Package/bpftool-full/description + A tool for inspection and simple manipulation of eBPF programs and maps. + This full version uses libbfd and libopcodes to support disassembly of + eBPF programs and jited code. +endef + +define Package/libbpf + SECTION:=libs + CATEGORY:=Libraries + TITLE:=libbpf - eBPF helper library + VARIANT:=lib + LICENSE:=LGPL-2.1 OR BSD-2-Clause + ABI_VERSION:=0 + URL:=http://www.kernel.org + DEPENDS:=+libelf +endef + +define Package/libbpf/description + libbpf is a library for loading eBPF programs and reading and manipulating eBPF objects from user-space. +endef + + +# LTO not compatible with DSO using PIC +ifneq ($(BUILD_VARIANT),lib) + TARGET_CFLAGS += -ffunction-sections -fdata-sections -flto + TARGET_LDFLAGS += -Wl,--gc-sections +endif + +MAKE_FLAGS += \ + EXTRA_CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS)" \ + LDFLAGS="$(TARGET_LDFLAGS)" \ + BPFTOOL_VERSION="$(LINUX_VERSION)" \ + FEATURES_DUMP="$(PKG_BUILD_DIR)/FEATURE-DUMP.openwrt" \ + OUTPUT="$(PKG_BUILD_DIR)/" \ + prefix="/usr" \ + $(if $(findstring c,$(OPENWRT_VERBOSE)),V=1,V='') + +ifeq ($(BUILD_VARIANT),full) + HAVE_LIBBFD:=1 + HAVE_LIBCAP:=0 + HAVE_CLANG:=0 + MAKE_PATH:=tools/bpf/bpftool +else ifeq ($(BUILD_VARIANT),minimal) + HAVE_LIBBFD:=0 + HAVE_LIBCAP:=0 + HAVE_CLANG:=0 + MAKE_PATH:=tools/bpf/bpftool +else ifeq ($(BUILD_VARIANT),lib) + HAVE_LIBBFD:=0 + HAVE_LIBCAP:=0 + HAVE_CLANG:=0 + MAKE_PATH:=tools/lib/bpf +endif + +# Perform a "throw-away" make to create a FEATURE-DUMP.* file to edit later. +# The "//" in the make target is actually needed, very unPOSIXly. +define Build/Configure + +$(MAKE_VARS) $(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR)/tools/bpf/bpftool \ + $(MAKE_FLAGS) FEATURES_DUMP= $(PKG_BUILD_DIR)//libbpf/libbpf.a + (cd $(PKG_BUILD_DIR); cat FEATURE-DUMP.bpftool libbpf/FEATURE-DUMP.libbpf \ + | sort | uniq > FEATURE-DUMP.openwrt) + $(SED) 's/feature-libbfd=[01]/feature-libbfd=$(HAVE_LIBBFD)/' \ + -e 's/feature-libcap=[01]/feature-libcap=$(HAVE_LIBCAP)/' \ + -e 's/feature-clang-bpf-global-var=[01]/feature-clang-bpf-global-var=$(HAVE_CLANG)/' \ + $(PKG_BUILD_DIR)/FEATURE-DUMP.openwrt +endef + +define Build/InstallDev/libbpf + $(INSTALL_DIR) $(1)/usr/include/bpf + $(CP) $(PKG_INSTALL_DIR)/usr/include/bpf/*.h $(1)/usr/include/bpf/ + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib$(LIB_SUFFIX)/libbpf.{a,so*} \ + $(1)/usr/lib/ + $(INSTALL_DIR) $(1)/usr/lib/pkgconfig + $(CP) $(PKG_INSTALL_DIR)/usr/lib$(LIB_SUFFIX)/pkgconfig/libbpf.pc \ + $(1)/usr/lib/pkgconfig/ +endef + +ifeq ($(BUILD_VARIANT),lib) + Build/InstallDev=$(Build/InstallDev/libbpf) +endif + +define Package/bpftool-$(BUILD_VARIANT)/install + $(INSTALL_DIR) $(1)/usr/libexec + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/bpftool \ + $(1)/usr/libexec/bpftool-$(BUILD_VARIANT) +endef + +define Package/libbpf/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib$(LIB_SUFFIX)/libbpf.so.* $(1)/usr/lib/ +endef + +$(eval $(call BuildPackage,bpftool-full)) +$(eval $(call BuildPackage,bpftool-minimal)) +$(eval $(call BuildPackage,libbpf)) diff --git a/package/network/utils/bpftools/patches/001-libbpf-ensure-no-local-symbols-counted-in-ABI-check.patch b/package/network/utils/bpftools/patches/001-libbpf-ensure-no-local-symbols-counted-in-ABI-check.patch new file mode 100644 index 0000000000..0b223644dc --- /dev/null +++ b/package/network/utils/bpftools/patches/001-libbpf-ensure-no-local-symbols-counted-in-ABI-check.patch @@ -0,0 +1,36 @@ +From fafb2e7eaec6d33ce16e28f481edf781219d5d27 Mon Sep 17 00:00:00 2001 +From: Tony Ambardar +Date: Fri, 24 Jul 2020 23:58:17 -0700 +Subject: [PATCH] tools/libbpf: ensure no local symbols counted in ABI check + +This avoids finding versioned local symbols such as _init and _fini in +the libbpf.so file. + +Signed-off-by: Tony Ambardar +--- + tools/lib/bpf/Makefile | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile +index bf8ed134cb8a..c820b0be9d63 100644 +--- a/tools/lib/bpf/Makefile ++++ b/tools/lib/bpf/Makefile +@@ -152,6 +152,7 @@ GLOBAL_SYM_COUNT = $(shell readelf -s --wide $(BPF_IN_SHARED) | \ + awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}' | \ + sort -u | wc -l) + VERSIONED_SYM_COUNT = $(shell readelf --dyn-syms --wide $(OUTPUT)libbpf.so | \ ++ awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}' | \ + grep -Eo '[^ ]+@LIBBPF_' | cut -d@ -f1 | sort -u | wc -l) + + CMD_TARGETS = $(LIB_TARGET) $(PC_FILE) +@@ -219,6 +220,7 @@ check_abi: $(OUTPUT)libbpf.so + awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}'| \ + sort -u > $(OUTPUT)libbpf_global_syms.tmp; \ + readelf --dyn-syms --wide $(OUTPUT)libbpf.so | \ ++ awk '/GLOBAL/ && /DEFAULT/ && !/UND/ {print $$NF}'| \ + grep -Eo '[^ ]+@LIBBPF_' | cut -d@ -f1 | \ + sort -u > $(OUTPUT)libbpf_versioned_syms.tmp; \ + diff -u $(OUTPUT)libbpf_global_syms.tmp \ +-- +2.25.1 + diff --git a/package/network/utils/bpftools/patches/002-libbpf-fix-build-failure-from-uninitialized-variable.patch b/package/network/utils/bpftools/patches/002-libbpf-fix-build-failure-from-uninitialized-variable.patch new file mode 100644 index 0000000000..a61d0930f8 --- /dev/null +++ b/package/network/utils/bpftools/patches/002-libbpf-fix-build-failure-from-uninitialized-variable.patch @@ -0,0 +1,46 @@ +From 74d0dcf7608b1bab116e297468ac51b57eb97ce0 Mon Sep 17 00:00:00 2001 +From: Tony Ambardar +Date: Thu, 20 Aug 2020 10:06:24 -0700 +Subject: [PATCH] libbpf: fix build failure from uninitialized variable warning + +While compiling libbpf, some GCC versions (at least 8.4.0) have difficulty +determining control flow and a emit warning for potentially uninitialized +usage of 'map', which results in a build error if using "-Werror": + +In file included from libbpf.c:56: +libbpf.c: In function '__bpf_object__open': +libbpf_internal.h:59:2: warning: 'map' may be used uninitialized in this function [-Wmaybe-uninitialized] + libbpf_print(level, "libbpf: " fmt, ##__VA_ARGS__); \ + ^~~~~~~~~~~~ +libbpf.c:5032:18: note: 'map' was declared here + struct bpf_map *map, *targ_map; + ^~~ + +The warning/error is false based on code inspection, so silence it with a +NULL initialization. + +Fixes: 646f02ffdd49 ("libbpf: Add BTF-defined map-in-map support") +Ref: 063e68813391 ("libbpf: Fix false uninitialized variable warning") + +Signed-off-by: Tony Ambardar +--- + tools/lib/bpf/libbpf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c +index 11e4725b8b1c..a72296797357 100644 +--- a/tools/lib/bpf/libbpf.c ++++ b/tools/lib/bpf/libbpf.c +@@ -5028,8 +5028,8 @@ static int bpf_object__collect_map_relos(struct bpf_object *obj, + int i, j, nrels, new_sz, ptr_sz = sizeof(void *); + const struct btf_var_secinfo *vi = NULL; + const struct btf_type *sec, *var, *def; ++ struct bpf_map *map = NULL, *targ_map; + const struct btf_member *member; +- struct bpf_map *map, *targ_map; + const char *name, *mname; + Elf_Data *symbols; + unsigned int moff; +-- +2.25.1 + diff --git a/package/network/utils/bpftools/patches/003-bpftool-allow-passing-BPFTOOL_VERSION-to-make.patch b/package/network/utils/bpftools/patches/003-bpftool-allow-passing-BPFTOOL_VERSION-to-make.patch new file mode 100644 index 0000000000..9423824ee8 --- /dev/null +++ b/package/network/utils/bpftools/patches/003-bpftool-allow-passing-BPFTOOL_VERSION-to-make.patch @@ -0,0 +1,26 @@ +From 668d1c2951e18512a27aec20b80dea627d01bf04 Mon Sep 17 00:00:00 2001 +From: Tony Ambardar +Date: Thu, 20 Aug 2020 16:05:48 -0700 +Subject: [PATCH] tools/bpftool: allow passing BPFTOOL_VERSION to make + +Signed-off-by: Tony Ambardar +--- + tools/bpf/bpftool/Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile +index 9e85f101be85..d9d821736c31 100644 +--- a/tools/bpf/bpftool/Makefile ++++ b/tools/bpf/bpftool/Makefile +@@ -25,7 +25,7 @@ endif + + LIBBPF = $(LIBBPF_PATH)libbpf.a + +-BPFTOOL_VERSION := $(shell make -rR --no-print-directory -sC ../../.. kernelversion) ++BPFTOOL_VERSION ?= $(shell make -rR --no-print-directory -sC ../../.. kernelversion) + + $(LIBBPF): FORCE + $(if $(LIBBPF_OUTPUT),@mkdir -p $(LIBBPF_OUTPUT)) +-- +2.25.1 + diff --git a/package/network/utils/bpftools/patches/004-v5.9-bpftool-use-only-ftw-for-file-tree-parsing.patch b/package/network/utils/bpftools/patches/004-v5.9-bpftool-use-only-ftw-for-file-tree-parsing.patch new file mode 100644 index 0000000000..7767b5f595 --- /dev/null +++ b/package/network/utils/bpftools/patches/004-v5.9-bpftool-use-only-ftw-for-file-tree-parsing.patch @@ -0,0 +1,238 @@ +From 6edda7633e4fdf33b91c2e86c05cab805a0dabb3 Mon Sep 17 00:00:00 2001 +From: Tony Ambardar +Date: Mon, 20 Jul 2020 19:48:16 -0700 +Subject: [PATCH] bpftool: Use only nftw for file tree parsing + +The bpftool sources include code to walk file trees, but use multiple +frameworks to do so: nftw and fts. While nftw conforms to POSIX/SUSv3 and +is widely available, fts is not conformant and less common, especially on +non-glibc systems. The inconsistent framework usage hampers maintenance +and portability of bpftool, in particular for embedded systems. + +Standardize code usage by rewriting one fts-based function to use nftw and +clean up some related function warnings by extending use of "const char *" +arguments. This change helps in building bpftool against musl for OpenWrt. + +Also fix an unsafe call to dirname() by duplicating the string to pass, +since some implementations may directly alter it. The same approach is +used in libbpf.c. + +Signed-off-by: Tony Ambardar +Signed-off-by: Daniel Borkmann +Reviewed-by: Quentin Monnet +Link: https://lore.kernel.org/bpf/20200721024817.13701-1-Tony.Ambardar@gmail.com +--- + tools/bpf/bpftool/common.c | 137 ++++++++++++++++++++++--------------- + tools/bpf/bpftool/main.h | 4 +- + 2 files changed, 82 insertions(+), 59 deletions(-) + +diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c +index 88264abaa738..d8bb4dd18c8a 100644 +--- a/tools/bpf/bpftool/common.c ++++ b/tools/bpf/bpftool/common.c +@@ -1,10 +1,11 @@ + // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) + /* Copyright (C) 2017-2018 Netronome Systems, Inc. */ + ++#define _GNU_SOURCE + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -123,24 +124,35 @@ int mount_tracefs(const char *target) + return err; + } + +-int open_obj_pinned(char *path, bool quiet) ++int open_obj_pinned(const char *path, bool quiet) + { +- int fd; ++ char *pname; ++ int fd = -1; + +- fd = bpf_obj_get(path); ++ pname = strdup(path); ++ if (!pname) { ++ if (!quiet) ++ p_err("mem alloc failed"); ++ goto out_ret; ++ } ++ ++ fd = bpf_obj_get(pname); + if (fd < 0) { + if (!quiet) +- p_err("bpf obj get (%s): %s", path, +- errno == EACCES && !is_bpffs(dirname(path)) ? ++ p_err("bpf obj get (%s): %s", pname, ++ errno == EACCES && !is_bpffs(dirname(pname)) ? + "directory not in bpf file system (bpffs)" : + strerror(errno)); +- return -1; ++ goto out_free; + } + ++out_free: ++ free(pname); ++out_ret: + return fd; + } + +-int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type) ++int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type) + { + enum bpf_obj_type type; + int fd; +@@ -353,71 +365,82 @@ void print_hex_data_json(uint8_t *data, size_t len) + jsonw_end_array(json_wtr); + } + ++/* extra params for nftw cb */ ++static struct pinned_obj_table *build_fn_table; ++static enum bpf_obj_type build_fn_type; ++ ++static int do_build_table_cb(const char *fpath, const struct stat *sb, ++ int typeflag, struct FTW *ftwbuf) ++{ ++ struct bpf_prog_info pinned_info; ++ __u32 len = sizeof(pinned_info); ++ struct pinned_obj *obj_node; ++ enum bpf_obj_type objtype; ++ int fd, err = 0; ++ ++ if (typeflag != FTW_F) ++ goto out_ret; ++ ++ fd = open_obj_pinned(fpath, true); ++ if (fd < 0) ++ goto out_ret; ++ ++ objtype = get_fd_type(fd); ++ if (objtype != build_fn_type) ++ goto out_close; ++ ++ memset(&pinned_info, 0, sizeof(pinned_info)); ++ if (bpf_obj_get_info_by_fd(fd, &pinned_info, &len)) ++ goto out_close; ++ ++ obj_node = calloc(1, sizeof(*obj_node)); ++ if (!obj_node) { ++ err = -1; ++ goto out_close; ++ } ++ ++ obj_node->id = pinned_info.id; ++ obj_node->path = strdup(fpath); ++ if (!obj_node->path) { ++ err = -1; ++ free(obj_node); ++ goto out_close; ++ } ++ ++ hash_add(build_fn_table->table, &obj_node->hash, obj_node->id); ++out_close: ++ close(fd); ++out_ret: ++ return err; ++} ++ + int build_pinned_obj_table(struct pinned_obj_table *tab, + enum bpf_obj_type type) + { +- struct bpf_prog_info pinned_info = {}; +- struct pinned_obj *obj_node = NULL; +- __u32 len = sizeof(pinned_info); + struct mntent *mntent = NULL; +- enum bpf_obj_type objtype; + FILE *mntfile = NULL; +- FTSENT *ftse = NULL; +- FTS *fts = NULL; +- int fd, err; ++ int flags = FTW_PHYS; ++ int nopenfd = 16; ++ int err = 0; + + mntfile = setmntent("/proc/mounts", "r"); + if (!mntfile) + return -1; + ++ build_fn_table = tab; ++ build_fn_type = type; ++ + while ((mntent = getmntent(mntfile))) { +- char *path[] = { mntent->mnt_dir, NULL }; ++ char *path = mntent->mnt_dir; + + if (strncmp(mntent->mnt_type, "bpf", 3) != 0) + continue; +- +- fts = fts_open(path, 0, NULL); +- if (!fts) +- continue; +- +- while ((ftse = fts_read(fts))) { +- if (!(ftse->fts_info & FTS_F)) +- continue; +- fd = open_obj_pinned(ftse->fts_path, true); +- if (fd < 0) +- continue; +- +- objtype = get_fd_type(fd); +- if (objtype != type) { +- close(fd); +- continue; +- } +- memset(&pinned_info, 0, sizeof(pinned_info)); +- err = bpf_obj_get_info_by_fd(fd, &pinned_info, &len); +- if (err) { +- close(fd); +- continue; +- } +- +- obj_node = malloc(sizeof(*obj_node)); +- if (!obj_node) { +- close(fd); +- fts_close(fts); +- fclose(mntfile); +- return -1; +- } +- +- memset(obj_node, 0, sizeof(*obj_node)); +- obj_node->id = pinned_info.id; +- obj_node->path = strdup(ftse->fts_path); +- hash_add(tab->table, &obj_node->hash, obj_node->id); +- +- close(fd); +- } +- fts_close(fts); ++ err = nftw(path, do_build_table_cb, nopenfd, flags); ++ if (err) ++ break; + } + fclose(mntfile); +- return 0; ++ return err; + } + + void delete_pinned_obj_table(struct pinned_obj_table *tab) +diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h +index af9ad56c303a..b02e763f1e9c 100644 +--- a/tools/bpf/bpftool/main.h ++++ b/tools/bpf/bpftool/main.h +@@ -140,8 +140,8 @@ int cmd_select(const struct cmd *cmds, int argc, char **argv, + int get_fd_type(int fd); + const char *get_fd_type_name(enum bpf_obj_type type); + char *get_fdinfo(int fd, const char *key); +-int open_obj_pinned(char *path, bool quiet); +-int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type); ++int open_obj_pinned(const char *path, bool quiet); ++int open_obj_pinned_any(const char *path, enum bpf_obj_type exp_type); + int mount_bpffs_for_pin(const char *name); + int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32)); + int do_pin_fd(int fd, const char *name); +-- +2.17.1 +