├── .github ├── docker │ └── Dockerfile.ubuntu └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── src ├── .gitignore ├── CMakeLists.txt ├── Makefile ├── tc.bpf.c ├── tc.c └── xmake.lua ├── tools ├── cmake │ ├── FindBpfObject.cmake │ └── FindLibBpf.cmake └── gen_vmlinux_h.sh └── vmlinux ├── arm64 ├── vmlinux.h └── vmlinux_516.h ├── vmlinux.h └── x86 ├── vmlinux.h └── vmlinux_508.h /.github/docker/Dockerfile.ubuntu: -------------------------------------------------------------------------------- 1 | ARG VERSION="22.04" 2 | FROM ubuntu:${VERSION} 3 | 4 | ARG LLVM_VERSION="14" 5 | ENV LLVM_VERSION=$LLVM_VERSION 6 | 7 | ARG SHORTNAME="jammy" 8 | 9 | RUN apt-get update && apt-get install -y curl gnupg 10 | RUN if [ "${LLVM_VERSION}" = "16" ]; \ 11 | then \ 12 | echo "\n\ 13 | deb http://apt.llvm.org/${SHORTNAME}/ llvm-toolchain-${SHORTNAME} main\n\ 14 | deb-src http://apt.llvm.org/${SHORTNAME}/ llvm-toolchain-${SHORTNAME} main\n\ 15 | " >> /etc/apt/sources.list;\ 16 | else \ 17 | echo "\n\ 18 | deb http://apt.llvm.org/${SHORTNAME}/ llvm-toolchain-${SHORTNAME}-${LLVM_VERSION} main\n\ 19 | deb-src http://apt.llvm.org/${SHORTNAME}/ llvm-toolchain-${SHORTNAME}-${LLVM_VERSION} main\n\ 20 | " >> /etc/apt/sources.list; \ 21 | fi 22 | RUN curl -L https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - 23 | 24 | ARG DEBIAN_FRONTEND="noninteractive" 25 | ENV TZ="Etc/UTC" 26 | 27 | RUN apt-get update && apt-get install -y \ 28 | libelf-dev \ 29 | zlib1g-dev \ 30 | libbfd-dev \ 31 | clang-${LLVM_VERSION} \ 32 | libclang-${LLVM_VERSION}-dev \ 33 | libclang-common-${LLVM_VERSION}-dev \ 34 | libclang1-${LLVM_VERSION} \ 35 | llvm-${LLVM_VERSION} \ 36 | llvm-${LLVM_VERSION}-dev \ 37 | llvm-${LLVM_VERSION}-runtime \ 38 | libllvm${LLVM_VERSION} \ 39 | make pkg-config \ 40 | rustc cargo rustfmt \ 41 | sudo \ 42 | && apt-get -y clean 43 | 44 | RUN ln -s /usr/bin/clang-${LLVM_VERSION} /usr/bin/clang && ln -s /usr/bin/llvm-strip-${LLVM_VERSION} /usr/bin/llvm-strip 45 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: libbpf-bootstrap build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build_libbpf_bootstrap: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | llvm: [14, 15, 16] 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | submodules: recursive 20 | - name: Build container 21 | uses: docker/build-push-action@v3 22 | with: 23 | push: false 24 | build-args: LLVM_VERSION=${{ matrix.llvm }} 25 | file: ./.github/docker/Dockerfile.ubuntu 26 | tags: build_container 27 | - name: Build examples/c 28 | run: | 29 | docker run \ 30 | -v $(pwd):/libbpf-bootstrap \ 31 | build_container \ 32 | /bin/bash -c \ 33 | 'cd /libbpf-bootstrap/examples/c && make' 34 | - name: Build examples/rust 35 | run: | 36 | docker run \ 37 | -v $(pwd):/libbpf-bootstrap \ 38 | build_container \ 39 | /bin/bash -c \ 40 | 'cd /libbpf-bootstrap/examples/rust && cargo build' 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /examples/c/build 3 | /examples/c/.xmake 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libbpf"] 2 | path = libbpf 3 | url = https://github.com/libbpf/libbpf.git 4 | [submodule "bpftool"] 5 | path = bpftool 6 | url = https://github.com/libbpf/bpftool 7 | [submodule "blazesym"] 8 | path = blazesym 9 | url = https://github.com/libbpf/blazesym.git 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Andrii Nakryiko 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nodeport service load-balancing demo using eBpf 2 | 3 | ## tc 4 | 5 | tc eBpf demo of Nodeport N-S load balancing and SNAT/ DNAT using recently added kfuncs to support connection tracking and NAT using eBpf programs. This could also be an option to implement L4 Route services for the Kubernetes Gateway API using these new eBpf functions. 6 | 7 | Notes: 8 | - The design is based on Approach A2 as [documented here](https://gist.github.com/srampal/b300d1a1f847d18d362a55844944f7a7). 9 | - The current version is an initial Proof of Concept/ demo currently meant to validate [Approach A2](https://gist.github.com/srampal/b300d1a1f847d18d362a55844944f7a7) and the use of the newly added kfuncs for managing kernel connection tracking (conntrack) tables. 10 | - This requires a system built with a custom kernel currently (it has been tested using a kernel from bpf-next/ 6.0.0-rc3) 11 | - To build, first recursively load all the sub-modules at the top of the repo (*git submodule update --init --recursive*), then 'make tc' under src 12 | 13 | ### Tech Talk 14 | A Tech Talk and demo of this project is [available here](https://youtu.be/4eJYd04R7rY). 15 | 16 | ### Executing the demo 17 | 18 | For the initial demo, the eBpf program is invoked from a CLI command and not via a Kubernetes controller for services (this is to be addressed in a following rev). To run the demo, create a Kubernetes deployment and provide the NodePort and backends via the CLI invocation on the Kubernetes worker nodes. For example: *tc eth0 \ \ \ \*. (For example *tc eth0 31000 10.240.1.2 10.240.1.3 80*). 19 | 20 | Additional details to be added with future updates ... 21 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | /.output 2 | /tc 3 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | 3 | cmake_minimum_required(VERSION 3.16) 4 | project(examples) 5 | 6 | # Tell cmake where to find BpfObject module 7 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../tools/cmake) 8 | 9 | # Build vendored libbpf 10 | include(ExternalProject) 11 | ExternalProject_Add(libbpf 12 | PREFIX libbpf 13 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../libbpf/src 14 | CONFIGURE_COMMAND "" 15 | BUILD_COMMAND make 16 | BUILD_STATIC_ONLY=1 17 | OBJDIR=${CMAKE_CURRENT_BINARY_DIR}/libbpf/libbpf 18 | DESTDIR=${CMAKE_CURRENT_BINARY_DIR}/libbpf 19 | INCLUDEDIR= 20 | LIBDIR= 21 | UAPIDIR= 22 | install 23 | BUILD_IN_SOURCE TRUE 24 | INSTALL_COMMAND "" 25 | STEP_TARGETS build 26 | ) 27 | 28 | ExternalProject_Add(bpftool 29 | PREFIX bpftool 30 | SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../bpftool/src 31 | CONFIGURE_COMMAND "" 32 | BUILD_COMMAND make 33 | OUTPUT=${CMAKE_CURRENT_BINARY_DIR}/bpftool/ 34 | BUILD_IN_SOURCE TRUE 35 | INSTALL_COMMAND "" 36 | STEP_TARGETS build 37 | ) 38 | 39 | # Set BpfObject input parameters -- note this is usually not necessary unless 40 | # you're in a highly vendored environment (like libbpf-bootstrap) 41 | set(BPFOBJECT_BPFTOOL_EXE ${CMAKE_CURRENT_BINARY_DIR}/bpftool/bpftool) 42 | set(BPFOBJECT_VMLINUX_H ${CMAKE_CURRENT_SOURCE_DIR}/../../vmlinux/vmlinux.h) 43 | set(LIBBPF_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/libbpf) 44 | set(LIBBPF_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/libbpf/libbpf.a) 45 | find_package(BpfObject REQUIRED) 46 | 47 | # Create an executable for each application 48 | file(GLOB apps *.bpf.c) 49 | foreach(app ${apps}) 50 | get_filename_component(app_stem ${app} NAME_WE) 51 | 52 | # Build object skeleton and depend skeleton on libbpf build 53 | bpf_object(${app_stem} ${app_stem}.bpf.c) 54 | add_dependencies(${app_stem}_skel libbpf-build) 55 | 56 | add_executable(${app_stem} ${app_stem}.c) 57 | target_link_libraries(${app_stem} ${app_stem}_skel) 58 | endforeach() 59 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | OUTPUT := ./.output 3 | CLANG ?= clang 4 | LLVM_STRIP ?= llvm-strip 5 | LIBBPF_SRC := $(abspath ../libbpf/src) 6 | BPFTOOL_SRC := $(abspath ../bpftool/src) 7 | LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) 8 | BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) 9 | BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool 10 | LIBBLAZESYM_SRC := $(abspath ../blazesym/) 11 | LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a) 12 | LIBBLAZESYM_HEADER := $(abspath $(OUTPUT)/blazesym.h) 13 | ARCH := $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 14 | VMLINUX := ../vmlinux/$(ARCH)/vmlinux.h 15 | # Use our own libbpf API headers and Linux UAPI headers distributed with 16 | # libbpf to avoid dependency on system-wide headers, which could be missing or 17 | # outdated 18 | INCLUDES := -I$(OUTPUT) -I../libbpf/include/uapi -I$(dir $(VMLINUX)) 19 | CFLAGS := -g -Wall 20 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 21 | 22 | # APPS = minimal minimal_legacy bootstrap uprobe kprobe fentry usdt sockfilter tc 23 | APPS = tc 24 | 25 | CARGO ?= $(shell which cargo) 26 | ifeq ($(strip $(CARGO)),) 27 | BZS_APPS := 28 | else 29 | BZS_APPS := profile 30 | APPS += $(BZS_APPS) 31 | # Required by libblazesym 32 | ALL_LDFLAGS += -lrt -ldl -lpthread -lm 33 | endif 34 | 35 | # Get Clang's default includes on this system. We'll explicitly add these dirs 36 | # to the includes list when compiling with `-target bpf` because otherwise some 37 | # architecture-specific dirs will be "missing" on some architectures/distros - 38 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 39 | # sys/cdefs.h etc. might be missing. 40 | # 41 | # Use '-idirafter': Don't interfere with include mechanics except where the 42 | # build would have failed anyways. 43 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 44 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 45 | 46 | ifeq ($(V),1) 47 | Q = 48 | msg = 49 | else 50 | Q = @ 51 | msg = @printf ' %-8s %s%s\n' \ 52 | "$(1)" \ 53 | "$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \ 54 | "$(if $(3), $(3))"; 55 | MAKEFLAGS += --no-print-directory 56 | endif 57 | 58 | define allow-override 59 | $(if $(or $(findstring environment,$(origin $(1))),\ 60 | $(findstring command line,$(origin $(1)))),,\ 61 | $(eval $(1) = $(2))) 62 | endef 63 | 64 | $(call allow-override,CC,$(CROSS_COMPILE)cc) 65 | $(call allow-override,LD,$(CROSS_COMPILE)ld) 66 | 67 | .PHONY: all 68 | all: $(APPS) 69 | 70 | .PHONY: clean 71 | clean: 72 | $(call msg,CLEAN) 73 | $(Q)rm -rf $(OUTPUT) $(APPS) 74 | 75 | $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): 76 | $(call msg,MKDIR,$@) 77 | $(Q)mkdir -p $@ 78 | 79 | # Build libbpf 80 | $(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf 81 | $(call msg,LIB,$@) 82 | $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ 83 | OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ 84 | INCLUDEDIR= LIBDIR= UAPIDIR= \ 85 | install 86 | 87 | # Build bpftool 88 | $(BPFTOOL): | $(BPFTOOL_OUTPUT) 89 | $(call msg,BPFTOOL,$@) 90 | $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap 91 | 92 | 93 | $(LIBBLAZESYM_SRC)/target/release/libblazesym.a:: 94 | $(Q)cd $(LIBBLAZESYM_SRC) && $(CARGO) build --features=cheader --release 95 | 96 | $(LIBBLAZESYM_OBJ): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT) 97 | $(call msg,LIB, $@) 98 | $(Q)cp $(LIBBLAZESYM_SRC)/target/release/libblazesym.a $@ 99 | 100 | $(LIBBLAZESYM_HEADER): $(LIBBLAZESYM_SRC)/target/release/libblazesym.a | $(OUTPUT) 101 | $(call msg,LIB,$@) 102 | $(Q)cp $(LIBBLAZESYM_SRC)/target/release/blazesym.h $@ 103 | 104 | # Build BPF code 105 | $(OUTPUT)/%.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) 106 | $(call msg,BPF,$@) 107 | $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 108 | $(Q)$(LLVM_STRIP) -g $@ # strip useless DWARF info 109 | 110 | # Generate BPF skeletons 111 | $(OUTPUT)/%.skel.h: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL) 112 | $(call msg,GEN-SKEL,$@) 113 | $(Q)$(BPFTOOL) gen skeleton $< > $@ 114 | 115 | # Build user-space code 116 | $(patsubst %,$(OUTPUT)/%.o,$(APPS)): %.o: %.skel.h 117 | 118 | $(OUTPUT)/%.o: %.c $(wildcard %.h) | $(OUTPUT) 119 | $(call msg,CC,$@) 120 | $(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@ 121 | 122 | $(patsubst %,$(OUTPUT)/%.o,$(BZS_APPS)): $(LIBBLAZESYM_HEADER) 123 | 124 | $(BZS_APPS): $(LIBBLAZESYM_OBJ) 125 | 126 | # Build application binary 127 | $(APPS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT) 128 | $(call msg,BINARY,$@) 129 | $(Q)$(CC) $(CFLAGS) $^ $(ALL_LDFLAGS) -lelf -lz -o $@ 130 | 131 | # delete failed targets 132 | .DELETE_ON_ERROR: 133 | 134 | # keep intermediate (.skel.h, .bpf.o, etc) targets 135 | .SECONDARY: 136 | -------------------------------------------------------------------------------- /src/tc.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | /* Copyright (c) 2022 Red Hat */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* Set this flag to enable/ disable debug messages */ 10 | #define DEBUG_ENABLED false 11 | 12 | #define DEBUG_BPF_PRINTK(...) if(DEBUG_ENABLED) {bpf_printk(__VA_ARGS__);} 13 | 14 | 15 | #define TC_ACT_OK 0 16 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 17 | #define TEST_NODEPORT ((unsigned short) 31000) 18 | 19 | struct np_backends { 20 | __be32 be1; 21 | __be32 be2; 22 | __u16 targetPort; 23 | }; 24 | 25 | /* Simplified map definition for initial POC */ 26 | struct { 27 | __uint(type, BPF_MAP_TYPE_HASH); 28 | __uint(max_entries, 1024); 29 | __type(key, __u16); 30 | __type(value, struct np_backends); 31 | } svc_map SEC(".maps"); 32 | 33 | struct bpf_ct_opts { 34 | s32 netns_id; 35 | s32 error; 36 | u8 l4proto; 37 | u8 dir; 38 | u8 reserved[2]; 39 | }; 40 | 41 | struct nf_conn * 42 | bpf_skb_ct_lookup(struct __sk_buff *, struct bpf_sock_tuple *, u32, 43 | struct bpf_ct_opts *, u32) __ksym; 44 | 45 | struct nf_conn * 46 | bpf_skb_ct_alloc(struct __sk_buff *skb_ctx, struct bpf_sock_tuple *bpf_tuple, 47 | u32 tuple__sz, struct bpf_ct_opts *opts, u32 opts__sz) __ksym; 48 | 49 | struct nf_conn *bpf_ct_insert_entry(struct nf_conn *nfct_i) __ksym; 50 | 51 | int bpf_ct_set_nat_info(struct nf_conn *nfct, 52 | union nf_inet_addr *addr, int port, 53 | enum nf_nat_manip_type manip) __ksym; 54 | 55 | void bpf_ct_set_timeout(struct nf_conn *nfct, u32 timeout) __ksym; 56 | 57 | int bpf_ct_set_status(const struct nf_conn *nfct, u32 status) __ksym; 58 | 59 | void bpf_ct_release(struct nf_conn *) __ksym; 60 | 61 | // static __always_inline int nodeport_lb4(struct __sk_buff *ctx) { 62 | 63 | /* Not marking this function to be inline for now */ 64 | int nodeport_lb4(struct __sk_buff *ctx) { 65 | 66 | void *data_end = (void *)(long)ctx->data_end; 67 | void *data = (void *)(long)ctx->data; 68 | struct ethhdr *eth = data; 69 | u64 nh_off = sizeof(*eth); 70 | struct np_backends *lkup; 71 | __be32 b1; 72 | __be32 b2; 73 | 74 | if (data + nh_off > data_end) 75 | return TC_ACT_OK; 76 | 77 | switch (bpf_ntohs(eth->h_proto)) { 78 | case ETH_P_IP: { 79 | struct bpf_sock_tuple bpf_tuple = {}; 80 | struct iphdr *iph = data + nh_off; 81 | struct bpf_ct_opts opts_def = { 82 | .netns_id = -1, 83 | }; 84 | struct nf_conn *ct; 85 | // bool ret; 86 | 87 | if ((void *)(iph + 1) > data_end) 88 | return TC_ACT_OK; 89 | 90 | opts_def.l4proto = iph->protocol; 91 | bpf_tuple.ipv4.saddr = iph->saddr; 92 | bpf_tuple.ipv4.daddr = iph->daddr; 93 | 94 | if (iph->protocol == IPPROTO_TCP) { 95 | struct tcphdr *tcph = (struct tcphdr *)(iph + 1); 96 | 97 | if ((void *)(tcph + 1) > data_end) 98 | return TC_ACT_OK; 99 | 100 | bpf_tuple.ipv4.sport = tcph->source; 101 | bpf_tuple.ipv4.dport = tcph->dest; 102 | } else if (iph->protocol == IPPROTO_UDP) { 103 | struct udphdr *udph = (struct udphdr *)(iph + 1); 104 | 105 | if ((void *)(udph + 1) > data_end) 106 | return TC_ACT_OK; 107 | 108 | bpf_tuple.ipv4.sport = udph->source; 109 | bpf_tuple.ipv4.dport = udph->dest; 110 | } else { 111 | return TC_ACT_OK; 112 | } 113 | 114 | // Skip all BPF-CT unless port is of the target nodeport 115 | /** 116 | if (bpf_tuple.ipv4.dport != bpf_ntohs(TEST_NODEPORT)) { 117 | return TC_ACT_OK; 118 | } 119 | **/ 120 | 121 | u16 key = bpf_ntohs(bpf_tuple.ipv4.dport); 122 | 123 | lkup = (struct np_backends *) bpf_map_lookup_elem(&svc_map, &key); 124 | 125 | if (lkup) { 126 | b1 = lkup->be1; 127 | b2 = lkup->be2; 128 | DEBUG_BPF_PRINTK("lkup result: Full BE1 0x%X BE2 0x%X \n", 129 | b1, b2) 130 | } else { 131 | DEBUG_BPF_PRINTK("lkup result: NULL \n") 132 | return TC_ACT_OK; 133 | } 134 | 135 | 136 | ct = bpf_skb_ct_lookup(ctx, &bpf_tuple, 137 | sizeof(bpf_tuple.ipv4), 138 | &opts_def, sizeof(opts_def)); 139 | // ret = !!ct; 140 | if (ct) { 141 | DEBUG_BPF_PRINTK("CT lookup (ct found) 0x%X\n", ct) 142 | DEBUG_BPF_PRINTK("Timeout %u status 0x%X dport 0x%X \n", 143 | ct->timeout, ct->status, bpf_tuple.ipv4.dport) 144 | if (iph->protocol == IPPROTO_TCP) { 145 | DEBUG_BPF_PRINTK("TCP proto state %u flags %u/ %u last_dir %u \n", 146 | ct->proto.tcp.state, 147 | ct->proto.tcp.seen[0].flags, ct->proto.tcp.seen[1].flags, 148 | ct->proto.tcp.last_dir) 149 | } 150 | bpf_ct_release(ct); 151 | } else { 152 | DEBUG_BPF_PRINTK("CT lookup (no entry) 0x%X\n", 0) 153 | DEBUG_BPF_PRINTK("dport 0x%X 0x%X\n", 154 | bpf_tuple.ipv4.dport, bpf_htons(TEST_NODEPORT)) 155 | DEBUG_BPF_PRINTK("Got IP packet: dest: %pI4, protocol: %u", 156 | &(iph->daddr), iph->protocol) 157 | /* Create a new CT entry */ 158 | 159 | struct nf_conn *nct = bpf_skb_ct_alloc(ctx, 160 | &bpf_tuple, sizeof(bpf_tuple.ipv4), 161 | &opts_def, sizeof(opts_def)); 162 | 163 | if (!nct) { 164 | DEBUG_BPF_PRINTK("bpf_skb_ct_alloc() failed\n") 165 | return TC_ACT_OK; 166 | } 167 | 168 | // Rudimentary load balancing for now based on received source port 169 | 170 | union nf_inet_addr addr = {}; 171 | 172 | addr.ip = b1; 173 | 174 | if (bpf_htons(bpf_tuple.ipv4.sport) % 2) { 175 | addr.ip = b2; 176 | } 177 | 178 | /* Add DNAT info */ 179 | bpf_ct_set_nat_info(nct, &addr, lkup->targetPort, NF_NAT_MANIP_DST); 180 | 181 | /* Now add SNAT (masquerade) info */ 182 | /* For now using the node IP, check this TODO */ 183 | /* addr.ip = 0x0101F00a; Kind-Net bridge IP 10.240.1.1 */ 184 | 185 | addr.ip = bpf_tuple.ipv4.daddr; 186 | 187 | bpf_ct_set_nat_info(nct, &addr, -1, NF_NAT_MANIP_SRC); 188 | 189 | bpf_ct_set_timeout(nct, 30000); 190 | bpf_ct_set_status(nct, IP_CT_NEW); 191 | 192 | ct = bpf_ct_insert_entry(nct); 193 | 194 | DEBUG_BPF_PRINTK("bpf_ct_insert_entry() returned ct 0x%x\n", ct) 195 | 196 | if (ct) { 197 | bpf_ct_release(ct); 198 | } 199 | } 200 | } 201 | default: 202 | break; 203 | } 204 | out: 205 | 206 | return TC_ACT_OK; 207 | 208 | } 209 | 210 | 211 | SEC("tc") 212 | int tc_ingress(struct __sk_buff *ctx) 213 | { 214 | int ret = TC_ACT_OK; 215 | #if 0 216 | void *data_end = (void *)(__u64)ctx->data_end; 217 | void *data = (void *)(__u64)ctx->data; 218 | struct ethhdr *l2h = NULL; 219 | struct iphdr *ip4h = NULL; 220 | struct tcphdr *tcph = NULL; 221 | 222 | if (ctx->protocol != bpf_htons(ETH_P_IP)) 223 | return TC_ACT_OK; 224 | 225 | l2h = data; 226 | if ((void *)(l2h + 1) > data_end) 227 | return TC_ACT_OK; 228 | 229 | ip4h = (struct iphdr *)(l2h + 1); 230 | if ((void *)(ip4h + 1) > data_end) 231 | return TC_ACT_OK; 232 | 233 | if (ip4h->protocol == IPPROTO_TCP) { 234 | tcph = (struct tcphdr *)(ip4h + 1); 235 | 236 | if ((void *)(tcph + 1) > data_end) { 237 | return TC_ACT_OK; 238 | } 239 | 240 | if (tcph->dest == bpf_htons(TEST_NODEPORT)) { 241 | DEBUG_BPF_PRINTK("1) Got IP Nodeport packet: dest: %pI4, protocol: %u", &(ip4h->daddr), ip4h->protocol); 242 | } 243 | } 244 | #endif 245 | 246 | ret = nodeport_lb4(ctx); 247 | return ret; 248 | } 249 | 250 | char __license[] SEC("license") = "GPL"; 251 | -------------------------------------------------------------------------------- /src/tc.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | /* Copyright (c) 2022 Red Hat */ 4 | #include 5 | #include 6 | #include "tc.skel.h" 7 | #include 8 | 9 | 10 | #include 11 | #include 12 | 13 | 14 | #define LO_IFINDEX 1 15 | 16 | 17 | static volatile sig_atomic_t exiting = 0; 18 | 19 | static void sig_int(int signo) 20 | { 21 | exiting = 1; 22 | } 23 | 24 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 25 | { 26 | return vfprintf(stderr, format, args); 27 | } 28 | 29 | int main(int argc, char **argv) 30 | { 31 | int ifindex; 32 | 33 | /* Nodeport demo program, assumes 2 backends for now (provided via cli) */ 34 | /* later pull a variable # set of backends from k8s controller watch updates */ 35 | 36 | uint16_t nodeport; 37 | 38 | struct np_backends { 39 | __u32 be1; 40 | __u32 be2; 41 | __u16 targetPort; 42 | }; 43 | 44 | struct np_backends backends; 45 | 46 | if (argc < 5) { 47 | fprintf(stderr, "Usage: tc nodeport \n"); 48 | return 1; 49 | } 50 | 51 | ifindex = if_nametoindex(argv[1]); 52 | if (!ifindex) { 53 | fprintf(stderr, "Bad interface name\n"); 54 | return 1; 55 | } 56 | 57 | nodeport = atoi(argv[2]); 58 | if (nodeport < 30000 || nodeport > 32000) { 59 | fprintf(stderr, "Nodeport value must be in range <30000, 32000>\n"); 60 | return 1; 61 | } 62 | 63 | 64 | inet_aton(argv[3], (struct in_addr *)&(backends.be1)); 65 | inet_aton(argv[4], (struct in_addr *)&(backends.be2)); 66 | 67 | if (backends.be1 == 0 || backends.be2 == 0) { 68 | fprintf(stderr, "Invalid backend IP values\n"); 69 | return 1; 70 | } 71 | 72 | if (argc < 6) 73 | backends.targetPort = 80; 74 | else 75 | backends.targetPort = atoi(argv[5]); 76 | 77 | DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook, 78 | .ifindex = ifindex, .attach_point = BPF_TC_INGRESS); 79 | DECLARE_LIBBPF_OPTS(bpf_tc_opts, tc_opts, 80 | .handle = 1, .priority = 1); 81 | bool hook_created = false; 82 | struct tc_bpf *skel; 83 | int err; 84 | 85 | libbpf_set_strict_mode(LIBBPF_STRICT_ALL); 86 | libbpf_set_print(libbpf_print_fn); 87 | 88 | skel = tc_bpf__open_and_load(); 89 | if (!skel) { 90 | fprintf(stderr, "Failed to open BPF skeleton\n"); 91 | return 1; 92 | } 93 | 94 | /* hack # of backends hard coded to 2 for initial demo */ 95 | __u16 key = nodeport; 96 | 97 | err = bpf_map__update_elem(skel->maps.svc_map, &key, sizeof(key), 98 | &backends, sizeof(backends), 99 | BPF_ANY); 100 | if (err ) { 101 | fprintf(stderr, "Failed to update svc_map: %d\n", err); 102 | fprintf(stderr, "continuing with default backend mappings \n"); 103 | goto cleanup; 104 | } 105 | 106 | /* The hook (i.e. qdisc) may already exists because: 107 | * 1. it is created by other processes or users 108 | * 2. or since we are attaching to the TC ingress ONLY, 109 | * bpf_tc_hook_destroy does NOT really remove the qdisc, 110 | * there may be an egress filter on the qdisc 111 | */ 112 | err = bpf_tc_hook_create(&tc_hook); 113 | if (!err) 114 | hook_created = true; 115 | if (err && err != -EEXIST) { 116 | fprintf(stderr, "Failed to create TC hook: %d\n", err); 117 | goto cleanup; 118 | } 119 | 120 | tc_opts.prog_fd = bpf_program__fd(skel->progs.tc_ingress); 121 | err = bpf_tc_attach(&tc_hook, &tc_opts); 122 | if (err) { 123 | fprintf(stderr, "Failed to attach TC: %d\n", err); 124 | goto cleanup; 125 | } 126 | 127 | if (signal(SIGINT, sig_int) == SIG_ERR) { 128 | err = errno; 129 | fprintf(stderr, "Can't set signal handler: %s\n", strerror(errno)); 130 | goto cleanup; 131 | } 132 | 133 | printf("Successfully started! Please run `sudo cat /sys/kernel/debug/tracing/trace_pipe` " 134 | "to see output of the BPF program.\n"); 135 | 136 | while (!exiting) { 137 | fprintf(stderr, "."); 138 | sleep(1); 139 | } 140 | 141 | tc_opts.flags = tc_opts.prog_fd = tc_opts.prog_id = 0; 142 | err = bpf_tc_detach(&tc_hook, &tc_opts); 143 | if (err) { 144 | fprintf(stderr, "Failed to detach TC: %d\n", err); 145 | goto cleanup; 146 | } 147 | 148 | cleanup: 149 | if (hook_created) 150 | bpf_tc_hook_destroy(&tc_hook); 151 | tc_bpf__destroy(skel); 152 | return -err; 153 | } 154 | -------------------------------------------------------------------------------- /src/xmake.lua: -------------------------------------------------------------------------------- 1 | add_rules("mode.release", "mode.debug") 2 | add_rules("platform.linux.bpf") 3 | set_license("GPL-2.0") 4 | 5 | if xmake.version():satisfies(">=2.5.7 <=2.5.9") then 6 | on_load(function (target) 7 | raise("xmake(%s) has a bug preventing BPF source code compilation. Please run `xmake update -f 2.5.6` to revert to v2.5.6 version or upgrade to xmake v2.6.1 that fixed the issue.", xmake.version()) 8 | end) 9 | end 10 | 11 | option("system-libbpf", {showmenu = true, default = false, description = "Use system-installed libbpf"}) 12 | option("require-bpftool", {showmenu = true, default = false, description = "Require bpftool package"}) 13 | 14 | add_requires("elfutils", "zlib") 15 | if is_plat("android") then 16 | add_requires("ndk >=22.x", "argp-standalone") 17 | set_toolchains("@ndk", {sdkver = "23"}) 18 | else 19 | add_requires("llvm >=10.x") 20 | set_toolchains("@llvm") 21 | add_requires("linux-headers") 22 | end 23 | 24 | add_includedirs("../../vmlinux") 25 | 26 | -- we can run `xmake f --require-bpftool=y` to pull bpftool from xmake-repo repository 27 | if has_config("require-bpftool") then 28 | add_requires("linux-tools", {configs = {bpftool = true}}) 29 | add_packages("linux-tools") 30 | else 31 | before_build(function (target) 32 | os.addenv("PATH", path.join(os.scriptdir(), "..", "..", "tools")) 33 | end) 34 | end 35 | 36 | -- we use the vendored libbpf sources for libbpf-bootstrap. 37 | -- for some projects you may want to use the system-installed libbpf, so you can run `xmake f --system-libbpf=y` 38 | if has_config("system-libbpf") then 39 | add_requires("libbpf", {system = true}) 40 | else 41 | target("libbpf") 42 | set_kind("static") 43 | set_basename("bpf") 44 | add_files("../../libbpf/src/*.c") 45 | add_includedirs("../../libbpf/include") 46 | add_includedirs("../../libbpf/include/uapi", {public = true}) 47 | add_includedirs("$(buildir)", {interface = true}) 48 | add_configfiles("../../libbpf/src/(*.h)", {prefixdir = "bpf"}) 49 | add_packages("elfutils", "zlib") 50 | if is_plat("android") then 51 | add_defines("__user=", "__force=", "__poll_t=uint32_t") 52 | end 53 | end 54 | 55 | target("minimal") 56 | set_kind("binary") 57 | add_files("minimal.c", "minimal.bpf.c") 58 | add_packages("linux-headers") 59 | if not has_config("system-libbpf") then 60 | add_deps("libbpf") 61 | end 62 | 63 | target("minimal_legacy") 64 | set_kind("binary") 65 | add_files("minimal_legacy.c", "minimal_legacy.bpf.c") 66 | add_packages("linux-headers") 67 | if not has_config("system-libbpf") then 68 | add_deps("libbpf") 69 | end 70 | 71 | target("bootstrap") 72 | set_kind("binary") 73 | add_files("bootstrap.c", "bootstrap.bpf.c") 74 | add_packages("linux-headers") 75 | if not has_config("system-libbpf") then 76 | add_deps("libbpf") 77 | end 78 | if is_plat("android") then 79 | add_packages("argp-standalone") 80 | end 81 | 82 | target("fentry") 83 | set_kind("binary") 84 | add_files("fentry.c", "fentry.bpf.c") 85 | add_packages("linux-headers") 86 | if not has_config("system-libbpf") then 87 | add_deps("libbpf") 88 | end 89 | 90 | target("uprobe") 91 | set_kind("binary") 92 | add_files("uprobe.c", "uprobe.bpf.c") 93 | add_packages("linux-headers") 94 | if not has_config("system-libbpf") then 95 | add_deps("libbpf") 96 | end 97 | 98 | target("kprobe") 99 | set_kind("binary") 100 | add_files("kprobe.c", "kprobe.bpf.c") 101 | add_packages("linux-headers") 102 | if not has_config("system-libbpf") then 103 | add_deps("libbpf") 104 | end 105 | if is_plat("android") then 106 | -- TODO we need fix vmlinux.h to support android 107 | set_default(false) 108 | end 109 | -------------------------------------------------------------------------------- /tools/cmake/FindBpfObject.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | 3 | #[=======================================================================[.rst: 4 | FindBpfObject 5 | -------- 6 | 7 | Find BpfObject 8 | 9 | This module finds if all the dependencies for eBPF Compile-Once-Run-Everywhere 10 | programs are available and where all the components are located. 11 | 12 | The caller may set the following variables to disable automatic 13 | search/processing for the associated component: 14 | 15 | ``BPFOBJECT_BPFTOOL_EXE`` 16 | Path to ``bpftool`` binary 17 | 18 | ``BPFOBJECT_CLANG_EXE`` 19 | Path to ``clang`` binary 20 | 21 | ``LIBBPF_INCLUDE_DIRS`` 22 | Path to ``libbpf`` development headers 23 | 24 | ``LIBBPF_LIBRARIES`` 25 | Path to `libbpf` library 26 | 27 | ``BPFOBJECT_VMLINUX_H`` 28 | Path to ``vmlinux.h`` generated by ``bpftool``. If unset, this module will 29 | attempt to automatically generate a copy. 30 | 31 | This module sets the following result variables: 32 | 33 | :: 34 | 35 | BpfObject_FOUND = TRUE if all components are found 36 | 37 | 38 | This module also provides the ``bpf_object()`` macro. This macro generates a 39 | cmake interface library for the BPF object's generated skeleton as well 40 | as the associated dependencies. 41 | 42 | .. code-block:: cmake 43 | 44 | bpf_object( ) 45 | 46 | Given an abstract ```` for a BPF object and the associated ```` 47 | file, generates an interface library target, ``_skel``, that may be 48 | linked against by other cmake targets. 49 | 50 | Example Usage: 51 | 52 | :: 53 | 54 | find_package(BpfObject REQUIRED) 55 | bpf_object(myobject myobject.bpf.c) 56 | add_executable(myapp myapp.c) 57 | target_link_libraries(myapp myobject_skel) 58 | 59 | #]=======================================================================] 60 | 61 | if(NOT BPFOBJECT_BPFTOOL_EXE) 62 | find_program(BPFOBJECT_BPFTOOL_EXE NAMES bpftool DOC "Path to bpftool executable") 63 | endif() 64 | 65 | if(NOT BPFOBJECT_CLANG_EXE) 66 | find_program(BPFOBJECT_CLANG_EXE NAMES clang DOC "Path to clang executable") 67 | 68 | execute_process(COMMAND ${BPFOBJECT_CLANG_EXE} --version 69 | OUTPUT_VARIABLE CLANG_version_output 70 | ERROR_VARIABLE CLANG_version_error 71 | RESULT_VARIABLE CLANG_version_result 72 | OUTPUT_STRIP_TRAILING_WHITESPACE) 73 | 74 | # Check that clang is new enough 75 | if(${CLANG_version_result} EQUAL 0) 76 | if("${CLANG_version_output}" MATCHES "clang version ([^\n]+)\n") 77 | # Transform X.Y.Z into X;Y;Z which can then be interpreted as a list 78 | set(CLANG_VERSION "${CMAKE_MATCH_1}") 79 | string(REPLACE "." ";" CLANG_VERSION_LIST ${CLANG_VERSION}) 80 | list(GET CLANG_VERSION_LIST 0 CLANG_VERSION_MAJOR) 81 | 82 | # Anything older than clang 10 doesn't really work 83 | string(COMPARE LESS ${CLANG_VERSION_MAJOR} 10 CLANG_VERSION_MAJOR_LT10) 84 | if(${CLANG_VERSION_MAJOR_LT10}) 85 | message(FATAL_ERROR "clang ${CLANG_VERSION} is too old for BPF CO-RE") 86 | endif() 87 | 88 | message(STATUS "Found clang version: ${CLANG_VERSION}") 89 | else() 90 | message(FATAL_ERROR "Failed to parse clang version string: ${CLANG_version_output}") 91 | endif() 92 | else() 93 | message(FATAL_ERROR "Command \"${BPFOBJECT_CLANG_EXE} --version\" failed with output:\n${CLANG_version_error}") 94 | endif() 95 | endif() 96 | 97 | if(NOT LIBBPF_INCLUDE_DIRS OR NOT LIBBPF_LIBRARIES) 98 | find_package(LibBpf) 99 | endif() 100 | 101 | if(BPFOBJECT_VMLINUX_H) 102 | get_filename_component(GENERATED_VMLINUX_DIR ${BPFOBJECT_VMLINUX_H} DIRECTORY) 103 | elseif(BPFOBJECT_BPFTOOL_EXE) 104 | # Generate vmlinux.h 105 | set(GENERATED_VMLINUX_DIR ${CMAKE_CURRENT_BINARY_DIR}) 106 | set(BPFOBJECT_VMLINUX_H ${GENERATED_VMLINUX_DIR}/vmlinux.h) 107 | execute_process(COMMAND ${BPFOBJECT_BPFTOOL_EXE} btf dump file /sys/kernel/btf/vmlinux format c 108 | OUTPUT_FILE ${BPFOBJECT_VMLINUX_H} 109 | ERROR_VARIABLE VMLINUX_error 110 | RESULT_VARIABLE VMLINUX_result) 111 | if(${VMLINUX_result} EQUAL 0) 112 | set(VMLINUX ${BPFOBJECT_VMLINUX_H}) 113 | else() 114 | message(FATAL_ERROR "Failed to dump vmlinux.h from BTF: ${VMLINUX_error}") 115 | endif() 116 | endif() 117 | 118 | include(FindPackageHandleStandardArgs) 119 | find_package_handle_standard_args(BpfObject 120 | REQUIRED_VARS 121 | BPFOBJECT_BPFTOOL_EXE 122 | BPFOBJECT_CLANG_EXE 123 | LIBBPF_INCLUDE_DIRS 124 | LIBBPF_LIBRARIES 125 | GENERATED_VMLINUX_DIR) 126 | 127 | # Get clang bpf system includes 128 | execute_process( 129 | COMMAND bash -c "${BPFOBJECT_CLANG_EXE} -v -E - < /dev/null 2>&1 | 130 | sed -n '/<...> search starts here:/,/End of search list./{ s| \\(/.*\\)|-idirafter \\1|p }'" 131 | OUTPUT_VARIABLE CLANG_SYSTEM_INCLUDES_output 132 | ERROR_VARIABLE CLANG_SYSTEM_INCLUDES_error 133 | RESULT_VARIABLE CLANG_SYSTEM_INCLUDES_result 134 | OUTPUT_STRIP_TRAILING_WHITESPACE) 135 | if(${CLANG_SYSTEM_INCLUDES_result} EQUAL 0) 136 | string(REPLACE "\n" " " CLANG_SYSTEM_INCLUDES ${CLANG_SYSTEM_INCLUDES_output}) 137 | message(STATUS "BPF system include flags: ${CLANG_SYSTEM_INCLUDES}") 138 | else() 139 | message(FATAL_ERROR "Failed to determine BPF system includes: ${CLANG_SYSTEM_INCLUDES_error}") 140 | endif() 141 | 142 | # Get target arch 143 | execute_process(COMMAND uname -m 144 | COMMAND sed -e "s/x86_64/x86/" -e "s/aarch64/arm64/" -e "s/ppc64le/powerpc/" -e "s/mips.*/mips/" 145 | OUTPUT_VARIABLE ARCH_output 146 | ERROR_VARIABLE ARCH_error 147 | RESULT_VARIABLE ARCH_result 148 | OUTPUT_STRIP_TRAILING_WHITESPACE) 149 | if(${ARCH_result} EQUAL 0) 150 | set(ARCH ${ARCH_output}) 151 | message(STATUS "BPF target arch: ${ARCH}") 152 | else() 153 | message(FATAL_ERROR "Failed to determine target architecture: ${ARCH_error}") 154 | endif() 155 | 156 | # Public macro 157 | macro(bpf_object name input) 158 | set(BPF_C_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${input}) 159 | set(BPF_O_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.bpf.o) 160 | set(BPF_SKEL_FILE ${CMAKE_CURRENT_BINARY_DIR}/${name}.skel.h) 161 | set(OUTPUT_TARGET ${name}_skel) 162 | 163 | # Build BPF object file 164 | add_custom_command(OUTPUT ${BPF_O_FILE} 165 | COMMAND ${BPFOBJECT_CLANG_EXE} -g -O2 -target bpf -D__TARGET_ARCH_${ARCH} 166 | ${CLANG_SYSTEM_INCLUDES} -I${GENERATED_VMLINUX_DIR} 167 | -isystem ${LIBBPF_INCLUDE_DIRS} -c ${BPF_C_FILE} -o ${BPF_O_FILE} 168 | VERBATIM 169 | DEPENDS ${BPF_C_FILE} 170 | COMMENT "[clang] Building BPF object: ${name}") 171 | 172 | # Build BPF skeleton header 173 | add_custom_command(OUTPUT ${BPF_SKEL_FILE} 174 | COMMAND bash -c "${BPFOBJECT_BPFTOOL_EXE} gen skeleton ${BPF_O_FILE} > ${BPF_SKEL_FILE}" 175 | VERBATIM 176 | DEPENDS ${BPF_O_FILE} 177 | COMMENT "[skel] Building BPF skeleton: ${name}") 178 | 179 | add_library(${OUTPUT_TARGET} INTERFACE) 180 | target_sources(${OUTPUT_TARGET} INTERFACE ${BPF_SKEL_FILE}) 181 | target_include_directories(${OUTPUT_TARGET} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) 182 | target_include_directories(${OUTPUT_TARGET} SYSTEM INTERFACE ${LIBBPF_INCLUDE_DIRS}) 183 | target_link_libraries(${OUTPUT_TARGET} INTERFACE ${LIBBPF_LIBRARIES} -lelf -lz) 184 | endmacro() 185 | -------------------------------------------------------------------------------- /tools/cmake/FindLibBpf.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | 3 | find_path(LIBBPF_INCLUDE_DIRS 4 | NAMES 5 | bpf/bpf.h 6 | bpf/btf.h 7 | bpf/libbpf.h 8 | PATHS 9 | /usr/include 10 | /usr/local/include 11 | /opt/local/include 12 | /sw/include 13 | ENV CPATH) 14 | 15 | find_library(LIBBPF_LIBRARIES 16 | NAMES 17 | bpf 18 | PATHS 19 | /usr/lib 20 | /usr/local/lib 21 | /opt/local/lib 22 | /sw/lib 23 | ENV LIBRARY_PATH 24 | ENV LD_LIBRARY_PATH) 25 | 26 | include (FindPackageHandleStandardArgs) 27 | 28 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBpf "Please install the libbpf development package" 29 | LIBBPF_LIBRARIES 30 | LIBBPF_INCLUDE_DIRS) 31 | 32 | mark_as_advanced(LIBBPF_INCLUDE_DIRS LIBBPF_LIBRARIES) 33 | -------------------------------------------------------------------------------- /tools/gen_vmlinux_h.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | $(dirname "$0")/bpftool btf dump file ${1:-/sys/kernel/btf/vmlinux} format c 4 | -------------------------------------------------------------------------------- /vmlinux/arm64/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_516.h -------------------------------------------------------------------------------- /vmlinux/vmlinux.h: -------------------------------------------------------------------------------- 1 | x86/vmlinux_508.h -------------------------------------------------------------------------------- /vmlinux/x86/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_508.h --------------------------------------------------------------------------------