├── .github └── workflows │ ├── build.yml │ ├── check.yml │ └── codeql.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── bcc-apps ├── .gitignore ├── README.md ├── c │ ├── Makefile │ ├── README.md │ └── hello_world.c ├── cpp │ ├── Makefile │ ├── README.md │ └── hello_world.cpp └── python │ ├── Makefile │ ├── README.md │ ├── bashreadline.c │ ├── bashreadline.py │ ├── execsnoop.c │ ├── execsnoop.py │ ├── execsnoop_v2.c │ ├── execsnoop_v2.py │ ├── hello.c │ ├── hello.py │ ├── hello_fields.py │ ├── hello_openat.py │ ├── hello_perf.py │ ├── hello_world.py │ ├── python_functions.c │ ├── python_functions.py │ ├── simple_app.py │ ├── simple_app_trace.py │ ├── tcpdrop.py │ ├── trace_open.c │ ├── trace_open.py │ └── trace_openat.py ├── bpf-apps ├── Makefile ├── README.md ├── bashreadline.bpf.c ├── bashreadline.c ├── bashreadline.h ├── bashreadline.skel.h ├── block_shell.bpf.c ├── block_shell.c ├── block_shell.skel.h ├── execsnoop.bpf.c ├── execsnoop.c ├── execsnoop.h ├── execsnoop.skel.h ├── execsnoop_v2.bpf.c ├── execsnoop_v2.c ├── execsnoop_v2.h ├── execsnoop_v2.skel.h ├── hello.bpf.c ├── hello.c ├── hello.skel.h ├── hello_btf.bpf.c ├── hello_btf.c ├── hello_btf.skel.h ├── http_trace.bpf.c ├── http_trace.c ├── http_trace.h ├── http_trace.skel.h ├── https_trace.bpf.c ├── https_trace.c ├── https_trace.h ├── https_trace.skel.h ├── https_trace_bad.bpf.c ├── https_trace_bad.c ├── https_trace_bad.skel.h ├── libbpf │ ├── libbpf.pc │ └── usr │ │ ├── include │ │ └── bpf │ │ │ ├── bpf.h │ │ │ ├── bpf_core_read.h │ │ │ ├── bpf_endian.h │ │ │ ├── bpf_helper_defs.h │ │ │ ├── bpf_helpers.h │ │ │ ├── bpf_tracing.h │ │ │ ├── btf.h │ │ │ ├── libbpf.h │ │ │ ├── libbpf_common.h │ │ │ ├── libbpf_legacy.h │ │ │ ├── libbpf_version.h │ │ │ ├── skel_internal.h │ │ │ └── usdt.bpf.h │ │ └── lib64 │ │ └── pkgconfig │ │ └── libbpf.pc ├── tc_block_tcp.bpf.c ├── tc_block_tcp.c ├── tc_block_tcp.skel.h ├── uprobe_helpers.h ├── vmlinux.h ├── xdp_drop.bpf.c ├── xdp_drop.c ├── xdp_drop.skel.h ├── xdp_drop_test.bpf.c ├── xdp_drop_test.c ├── xdp_drop_test.skel.h ├── xdp_drop_trace.bpf.c ├── xdp_drop_trace.c ├── xdp_drop_trace.skel.h ├── xdppass.bpf.c ├── xdppass.c └── xdppass.skel.h ├── bpftrace ├── README.md ├── block-container-shell.bt ├── bpf_bpf.bt ├── dropwatch-v2.bt ├── dropwatch.bt ├── execsnoop-container.bt ├── execsnoop-v1.bt ├── execsnoop-v2.bt ├── execsnoop-v3.bt ├── execsnoop-v4.bt ├── sslsnoop.bt ├── tcpdrop-new.bt └── tcpdrop-old.bt ├── chatgpt ├── README.md ├── app.py └── doc │ ├── README.md │ ├── reference_guide.md │ └── tutorial_one_liners_chinese.md ├── go ├── README.md ├── go.sum ├── trace.go ├── vmlinux.h ├── xdp_trace.bpf.c ├── xdp_trace_bpfeb.go └── xdp_trace_bpfel.go ├── kernel ├── Makefile ├── README.md ├── hello_kern.c ├── hello_user.c ├── trace_helpers.c └── trace_helpers.h ├── loadbalancer ├── README.md ├── nginx │ ├── Dockerfile │ ├── README.md │ └── nginx.conf ├── sockops │ ├── Makefile │ ├── README.md │ ├── sockops.bpf.c │ ├── sockops.h │ └── sockredir.bpf.c ├── webserver │ ├── Dockerfile │ ├── README.md │ └── nginx.conf └── xdp │ ├── Makefile │ ├── README.md │ ├── vmlinux.h │ ├── xdp-proxy-v2.bpf.c │ ├── xdp-proxy-v2.c │ ├── xdp-proxy-v2.h │ ├── xdp-proxy-v2.skel.h │ ├── xdp-proxy.bpf.c │ ├── xdp-proxy.c │ └── xdp-proxy.skel.h ├── rust ├── README.md ├── hello-aya-xdp │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── hello-aya-common │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── hello-aya-ebpf │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src │ │ │ ├── lib.rs │ │ │ └── main.rs │ ├── hello-aya │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src │ │ │ └── main.rs │ └── rustfmt.toml ├── hello-aya │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── hello-aya-common │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── hello-aya-ebpf │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src │ │ │ ├── lib.rs │ │ │ └── main.rs │ ├── hello-aya │ │ ├── Cargo.toml │ │ ├── build.rs │ │ └── src │ │ │ └── main.rs │ └── rustfmt.toml ├── hello-libbpf │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ ├── bpf │ │ ├── tc.bpf.c │ │ └── tc.skel.rs │ │ └── main.rs ├── tc_block_tcp │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── bpf │ │ ├── mod.rs │ │ ├── tc.bpf.c │ │ ├── tc.skel.rs │ │ └── vmlinux.h │ │ └── main.rs └── xdppass │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── bpf │ ├── mod.rs │ ├── xdppass.bpf.c │ └── xdppass.skel.rs │ └── main.rs ├── tools ├── bpftool └── faddr2line └── windows ├── README.md ├── connection_tracker ├── README.md ├── bpf │ ├── bpf.log │ ├── bpf.vcxproj │ ├── bpf.vcxproj.filters │ ├── bpf.vcxproj.user │ ├── conn_track.c │ └── packages.config ├── conn_track.sln └── conn_track │ ├── conn_track.vcxproj │ ├── conn_track.vcxproj.filters │ ├── conn_track.vcxproj.user │ ├── conn_tracker.cpp │ ├── conn_tracker.h │ └── packages.config ├── hello ├── .DS_Store ├── README.md └── bpf.c └── xdpdrop ├── .DS_Store ├── README.md └── drop.c /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Validation 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | submodules: 'recursive' 19 | - name: Setup 20 | run: | 21 | sudo apt-get update 22 | sudo apt-get install -y make clang llvm libelf-dev 23 | - name: build 24 | run: | 25 | cd bpf-apps 26 | make libbpf 27 | make 28 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: Spell Check 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Setup 17 | run: | 18 | curl -L https://git.io/misspell | bash 19 | - name: Spell Check 20 | run: | 21 | find . -type f | grep -v vmlinux.h | xargs ./bin/misspell 22 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [ "main" ] 9 | schedule: 10 | - cron: '25 11 * * 4' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | permissions: 17 | actions: read 18 | contents: read 19 | security-events: write 20 | 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | language: [ 'cpp', 'python' ] 25 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 26 | # Use only 'java' to analyze code written in Java, Kotlin or both 27 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 28 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 29 | 30 | steps: 31 | - name: Checkout repository 32 | uses: actions/checkout@v3 33 | with: 34 | submodules: 'recursive' 35 | 36 | # Initializes the CodeQL tools for scanning. 37 | - name: Initialize CodeQL 38 | uses: github/codeql-action/init@v2 39 | with: 40 | languages: ${{ matrix.language }} 41 | # If you wish to specify custom queries, you can do so here or in a config file. 42 | # By default, queries listed here will override any specified in a config file. 43 | # Prefix the list here with "+" to use these queries and those in the config file. 44 | 45 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 46 | # queries: security-extended,security-and-quality 47 | 48 | 49 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 50 | # If this step fails, then you should remove it and run the build manually (see below) 51 | # - name: Autobuild 52 | # uses: github/codeql-action/autobuild@v2 53 | # ℹ️ Command-line programs to run using the OS shell. 54 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 55 | 56 | # If the Autobuild fails above, remove it and uncomment the following three lines. 57 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 58 | 59 | - name: Build 60 | run: | 61 | sudo apt-get update 62 | sudo apt-get install -y make clang llvm libelf-dev 63 | cd bpf-apps 64 | make libbpf 65 | make 66 | 67 | - name: Perform CodeQL Analysis 68 | uses: github/codeql-action/analyze@v2 69 | with: 70 | category: "/language:${{matrix.language}}" 71 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Binaries 40 | go/trace 41 | bpf-apps/bashreadline 42 | bpf-apps/block_shell 43 | bpf-apps/execsnoop 44 | bpf-apps/execsnoop_v2 45 | bpf-apps/hello 46 | bpf-apps/hello_btf 47 | bpf-apps/tc_block_tcp 48 | bpf-apps/xdppass 49 | bpf-apps/http_trace 50 | bpf-apps/https_trace 51 | bpf-apps/https_trace_bad 52 | bpf-apps/xdp_drop_test 53 | bpf-apps/xdp_drop 54 | bpf-apps/xdp_drop_trace 55 | 56 | # Debug files 57 | *.dSYM/ 58 | *.su 59 | *.idb 60 | *.pdb 61 | 62 | # Kernel Module Compile Results 63 | *.mod* 64 | *.cmd 65 | .tmp_versions/ 66 | modules.order 67 | Module.symvers 68 | Mkfile.old 69 | dkms.conf 70 | 71 | # Python 72 | .venv 73 | *.pyc 74 | .vscode 75 | 76 | # Rust 77 | target 78 | 79 | # Other 80 | .DS_Store 81 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libbpf"] 2 | path = libbpf 3 | url = https://github.com/libbpf/libbpf 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ebpf-apps 2 | 3 | eBPF sample apps based on [BCC](https://github.com/iovisor/bcc), [libbpf](https://github.com/libbpf/libbpf) and various language bindings. 4 | 5 | ## Contents 6 | 7 | * [bcc-apps](bcc-apps): eBPF samples with BCC. 8 | * [bcc-apps/c](bcc-apps/c): BCC samples with C binding. 9 | * [bcc-apps/cpp](bcc-apps/cpp): BCC samples with C++ binding. 10 | * [bcc-apps/python](bcc-apps/python): BCC samples with Python binding. 11 | * [bpf-apps](bpf-apps): eBPF samples with libbpf and CO-RE. 12 | * [bpftrace](bpftrace): eBPF samples with bpftrace. 13 | * [go](go): eBPF samples with Go bindings. 14 | * [kernel](kernel): eBPF samples built with kernel source. 15 | * [rust](rust): eBPF samples with Rust bindings. 16 | * [tools](tools): Tools for eBPF (e.g. bpftool and faddr2line). 17 | * [chatgpt](chatgpt): generating eBPF programs with ChatGPT. 18 | 19 | ## Pre-requisites 20 | 21 | ### BCC 22 | 23 | [BCC](https://github.com/iovisor/bcc) and its development libraries should be installed. 24 | 25 | #### Install from packages 26 | 27 | Please follow [INSTALL.md](https://github.com/iovisor/bcc/blob/master/INSTALL.md) to see the detailed guides. For example, on Ubuntu or RHEL: 28 | 29 | ```sh 30 | # Ubuntu 31 | sudo apt-get install bpfcc-tools libbpfcc-dev linux-headers-$(uname -r) 32 | 33 | # RHEL 34 | sudo yum install bcc-tools bcc-devel 35 | ``` 36 | 37 | #### Install from source 38 | 39 | Please follow [INSTALL.md](https://github.com/iovisor/bcc/blob/master/INSTALL.md#source) to see the detailed guides. For example, on Ubuntu 20.04+: 40 | 41 | ```sh 42 | sudo apt install -y bison build-essential cmake flex git libedit-dev llvm-dev libclang-dev python zlib1g-dev libelf-dev libfl-dev python3-distutils 43 | 44 | git clone https://github.com/iovisor/bcc.git 45 | mkdir bcc/build; cd bcc/build 46 | cmake .. 47 | make 48 | sudo make install 49 | cmake -DPYTHON_CMD=python3 .. # build python3 binding 50 | pushd src/python/ 51 | make 52 | sudo make install 53 | popd 54 | ``` 55 | 56 | ### libbpf with CO-RE 57 | 58 | To use BTF and CO-RE, `CONFIG_DEBUG_INFO_BTF=y` and `CONFIG_DEBUG_INFO_BTF_MODULES=y` need to be enabled. If you don't want to rebuild the kernel, the following distos have enabled those options by default: 59 | 60 | * Ubuntu 20.10+ 61 | * Fedora 31+ 62 | * RHEL 8.2+ 63 | * Debian 11+ 64 | 65 | And to build bpf applications, the following development tools should also be installed: 66 | 67 | ```sh 68 | # Ubuntu 69 | sudo apt-get install -y make clang llvm libelf-dev linux-tools-$(uname -r) 70 | 71 | # RHEL 72 | sudo yum install -y make clang llvm elfutils-libelf-devel bpftool 73 | ``` 74 | 75 | ## Useful Links 76 | 77 | * [极客时间专栏《eBPF 核心技术与实战》](https://time.geekbang.org/column/intro/100104501) 78 | * [eBPF.io](https://ebpf.io/) 79 | * [Linux kernel BPF samples](https://elixir.bootlin.com/linux/v5.13/source/samples/bpf) 80 | * [BPF and XDP Reference Guide](https://docs.cilium.io/en/latest/bpf/) 81 | * [XDP Hands-On Tutorial](https://github.com/xdp-project/xdp-tutorial) 82 | -------------------------------------------------------------------------------- /bcc-apps/.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | *.pyc 10 | *~ 11 | 12 | # Linker output 13 | *.ilk 14 | *.map 15 | *.exp 16 | 17 | # Precompiled Headers 18 | *.gch 19 | *.pch 20 | 21 | # Libraries 22 | *.lib 23 | *.a 24 | *.la 25 | *.lo 26 | 27 | # Shared objects (inc. Windows DLLs) 28 | *.dll 29 | *.so 30 | *.so.* 31 | *.dylib 32 | 33 | # Executables 34 | *.exe 35 | *.out 36 | *.app 37 | *.i*86 38 | *.x86_64 39 | *.hex 40 | 41 | # Debug files 42 | *.dSYM/ 43 | *.su 44 | *.idb 45 | *.pdb 46 | 47 | # Kernel Module Compile Results 48 | *.mod* 49 | *.cmd 50 | .tmp_versions/ 51 | modules.order 52 | Module.symvers 53 | Mkfile.old 54 | dkms.conf 55 | -------------------------------------------------------------------------------- /bcc-apps/README.md: -------------------------------------------------------------------------------- 1 | # eBPF apps based on BCC (BPF Compiler Collection). 2 | 3 | ## Contents 4 | 5 | ## Contents 6 | 7 | * [c](c): BCC apps with C bindings. 8 | * [cpp](cpp): BCC apps with cpp bindings. 9 | * [python](python): BCC apps with Python bindings. 10 | 11 | ## Pre-requisites 12 | 13 | [BCC](https://github.com/iovisor/bcc) and its development libraries should be installed. 14 | 15 | ### Install from packages 16 | 17 | Please follow [INSTALL.md](https://github.com/iovisor/bcc/blob/master/INSTALL.md) to see the detailed guides. For example, on Ubuntu or RHEL: 18 | 19 | ```sh 20 | # Ubuntu 21 | sudo apt-get install bpfcc-tools libbpfcc-dev linux-headers-$(uname -r) 22 | 23 | # RHEL 24 | sudo yum install bcc-tools bcc-devel 25 | ``` 26 | 27 | ### Install from source 28 | 29 | Please follow [INSTALL.md](https://github.com/iovisor/bcc/blob/master/INSTALL.md#source) to see the detailed guides. For example, on Ubuntu 20.04+: 30 | 31 | ```sh 32 | sudo apt install -y bison build-essential cmake flex git libedit-dev llvm-dev libclang-dev python3 zlib1g-dev libelf-dev libfl-dev python3-setuptools libpolly-18-dev 33 | 34 | git clone https://github.com/iovisor/bcc.git 35 | mkdir bcc/build; cd bcc/build 36 | cmake .. 37 | make 38 | sudo make install 39 | cmake -DPYTHON_CMD=python3 .. # build python3 binding 40 | pushd src/python/ 41 | make 42 | sudo make install 43 | popd 44 | ``` 45 | -------------------------------------------------------------------------------- /bcc-apps/c/Makefile: -------------------------------------------------------------------------------- 1 | APPS = hello_world 2 | 3 | .PHONY: all 4 | all: $(APPS) 5 | 6 | $(APPS): 7 | clang -lbcc -lbcc_bpf $@.c -o $@ 8 | 9 | format: 10 | VERSION_CONTROL=none indent -linux *.c 11 | 12 | clean: 13 | rm -rf $(APPS) 14 | -------------------------------------------------------------------------------- /bcc-apps/c/README.md: -------------------------------------------------------------------------------- 1 | # BCC apps with C bindings 2 | 3 | ## How to build 4 | 5 | ```sh 6 | # Build 7 | make 8 | 9 | # Run 10 | sudo ./hello_world 11 | ``` 12 | -------------------------------------------------------------------------------- /bcc-apps/c/hello_world.c: -------------------------------------------------------------------------------- 1 | // This is a Hello World example with C. 2 | // Source: BPF Performance Tools 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define DEBUGFS "/sys/kernel/debug/tracing" 13 | 14 | char bpf_log_buf[BPF_LOG_BUF_SIZE]; 15 | 16 | int main() 17 | { 18 | int prog_fd, probe_fd; 19 | 20 | struct bpf_insn prog[] = { 21 | BPF_MOV64_IMM(BPF_REG_1, 0xa21), /* '!\n' */ 22 | BPF_STX_MEM(BPF_H, BPF_REG_10, BPF_REG_1, -4), 23 | BPF_MOV64_IMM(BPF_REG_1, 0x646c726f), /* 'orld' */ 24 | BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_1, -8), 25 | BPF_MOV64_IMM(BPF_REG_1, 0x57202c6f), /* 'o, W' */ 26 | BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_1, -12), 27 | BPF_MOV64_IMM(BPF_REG_1, 0x6c6c6548), /* 'Hell' */ 28 | BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_1, -16), 29 | BPF_MOV64_IMM(BPF_REG_1, 0), 30 | BPF_STX_MEM(BPF_B, BPF_REG_10, BPF_REG_1, -2), 31 | BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), 32 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -16), 33 | BPF_MOV64_IMM(BPF_REG_2, 15), 34 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, 35 | BPF_FUNC_trace_printk), 36 | BPF_MOV64_IMM(BPF_REG_0, 0), 37 | BPF_EXIT_INSN(), 38 | }; 39 | size_t insns_cnt = sizeof(prog) / sizeof(struct bpf_insn); 40 | 41 | prog_fd = bpf_load_program(BPF_PROG_TYPE_KPROBE, 42 | prog, 43 | insns_cnt, 44 | "GPL", 45 | LINUX_VERSION_CODE, 46 | bpf_log_buf, BPF_LOG_BUF_SIZE); 47 | if (prog_fd < 0) { 48 | printf("ERROR: failed to load prog '%s'\n", strerror(errno)); 49 | return 1; 50 | } 51 | 52 | probe_fd = 53 | bpf_attach_kprobe(prog_fd, BPF_PROBE_ENTRY, "hello_world", 54 | "do_nanosleep", 0, 0); 55 | if (prog_fd < 0) { 56 | return 2; 57 | } 58 | 59 | system("cat " DEBUGFS "/trace_pipe"); 60 | close(probe_fd); 61 | bpf_detach_kprobe("hello_world"); 62 | close(prog_fd); 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /bcc-apps/cpp/Makefile: -------------------------------------------------------------------------------- 1 | APPS = hello_world 2 | 3 | .PHONY: all 4 | all: $(APPS) 5 | 6 | $(APPS): 7 | clang++ -I /usr/include/bcc/compat -lbcc --std=c++11 $@.cpp -o $@ 8 | 9 | clean: 10 | rm -rf $(APPS) 11 | -------------------------------------------------------------------------------- /bcc-apps/cpp/README.md: -------------------------------------------------------------------------------- 1 | # BCC apps with cpp bindings 2 | 3 | ## How to build 4 | 5 | ```sh 6 | # Build 7 | make 8 | 9 | # Run 10 | sudo ./hello_world 11 | ``` 12 | -------------------------------------------------------------------------------- /bcc-apps/cpp/hello_world.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Facebook, Inc. 3 | * Licensed under the Apache License, Version 2.0 (the "License") 4 | */ 5 | 6 | // Build command: clang++ -I /usr/include/bcc/compat -lbcc --std=c++11 helloworld.cpp -o hello 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | const std::string BPF_PROGRAM = R"( 15 | int on_sys_clone(void *ctx) { 16 | bpf_trace_printk(" Hello, World ! Here I did a sys_clone call ! \n "); 17 | return 0; 18 | } 19 | )"; 20 | 21 | int main() 22 | { 23 | ebpf::BPF bpf; 24 | auto init_res = bpf.init(BPF_PROGRAM); 25 | if (init_res.code() != 0) { 26 | std::cerr << init_res.msg() << std::endl; 27 | return 1; 28 | } 29 | 30 | std::ifstream pipe("/sys/kernel/debug/tracing/trace_pipe"); 31 | std::string line; 32 | std::string clone_fnname = bpf.get_syscall_fnname("clone"); 33 | 34 | auto attach_res = bpf.attach_kprobe(clone_fnname, "on_sys_clone"); 35 | if (attach_res.code() != 0) { 36 | std::cerr << attach_res.msg() << std::endl; 37 | return 1; 38 | } 39 | 40 | while (true) { 41 | if (std::getline(pipe, line)) { 42 | std::cout << line << std::endl; 43 | // Detach the probe if we got at least one line. 44 | auto detach_res = bpf.detach_kprobe(clone_fnname); 45 | if (detach_res.code() != 0) { 46 | std::cerr << detach_res.msg() << std::endl; 47 | return 1; 48 | } 49 | break; 50 | } else { 51 | std::cout << "Waiting for a sys_clone event" << 52 | std::endl; 53 | sleep(1); 54 | } 55 | } 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /bcc-apps/python/Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: format 3 | format: 4 | black . 5 | VERSION_CONTROL=none indent -linux *.c 6 | 7 | .PHONY: install-deps 8 | install-deps: 9 | pip install black --user 10 | -------------------------------------------------------------------------------- /bcc-apps/python/README.md: -------------------------------------------------------------------------------- 1 | # BCC apps with Python bindings 2 | 3 | ## How to run 4 | 5 | Just run the python script with `sudo`, e.g. 6 | 7 | ```sh 8 | sudo ./execsnoop.py 9 | ``` 10 | -------------------------------------------------------------------------------- /bcc-apps/python/bashreadline.c: -------------------------------------------------------------------------------- 1 | /* Tracing bash's readline retval */ 2 | #include 3 | 4 | struct data_t { 5 | u32 uid; 6 | char command[64]; 7 | }; 8 | BPF_PERF_OUTPUT(events); 9 | 10 | int bash_readline(struct pt_regs *ctx) 11 | { 12 | struct data_t data = { }; 13 | data.uid = bpf_get_current_uid_gid(); 14 | 15 | // PT_REGS_RC(ctx) holds the return value. 16 | bpf_probe_read_user(&data.command, sizeof(data.command), 17 | (void *)PT_REGS_RC(ctx)); 18 | 19 | // submit perf event. 20 | events.perf_submit(ctx, &data, sizeof(data)); 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /bcc-apps/python/bashreadline.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Tracing bash's readline retval 3 | from bcc import BPF 4 | from time import strftime 5 | 6 | # load BPF program 7 | b = BPF(src_file="bashreadline.c") 8 | 9 | # attach uretprobe 10 | b.attach_uretprobe(name="/usr/bin/bash", sym="readline", fn_name="bash_readline") 11 | 12 | 13 | # callback for perf event 14 | def print_event(cpu, data, size): 15 | event = b["events"].event(data) 16 | print( 17 | "%-9s %-6d %s" 18 | % (strftime("%H:%M:%S"), event.uid, event.command.decode("utf-8")) 19 | ) 20 | 21 | 22 | # print header 23 | print("%-9s %-6s %s" % ("TIME", "UID", "COMMAND")) 24 | 25 | # loop with callback to print_event 26 | b["events"].open_perf_buffer(print_event) 27 | while 1: 28 | try: 29 | b.perf_buffer_poll() 30 | except KeyboardInterrupt: 31 | exit() 32 | -------------------------------------------------------------------------------- /bcc-apps/python/execsnoop.c: -------------------------------------------------------------------------------- 1 | /* Tracing execve system call. */ 2 | #include 3 | #include 4 | 5 | // consts for arguments (ensure below stack size limit 512) 6 | #define ARGSIZE 64 7 | #define TOTAL_MAX_ARGS 5 8 | #define FULL_MAX_ARGS_ARR (TOTAL_MAX_ARGS * ARGSIZE) 9 | #define LAST_ARG (FULL_MAX_ARGS_ARR - ARGSIZE) 10 | 11 | // perf event map (sharing data to userspace) and hash map (sharing data between tracepoints) 12 | struct data_t { 13 | u32 pid; 14 | char comm[TASK_COMM_LEN]; 15 | int retval; 16 | unsigned int args_size; 17 | char argv[FULL_MAX_ARGS_ARR]; 18 | }; 19 | BPF_PERF_OUTPUT(events); 20 | BPF_HASH(tasks, u32, struct data_t); 21 | 22 | // helper function to read string from userspace. 23 | static int __bpf_read_arg_str(struct data_t *data, const char *ptr) 24 | { 25 | if (data->args_size > LAST_ARG) { 26 | return -1; 27 | } 28 | 29 | int ret = bpf_probe_read_user_str(&data->argv[data->args_size], ARGSIZE, 30 | (void *)ptr); 31 | if (ret > ARGSIZE || ret < 0) { 32 | return -1; 33 | } 34 | // increase the args size. the first tailing '\0' is not counted and hence it 35 | // would be overwritten by the next call. 36 | data->args_size += (ret - 1); 37 | 38 | return 0; 39 | } 40 | 41 | // sys_enter_execve tracepoint. 42 | TRACEPOINT_PROBE(syscalls, sys_enter_execve) 43 | { 44 | // variables definitions 45 | unsigned int ret = 0; 46 | const char **argv = (const char **)(args->argv); 47 | 48 | // get the pid and comm 49 | struct data_t data = { }; 50 | u32 pid = bpf_get_current_pid_tgid(); 51 | data.pid = pid; 52 | bpf_get_current_comm(&data.comm, sizeof(data.comm)); 53 | 54 | // get the binary name (first argment) 55 | if (__bpf_read_arg_str(&data, (const char *)argv[0]) < 0) { 56 | goto out; 57 | } 58 | // get other arguments (skip first arg because it has already been read) 59 | #pragma unroll 60 | for (int i = 1; i < TOTAL_MAX_ARGS; i++) { 61 | if (__bpf_read_arg_str(&data, (const char *)argv[i]) < 0) { 62 | goto out; 63 | } 64 | } 65 | 66 | out: 67 | // store the data in hash map 68 | tasks.update(&pid, &data); 69 | return 0; 70 | } 71 | 72 | // sys_exit_execve tracepoint 73 | TRACEPOINT_PROBE(syscalls, sys_exit_execve) 74 | { 75 | // query the data from hash map 76 | u32 pid = bpf_get_current_pid_tgid(); 77 | struct data_t *data = tasks.lookup(&pid); 78 | 79 | // submit perf events after getting the retval 80 | if (data != NULL) { 81 | data->retval = args->ret; 82 | events.perf_submit(args, data, sizeof(struct data_t)); 83 | 84 | // clean up the hash map 85 | tasks.delete(&pid); 86 | } 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /bcc-apps/python/execsnoop.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Tracing execve() system call. 3 | from bcc import BPF 4 | from bcc.utils import printb 5 | 6 | 7 | # 1) load BPF program 8 | b = BPF(src_file="execsnoop.c") 9 | 10 | # 2) print header 11 | print("%-6s %-16s %-3s %s" % ("PID", "COMM", "RET", "ARGS")) 12 | 13 | 14 | # 3) define the callback for perf event 15 | def print_event(cpu, data, size): 16 | # event data struct is generated from "struct data_t" by bcc 17 | event = b["events"].event(data) 18 | printb(b"%-6d %-16s %-3d %-16s" % (event.pid, event.comm, event.retval, event.argv)) 19 | 20 | 21 | # 4) loop with callback to print_event 22 | b["events"].open_perf_buffer(print_event) 23 | while 1: 24 | try: 25 | b.perf_buffer_poll() 26 | except KeyboardInterrupt: 27 | exit() 28 | -------------------------------------------------------------------------------- /bcc-apps/python/execsnoop_v2.c: -------------------------------------------------------------------------------- 1 | /* Tracing execve system call. */ 2 | #include 3 | #include 4 | 5 | // consts for arguments (ensure below stack size limit 512) 6 | #define ARGSIZE 64 7 | #define TOTAL_MAX_ARGS 5 8 | #define FULL_MAX_ARGS_ARR (TOTAL_MAX_ARGS * ARGSIZE) 9 | #define LAST_ARG (FULL_MAX_ARGS_ARR - ARGSIZE) 10 | 11 | // perf event map (sharing data to userspace) and hash map (sharing data between tracepoints) 12 | struct data_t { 13 | u32 pid; 14 | char comm[TASK_COMM_LEN]; 15 | int retval; 16 | unsigned int args_size; 17 | char argv[FULL_MAX_ARGS_ARR]; 18 | }; 19 | BPF_PERF_OUTPUT(events); 20 | BPF_HASH(tasks, u32, struct data_t); 21 | 22 | // helper function to read string from userspace. 23 | static int __bpf_read_arg_str(struct data_t *data, const char *ptr) 24 | { 25 | if (data->args_size > LAST_ARG) { 26 | return -1; 27 | } 28 | 29 | int ret = bpf_probe_read_user_str(&data->argv[data->args_size], ARGSIZE, 30 | (void *)ptr); 31 | if (ret > ARGSIZE || ret < 0) { 32 | return -1; 33 | } 34 | // increase the args size. the first tailing '\0' is not counted and hence it 35 | // would be overwritten by the next call. 36 | data->args_size += (ret - 1); 37 | 38 | return 0; 39 | } 40 | 41 | // help function to read given size of data from ptr. 42 | static int __bpf_read_arg(struct data_t *data, const char *ptr, int size) 43 | { 44 | if (data->args_size > LAST_ARG) { 45 | return -1; 46 | } 47 | 48 | int ret = 49 | bpf_probe_read(&data->argv[data->args_size], ARGSIZE, (void *)ptr); 50 | if (ret < 0) { 51 | return -1; 52 | } 53 | 54 | data->args_size += size; 55 | return 0; 56 | } 57 | 58 | // sys_enter_execve tracepoint. 59 | TRACEPOINT_PROBE(syscalls, sys_enter_execve) 60 | { 61 | // variables definitions 62 | const char spaces[] = " "; 63 | const char ellipsis[] = "..."; 64 | unsigned int ret = 0; 65 | 66 | // tracepoint arguments are available in args struct, which are same as tracepoint arguments. 67 | const char **argv = (const char **)(args->argv); 68 | 69 | // get the pid and comm 70 | struct data_t data = { }; 71 | u32 pid = bpf_get_current_pid_tgid(); 72 | data.pid = pid; 73 | bpf_get_current_comm(&data.comm, sizeof(data.comm)); 74 | 75 | // get the binary name (first argment) 76 | if (__bpf_read_arg_str(&data, (const char *)argv[0]) < 0) { 77 | goto out; 78 | } 79 | // append a space for better reading 80 | if (__bpf_read_arg(&data, (const char *)spaces, 1) < 0) { 81 | goto out; 82 | } 83 | // get other arguments (skip first arg because it has already been read) 84 | #pragma unroll 85 | for (int i = 1; i < TOTAL_MAX_ARGS; i++) { 86 | if (__bpf_read_arg_str(&data, (const char *)argv[i]) < 0) { 87 | goto out; 88 | } 89 | // append a space for better reading 90 | if (i < TOTAL_MAX_ARGS - 1 91 | && __bpf_read_arg(&data, (const char *)spaces, 1) < 0) { 92 | goto out; 93 | } 94 | } 95 | 96 | // handle truncated argument list by showing "..." at the end 97 | if (data.args_size < FULL_MAX_ARGS_ARR - 4) { 98 | __bpf_read_arg(&data, (const char *)ellipsis, 3); 99 | } 100 | 101 | out: 102 | // store the data in hash map 103 | tasks.update(&pid, &data); 104 | return 0; 105 | } 106 | 107 | // sys_exit_execve tracepoint 108 | TRACEPOINT_PROBE(syscalls, sys_exit_execve) 109 | { 110 | // query the data from hash map 111 | u32 pid = bpf_get_current_pid_tgid(); 112 | struct data_t *data = tasks.lookup(&pid); 113 | 114 | // submit perf events after getting the retval 115 | if (data != NULL) { 116 | data->retval = args->ret; 117 | events.perf_submit(args, data, sizeof(struct data_t)); 118 | 119 | // clean up the hash map 120 | tasks.delete(&pid); 121 | } 122 | 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /bcc-apps/python/execsnoop_v2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Tracing execve() system call. 3 | from bcc import BPF 4 | from bcc.utils import printb 5 | 6 | 7 | # 1) load BPF program 8 | b = BPF(src_file="execsnoop_v2.c") 9 | 10 | # 2) print header 11 | print("%-6s %-16s %-3s %s" % ("PID", "COMM", "RET", "ARGS")) 12 | 13 | 14 | # 3) define the callback for perf event 15 | def print_event(cpu, data, size): 16 | # event data struct is generated from "struct data_t" by bcc 17 | event = b["events"].event(data) 18 | printb(b"%-6d %-16s %-3d %-16s" % (event.pid, event.comm, event.retval, event.argv)) 19 | 20 | 21 | # 4) loop with callback to print_event 22 | b["events"].open_perf_buffer(print_event) 23 | while 1: 24 | try: 25 | b.perf_buffer_poll() 26 | except KeyboardInterrupt: 27 | exit() 28 | -------------------------------------------------------------------------------- /bcc-apps/python/hello.c: -------------------------------------------------------------------------------- 1 | // This is a Hello World example of BPF. 2 | int hello_world(void *ctx) 3 | { 4 | bpf_trace_printk("Hello, World!"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /bcc-apps/python/hello.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # This is a Hello World example of BPF. 3 | from bcc import BPF 4 | 5 | # load BPF program 6 | b = BPF(src_file="hello.c") 7 | b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world") 8 | b.trace_print() 9 | -------------------------------------------------------------------------------- /bcc-apps/python/hello_fields.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # This is a Hello World example that formats output as fields. 4 | 5 | from bcc import BPF 6 | from bcc.utils import printb 7 | 8 | # define BPF program 9 | prog = """ 10 | int hello(void *ctx) { 11 | bpf_trace_printk("Hello, World!\\n"); 12 | return 0; 13 | } 14 | """ 15 | 16 | # load BPF program 17 | b = BPF(text=prog) 18 | b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello") 19 | 20 | # header 21 | print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE")) 22 | 23 | # format output 24 | while 1: 25 | try: 26 | (task, pid, cpu, flags, ts, msg) = b.trace_fields() 27 | except ValueError: 28 | continue 29 | except KeyboardInterrupt: 30 | exit() 31 | printb(b"%-18.9f %-16s %-6d %s" % (ts, task, pid, msg)) 32 | -------------------------------------------------------------------------------- /bcc-apps/python/hello_openat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # This is a Hello World example of BPF. 3 | from bcc import BPF 4 | 5 | # load BPF program 6 | b = BPF(src_file="hello.c") 7 | b.attach_kprobe(event="__x64_sys_openat", fn_name="hello_world") 8 | b.trace_print() 9 | -------------------------------------------------------------------------------- /bcc-apps/python/hello_perf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Refer https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md#lesson-7-hello_perf_outputpy 3 | from bcc import BPF 4 | 5 | # define BPF program 6 | prog = """ 7 | #include 8 | 9 | // define output data structure in C 10 | struct data_t { 11 | u32 pid; 12 | u64 ts; 13 | char comm[TASK_COMM_LEN]; 14 | }; 15 | BPF_PERF_OUTPUT(events); 16 | 17 | int hello(struct pt_regs *ctx) { 18 | struct data_t data = {}; 19 | 20 | data.pid = bpf_get_current_pid_tgid(); 21 | data.ts = bpf_ktime_get_ns(); 22 | bpf_get_current_comm(&data.comm, sizeof(data.comm)); 23 | 24 | events.perf_submit(ctx, &data, sizeof(data)); 25 | 26 | return 0; 27 | } 28 | """ 29 | 30 | # load BPF program 31 | b = BPF(text=prog) 32 | b.attach_kprobe(event=b.get_syscall_fnname("clone"), fn_name="hello") 33 | 34 | # header 35 | print("%-18s %-16s %-6s %s" % ("TIME(s)", "COMM", "PID", "MESSAGE")) 36 | 37 | # process event 38 | start = 0 39 | 40 | 41 | def print_event(cpu, data, size): 42 | global start 43 | event = b["events"].event(data) 44 | if start == 0: 45 | start = event.ts 46 | time_s = (float(event.ts - start)) / 1000000000 47 | print( 48 | "%-18.9f %-16s %-6d %s" % (time_s, event.comm, event.pid, "Hello, perf_output!") 49 | ) 50 | 51 | 52 | # loop with callback to print_event 53 | b["events"].open_perf_buffer(print_event) 54 | while 1: 55 | b.perf_buffer_poll() 56 | -------------------------------------------------------------------------------- /bcc-apps/python/hello_world.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # This is a Hello World example of BPF. 4 | from bcc import BPF 5 | 6 | # define BPF program 7 | prog = """ 8 | int kprobe__sys_clone(void *ctx) 9 | { 10 | bpf_trace_printk("Hello, World!\\n"); 11 | return 0; 12 | } 13 | """ 14 | 15 | # load BPF program 16 | b = BPF(text=prog) 17 | b.trace_print() 18 | -------------------------------------------------------------------------------- /bcc-apps/python/python_functions.c: -------------------------------------------------------------------------------- 1 | /* Tracing Python functions */ 2 | #include 3 | 4 | struct data_t { 5 | char filename[128]; 6 | char funcname[128]; 7 | int lineno; 8 | }; 9 | BPF_PERF_OUTPUT(events); 10 | 11 | int print_functions(struct pt_regs *ctx) 12 | { 13 | uint64_t argptr; 14 | struct data_t data = { }; 15 | 16 | /* function__entry params are (str filename, str funcname, int lineno) */ 17 | bpf_usdt_readarg(1, ctx, &argptr); 18 | bpf_probe_read_user(&data.filename, sizeof(data.filename), 19 | (void *)argptr); 20 | bpf_usdt_readarg(2, ctx, &argptr); 21 | bpf_probe_read_user(&data.funcname, sizeof(data.funcname), 22 | (void *)argptr); 23 | bpf_usdt_readarg(3, ctx, &data.lineno); 24 | 25 | // submit perf event. 26 | events.perf_submit(ctx, &data, sizeof(data)); 27 | return 0; 28 | }; 29 | -------------------------------------------------------------------------------- /bcc-apps/python/python_functions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # Tracing Python functions 4 | import subprocess 5 | from bcc import BPF, USDT 6 | from bcc.utils import printb 7 | 8 | # find the PID for "python3 -m http.server" 9 | cmd = subprocess.Popen( 10 | ["pgrep", "-f", "http.server"], stdout=subprocess.PIPE, shell=False 11 | ).communicate() 12 | if cmd[0]: 13 | pid = int(cmd[0].decode("ascii").strip()) 14 | else: 15 | print("ERROR: cannot find PID for python3 -m http.server") 16 | exit() 17 | 18 | # load BPF program 19 | u = USDT(pid=pid) 20 | u.enable_probe(probe="function__entry", fn_name="print_functions") 21 | b = BPF(src_file="python-functions.c", usdt_contexts=[u]) 22 | 23 | 24 | # callback for perf event 25 | def print_event(cpu, data, size): 26 | event = b["events"].event(data) 27 | printb(b"%-9s %-6d %s" % (event.filename, event.lineno, event.funcname)) 28 | 29 | 30 | # print header 31 | print("%-9s %-6s %s" % ("FILENAME", "LINENO", "FUNCTION")) 32 | 33 | # loop with callback to print_event 34 | b["events"].open_perf_buffer(print_event) 35 | while 1: 36 | try: 37 | b.perf_buffer_poll() 38 | except KeyboardInterrupt: 39 | exit() 40 | -------------------------------------------------------------------------------- /bcc-apps/python/simple_app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # A simple sample app with USDT. 4 | # Please follow https://github.com/sthima/python-stapsdt to install dependencies. 5 | import stapsdt 6 | import http.server 7 | import socketserver 8 | from urllib import parse 9 | 10 | # Add a USDT probe. 11 | provider = stapsdt.Provider("simple_app") 12 | probe = provider.add_probe("sum", stapsdt.ArgTypes.int32, stapsdt.ArgTypes.int32) 13 | provider.load() 14 | 15 | 16 | class MyHTTPHandler(http.server.SimpleHTTPRequestHandler): 17 | def do_GET(self): 18 | query = parse.parse_qs(parse.urlparse(self.path).query) 19 | a = int(query.get("a", [1])[0]) 20 | b = int(query.get("b", [2])[0]) 21 | self.send_response(200) 22 | self.send_header("Content-type", "text/html") 23 | self.end_headers() 24 | self.wfile.write(str(sum(a, b)).encode()) 25 | 26 | 27 | def sum(a, b): 28 | probe.fire(a, b) 29 | return a + b 30 | 31 | 32 | if __name__ == "__main__": 33 | my_server = socketserver.TCPServer(("", 8080), MyHTTPHandler) 34 | my_server.allow_reuse_address = True 35 | my_server.serve_forever() 36 | -------------------------------------------------------------------------------- /bcc-apps/python/simple_app_trace.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # 3 | # Tracing Python USDT. 4 | # Start ./simple_app.py before starting this trace script. 5 | import subprocess 6 | from bcc import BPF, USDT 7 | from bcc.utils import printb 8 | 9 | prog = """#include 10 | 11 | struct data_t { 12 | int a; 13 | int b; 14 | }; 15 | BPF_PERF_OUTPUT(events); 16 | 17 | int print_functions(struct pt_regs *ctx) 18 | { 19 | uint64_t argptr; 20 | struct data_t data = { }; 21 | 22 | /* function__entry params are (int a, int b) */ 23 | bpf_usdt_readarg(1, ctx, &data.a); 24 | bpf_usdt_readarg(2, ctx, &data.b); 25 | events.perf_submit(ctx, &data, sizeof(data)); 26 | return 0; 27 | }; 28 | """ 29 | 30 | # find the PID for "simple_app.py" 31 | cmd = subprocess.Popen( 32 | ["pgrep", "-f", "simple_app.py"], stdout=subprocess.PIPE, shell=False 33 | ).communicate() 34 | if cmd[0]: 35 | pid = int(cmd[0].decode("ascii").strip()) 36 | else: 37 | print("ERROR: cannot find PID for simple_app.py") 38 | exit() 39 | 40 | # load BPF program 41 | u = USDT(pid=pid) 42 | u.enable_probe(probe="sum", fn_name="print_functions") 43 | b = BPF(text=prog, usdt_contexts=[u]) 44 | 45 | 46 | # callback for perf event 47 | def print_event(cpu, data, size): 48 | event = b["events"].event(data) 49 | printb(b"%-6d %-6d" % (event.a, event.b)) 50 | 51 | 52 | # print header 53 | print("%-6s %-6s" % ("a", "b")) 54 | 55 | # loop with callback to print_event 56 | b["events"].open_perf_buffer(print_event) 57 | while 1: 58 | try: 59 | b.perf_buffer_poll() 60 | except KeyboardInterrupt: 61 | exit() 62 | -------------------------------------------------------------------------------- /bcc-apps/python/trace_open.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // define the output data structure. 5 | struct data_t { 6 | u32 pid; 7 | u64 ts; 8 | char comm[TASK_COMM_LEN]; 9 | char fname[NAME_MAX]; 10 | }; 11 | BPF_PERF_OUTPUT(events); 12 | 13 | // define the handler for kprobe. 14 | // refer https://elixir.bootlin.com/linux/latest/source/fs/open.c#L1196 for the param definitions. 15 | int hello_world(struct pt_regs *ctx, int dfd, const char __user * filename, 16 | struct open_how *how) 17 | { 18 | struct data_t data = { }; 19 | 20 | data.pid = bpf_get_current_pid_tgid(); 21 | data.ts = bpf_ktime_get_ns(); 22 | if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) { 23 | bpf_probe_read(&data.fname, sizeof(data.fname), 24 | (void *)filename); 25 | } 26 | 27 | events.perf_submit(ctx, &data, sizeof(data)); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /bcc-apps/python/trace_open.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Tracing openat2() system call. 3 | from bcc import BPF 4 | from bcc.utils import printb 5 | 6 | 7 | # 1) load BPF program 8 | b = BPF(src_file="trace_open.c") 9 | b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world") 10 | 11 | # 2) print header 12 | print("%-18s %-16s %-6s %-16s" % ("TIME(s)", "COMM", "PID", "FILE")) 13 | 14 | # 3) define the callback for perf event 15 | start = 0 16 | 17 | 18 | def print_event(cpu, data, size): 19 | global start 20 | event = b["events"].event(data) 21 | if start == 0: 22 | start = event.ts 23 | time_s = (float(event.ts - start)) / 1000000000 24 | printb(b"%-18.9f %-16s %-6d %-16s" % (time_s, event.comm, event.pid, event.fname)) 25 | 26 | 27 | # 4) loop with callback to print_event 28 | b["events"].open_perf_buffer(print_event) 29 | while 1: 30 | try: 31 | b.perf_buffer_poll() 32 | except KeyboardInterrupt: 33 | exit() 34 | -------------------------------------------------------------------------------- /bcc-apps/python/trace_openat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Tracing openat() system call. 3 | from bcc import BPF 4 | from bcc.utils import printb 5 | 6 | prog =''' 7 | #include 8 | #include 9 | #include 10 | 11 | // define the output data structure. 12 | struct data_t { 13 | u32 pid; 14 | u64 ts; 15 | char comm[TASK_COMM_LEN]; 16 | char fname[NAME_MAX]; 17 | }; 18 | BPF_PERF_OUTPUT(events); 19 | 20 | TRACEPOINT_PROBE(syscalls, sys_enter_openat) 21 | { 22 | struct data_t data = { }; 23 | 24 | data.pid = bpf_get_current_pid_tgid(); 25 | data.ts = bpf_ktime_get_ns(); 26 | if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0) { 27 | bpf_probe_read_user(&data.fname, sizeof(data.fname), args->filename); 28 | } 29 | 30 | events.perf_submit(args, &data, sizeof(data)); 31 | return 0; 32 | } 33 | ''' 34 | 35 | # 1) load BPF program 36 | b = BPF(text=prog) 37 | 38 | # 2) print header 39 | print("%-18s %-16s %-6s %-16s" % ("TIME(s)", "COMM", "PID", "FILE")) 40 | 41 | # 3) define the callback for perf event 42 | start = 0 43 | 44 | 45 | def print_event(cpu, data, size): 46 | global start 47 | event = b["events"].event(data) 48 | if start == 0: 49 | start = event.ts 50 | time_s = (float(event.ts - start)) / 1000000000 51 | printb(b"%-18.9f %-16s %-6d %-16s" % (time_s, event.comm, event.pid, event.fname)) 52 | 53 | 54 | # 4) loop with callback to print_event 55 | b["events"].open_perf_buffer(print_event) 56 | while 1: 57 | try: 58 | b.perf_buffer_poll() 59 | except KeyboardInterrupt: 60 | exit() 61 | -------------------------------------------------------------------------------- /bpf-apps/Makefile: -------------------------------------------------------------------------------- 1 | APPS = hello execsnoop execsnoop_v2 bashreadline hello_btf block_shell xdppass tc_block_tcp http_trace https_trace https_trace_bad xdp_drop_test xdp_drop xdp_drop_trace 2 | bpftool = $(shell which bpftool || ../tools/bpftool) 3 | LIBBPF_SRC := $(abspath ../libbpf/src) 4 | LIBBPF_OBJ := $(abspath libbpf/libbpf.a) 5 | INCLUDES := -Ilibbpf/usr/include -I../libbpf/include/uapi -I/usr/include/x86_64-linux-gnu -I. 6 | 7 | .PHONY: all 8 | all: $(APPS) 9 | 10 | $(APPS): %: %.bpf.c %.c $(LIBBPF_OBJ) $(wildcard %.h) 11 | clang -g -O2 -target bpf -D__TARGET_ARCH_x86 $(INCLUDES) -c $@.bpf.c -o $@.bpf.o 12 | $(bpftool) gen skeleton $@.bpf.o > $@.skel.h 13 | clang -g -O2 -Wall $(INCLUDES) -c $@.c -o $@.o 14 | clang -Wall -O2 -g $@.o -static $(LIBBPF_OBJ) -lelf -lz -lzstd -o $@ 15 | 16 | vmlinux: 17 | $(bpftool) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h 18 | 19 | libbpf: $(LIBBPF_OBJ) 20 | 21 | $(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) 22 | make -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 OBJDIR=$(dir $@) DESTDIR=$(dir $@) install 23 | 24 | format: 25 | VERSION_CONTROL=none indent -linux *.h *.c 26 | 27 | clean: 28 | rm -rf $(APPS) *.o 29 | -------------------------------------------------------------------------------- /bpf-apps/README.md: -------------------------------------------------------------------------------- 1 | # eBPF apps with [libbpf](https://github.com/libbpf/libbpf) 2 | 3 | ## Contents 4 | 5 | * `hello`: hello world for libbpf. 6 | * `execsnoop` and `execsnoop_v2`: kprobe for tracepoint/syscalls/sys_enter_execve. 7 | * `bashreadline`: uprobe for bash's readline. 8 | * `hello_btf`: custom BTF Hello world for kernel without built-in BTF support. 9 | 10 | ## Pre-requisites 11 | 12 | To use BTF and CO-RE, `CONFIG_DEBUG_INFO_BTF=y` and `CONFIG_DEBUG_INFO_BTF_MODULES=y` need to be enabled. If you don't want to rebuild the kernel, the following distos have enabled those options by default: 13 | 14 | * Ubuntu 20.10+ 15 | * Fedora 31+ 16 | * RHEL 8.2+ 17 | * Debian 11+ 18 | 19 | And to build bpf applications, the following development tools should also be installed: 20 | 21 | ```sh 22 | # Ubuntu 23 | sudo apt-get install -y make clang llvm libelf-dev linux-tools-$(uname -r) 24 | 25 | # RHEL 26 | sudo yum install -y make clang llvm elfutils-libelf-devel bpftool 27 | ``` 28 | -------------------------------------------------------------------------------- /bpf-apps/bashreadline.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* Copyright (c) 2021 Facebook */ 3 | /* Refer https://github.com/iovisor/bcc/blob/master/libbpf-tools/bashreadline.bpf.c */ 4 | #include "vmlinux.h" 5 | #include 6 | #include 7 | #include "bashreadline.h" 8 | 9 | #define TASK_COMM_LEN 16 10 | 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 13 | __uint(key_size, sizeof(__u32)); 14 | __uint(value_size, sizeof(__u32)); 15 | } events SEC(".maps"); 16 | 17 | SEC("uretprobe/readline") 18 | int BPF_KRETPROBE(printret, const void *ret) 19 | { 20 | struct str_t data; 21 | char comm[TASK_COMM_LEN]; 22 | u32 pid; 23 | 24 | if (!ret) 25 | return 0; 26 | 27 | bpf_get_current_comm(&comm, sizeof(comm)); 28 | pid = bpf_get_current_pid_tgid() >> 32; 29 | data.pid = pid; 30 | bpf_probe_read_user(&data.str, sizeof(data.str), ret); 31 | 32 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, 33 | sizeof(data)); 34 | return 0; 35 | }; 36 | 37 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 38 | -------------------------------------------------------------------------------- /bpf-apps/bashreadline.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2021 Facebook */ 3 | /* Refer https://github.com/iovisor/bcc/blob/master/libbpf-tools/bashreadline.c */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include "bashreadline.h" 13 | #include "bashreadline.skel.h" 14 | #include "uprobe_helpers.h" 15 | 16 | #define PERF_BUFFER_PAGES 16 17 | #define PERF_POLL_TIMEOUT_MS 100 18 | #define warn(...) fprintf(stderr, __VA_ARGS__) 19 | 20 | static bool verbose = false; 21 | 22 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, 23 | va_list args) 24 | { 25 | if (level == LIBBPF_DEBUG && !verbose) 26 | return 0; 27 | return vfprintf(stderr, format, args); 28 | } 29 | 30 | static void handle_event(void *ctx, int cpu, void *data, __u32 data_size) 31 | { 32 | struct str_t *e = (struct str_t *)data; 33 | struct tm *tm; 34 | char ts[16]; 35 | time_t t; 36 | 37 | time(&t); 38 | tm = localtime(&t); 39 | strftime(ts, sizeof(ts), "%H:%m:%S", tm); 40 | 41 | printf("%-9s %-7d %s\n", ts, e->pid, e->str); 42 | } 43 | 44 | static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) 45 | { 46 | warn("lost %llu events on CPU #%d\n", lost_cnt, cpu); 47 | } 48 | 49 | int main(int argc, char **argv) 50 | { 51 | struct bashreadline_bpf *obj = NULL; 52 | struct perf_buffer *pb = NULL; 53 | off_t func_off; 54 | int err = 0; 55 | 56 | libbpf_set_strict_mode(LIBBPF_STRICT_ALL); 57 | libbpf_set_print(libbpf_print_fn); 58 | 59 | obj = bashreadline_bpf__open_and_load(); 60 | if (!obj) { 61 | warn("failed to open and load BPF object\n"); 62 | goto cleanup; 63 | } 64 | 65 | func_off = get_elf_func_offset("/usr/bin/bash", "readline"); 66 | if (func_off < 0) { 67 | warn("cound not find readline in bash\n"); 68 | goto cleanup; 69 | } 70 | 71 | obj->links.printret = 72 | bpf_program__attach_uprobe(obj->progs.printret, true, -1, 73 | "/usr/bin/bash", func_off); 74 | if (!obj->links.printret) { 75 | err = -errno; 76 | warn("failed to attach readline: %d\n", err); 77 | goto cleanup; 78 | } 79 | 80 | pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES, 81 | handle_event, handle_lost_events, NULL, NULL); 82 | if (!pb) { 83 | err = -errno; 84 | warn("failed to open perf buffer: %d\n", err); 85 | goto cleanup; 86 | } 87 | 88 | printf("%-9s %-7s %s\n", "TIME", "PID", "COMMAND"); 89 | 90 | /* Main polling loop */ 91 | while ((err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS)) >= 0) ; 92 | printf("Error polling perf buffer: %d\n", err); 93 | 94 | cleanup: 95 | perf_buffer__free(pb); 96 | bashreadline_bpf__destroy(obj); 97 | 98 | return err != 0; 99 | } 100 | -------------------------------------------------------------------------------- /bpf-apps/bashreadline.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2021 Facebook */ 3 | /* Refer https://github.com/iovisor/bcc/blob/master/libbpf-tools/bashreadline.h */ 4 | #ifndef __BASHREADLINE_H 5 | #define __BASHREADLINE_H 6 | 7 | #define MAX_LINE_SIZE 80 8 | 9 | struct str_t { 10 | __u32 pid; 11 | char str[MAX_LINE_SIZE]; 12 | }; 13 | 14 | #endif /* __BASHREADLINE_H */ 15 | -------------------------------------------------------------------------------- /bpf-apps/block_shell.bpf.c: -------------------------------------------------------------------------------- 1 | /* Blocking all bash commands */ 2 | /* Require: "CONFIG_BPF_LSM=y" and CONFIG_LSM="bpf,..." */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #define EPERM 1 9 | 10 | #ifndef NULL 11 | #define NULL 0 12 | #endif 13 | 14 | static __always_inline int handle_new_process(struct task_struct *parent, 15 | struct task_struct *child) 16 | { 17 | char bash[] = "bash"; 18 | 19 | pid_t pid = BPF_CORE_READ(child, pid); 20 | char comm[16]; 21 | bpf_get_current_comm(&comm, sizeof(comm)); 22 | u64 pidns = child->nsproxy->pid_ns_for_children->ns.inum; 23 | 24 | for (int i = 0; i < sizeof(bash); i++) { 25 | if (comm[i] != bash[i]) { 26 | return 0; 27 | } 28 | } 29 | 30 | bpf_printk("lsm: blocking %s (pid: %d) in pidns %ld\n", comm, pid, 31 | pidns); 32 | return -EPERM; 33 | } 34 | 35 | SEC("lsm/task_alloc") 36 | int BPF_PROG(task_alloc, struct task_struct *task, unsigned long clone_flags, 37 | int ret_prev) 38 | { 39 | struct task_struct *parent = BPF_CORE_READ(task, real_parent); 40 | if (parent == NULL) { 41 | return -EPERM; /* Shouldn't happen */ 42 | } 43 | 44 | /* Handle results of previous programs */ 45 | if (ret_prev != 0) { 46 | return ret_prev; 47 | } 48 | 49 | return handle_new_process(parent, task); 50 | } 51 | 52 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 53 | -------------------------------------------------------------------------------- /bpf-apps/block_shell.c: -------------------------------------------------------------------------------- 1 | /* Blocking all bash commands */ 2 | /* Require: "CONFIG_BPF_LSM=y" and CONFIG_LSM="bpf,..." */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "block_shell.skel.h" 8 | 9 | int main(int argc, char **argv) 10 | { 11 | struct block_shell_bpf *obj; 12 | int err = 0; 13 | 14 | struct rlimit rlim_new = { 15 | .rlim_cur = RLIM_INFINITY, 16 | .rlim_max = RLIM_INFINITY, 17 | }; 18 | 19 | err = setrlimit(RLIMIT_MEMLOCK, &rlim_new); 20 | if (err) { 21 | fprintf(stderr, "failed to change rlimit\n"); 22 | return 1; 23 | } 24 | obj = block_shell_bpf__open(); 25 | if (!obj) { 26 | fprintf(stderr, "failed to open and/or load BPF object\n"); 27 | return 1; 28 | } 29 | 30 | err = block_shell_bpf__load(obj); 31 | if (err) { 32 | fprintf(stderr, "failed to load BPF object %d\n", err); 33 | goto cleanup; 34 | } 35 | 36 | err = block_shell_bpf__attach(obj); 37 | if (err) { 38 | fprintf(stderr, "failed to attach BPF programs\n"); 39 | goto cleanup; 40 | } 41 | 42 | printf 43 | ("Successfully started! Tracing /sys/kernel/debug/tracing/trace_pipe...\n"); 44 | 45 | system("cat /sys/kernel/debug/tracing/trace_pipe"); 46 | 47 | cleanup: 48 | block_shell_bpf__destroy(obj); 49 | return err != 0; 50 | } 51 | -------------------------------------------------------------------------------- /bpf-apps/execsnoop.bpf.c: -------------------------------------------------------------------------------- 1 | // Uncomment this line if don't have BTF on the running machine. 2 | // #define BPF_NO_PRESERVE_ACCESS_INDEX 3 | 4 | #include "vmlinux.h" 5 | #include 6 | #include "execsnoop.h" 7 | 8 | static const struct event empty_event = { }; 9 | 10 | // define hash map and perf event map 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_HASH); 13 | __uint(max_entries, 10240); 14 | __type(key, pid_t); 15 | __type(value, struct event); 16 | } execs SEC(".maps"); 17 | 18 | struct { 19 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 20 | __uint(key_size, sizeof(u32)); 21 | __uint(value_size, sizeof(u32)); 22 | } 23 | events SEC(".maps"); 24 | 25 | // tracepoint for sys_enter_execve. 26 | SEC("tracepoint/syscalls/sys_enter_execve") 27 | int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter 28 | *ctx) 29 | { 30 | struct event *event; 31 | const char **args = (const char **)(ctx->args[1]); 32 | const char *argp; 33 | 34 | // get the PID 35 | u64 id = bpf_get_current_pid_tgid(); 36 | pid_t pid = (pid_t) id; 37 | 38 | // update the exec metadata to execs map 39 | if (bpf_map_update_elem(&execs, &pid, &empty_event, BPF_NOEXIST)) { 40 | return 0; 41 | } 42 | event = bpf_map_lookup_elem(&execs, &pid); 43 | if (!event) { 44 | return 0; 45 | } 46 | // update event metadata 47 | event->pid = pid; 48 | event->args_count = 0; 49 | event->args_size = 0; 50 | 51 | // query the first parameter 52 | unsigned int ret = bpf_probe_read_user_str(event->args, ARGSIZE, 53 | (const char *)ctx->args[0]); 54 | if (ret <= ARGSIZE) { 55 | event->args_size += ret; 56 | } else { 57 | /* write an empty string */ 58 | event->args[0] = '\0'; 59 | event->args_size++; 60 | } 61 | 62 | // query the extra parameters 63 | event->args_count++; 64 | #pragma unroll 65 | for (int i = 1; i < TOTAL_MAX_ARGS; i++) { 66 | bpf_probe_read_user(&argp, sizeof(argp), &args[i]); 67 | if (!argp) 68 | return 0; 69 | 70 | if (event->args_size > LAST_ARG) 71 | return 0; 72 | 73 | ret = 74 | bpf_probe_read_user_str(&event->args[event->args_size], 75 | ARGSIZE, argp); 76 | if (ret > ARGSIZE) 77 | return 0; 78 | 79 | event->args_count++; 80 | event->args_size += ret; 81 | } 82 | 83 | /* try to read one more argument to check if there is one */ 84 | bpf_probe_read_user(&argp, sizeof(argp), &args[TOTAL_MAX_ARGS]); 85 | if (!argp) 86 | return 0; 87 | 88 | /* pointer to max_args+1 isn't null, assume we have more arguments */ 89 | event->args_count++; 90 | 91 | return 0; 92 | } 93 | 94 | // tracepoint for sys_exit_execve. 95 | SEC("tracepoint/syscalls/sys_exit_execve") 96 | int tracepoint__syscalls__sys_exit_execve(struct trace_event_raw_sys_exit *ctx) 97 | { 98 | u64 id; 99 | pid_t pid; 100 | int ret; 101 | struct event *event; 102 | 103 | // get the exec metadata from execs map 104 | id = bpf_get_current_pid_tgid(); 105 | pid = (pid_t) id; 106 | event = bpf_map_lookup_elem(&execs, &pid); 107 | if (!event) 108 | return 0; 109 | 110 | // update event retval 111 | ret = ctx->ret; 112 | event->retval = ret; 113 | bpf_get_current_comm(&event->comm, sizeof(event->comm)); 114 | 115 | // submit to perf event 116 | size_t len = EVENT_SIZE(event); 117 | if (len <= sizeof(*event)) 118 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, event, 119 | len); 120 | 121 | // cleanup exec from hash map 122 | bpf_map_delete_elem(&execs, &pid); 123 | return 0; 124 | } 125 | 126 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 127 | -------------------------------------------------------------------------------- /bpf-apps/execsnoop.c: -------------------------------------------------------------------------------- 1 | // Based on execsnoop(8) from BCC by Brendan Gregg and others. 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "execsnoop.h" 9 | #include "execsnoop.skel.h" 10 | 11 | // Handler for libbpf errors and debug info callback. 12 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, 13 | va_list args) 14 | { 15 | #ifdef DEBUGBPF 16 | return vfprintf(stderr, format, args); 17 | #else 18 | return 0; 19 | #endif 20 | } 21 | 22 | // Handler for lost events. 23 | void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) 24 | { 25 | fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu); 26 | } 27 | 28 | // Heandler for print arguments (args are separated by '\0'). 29 | static void print_args(const struct event *e) 30 | { 31 | int args_counter = 0; 32 | 33 | for (int i = 0; i < e->args_size && args_counter < e->args_count; i++) { 34 | char c = e->args[i]; 35 | if (c == '\0') { 36 | args_counter++; 37 | putchar(' '); 38 | } else { 39 | putchar(c); 40 | } 41 | } 42 | if (e->args_count > TOTAL_MAX_ARGS) { 43 | fputs(" ...", stdout); 44 | } 45 | } 46 | 47 | // Handler for each perf event. 48 | void handle_event(void *ctx, int cpu, void *data, __u32 data_sz) 49 | { 50 | const struct event *e = data; 51 | printf("%-16s %-6d %3d ", e->comm, e->pid, e->retval); 52 | print_args(e); 53 | putchar('\n'); 54 | } 55 | 56 | // Bump RLIMIT_MEMLOCK to allow BPF sub-system to do anything it needs. 57 | static void bump_memlock_rlimit(void) 58 | { 59 | struct rlimit rlim_new = { 60 | .rlim_cur = RLIM_INFINITY, 61 | .rlim_max = RLIM_INFINITY, 62 | }; 63 | 64 | if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) { 65 | fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n"); 66 | exit(1); 67 | } 68 | } 69 | 70 | int main(int argc, char **argv) 71 | { 72 | struct execsnoop_bpf *skel; 73 | struct perf_buffer *pb = NULL; 74 | int err; 75 | 76 | /* Set up libbpf errors and debug info callback */ 77 | libbpf_set_print(libbpf_print_fn); 78 | 79 | /* Bump RLIMIT_MEMLOCK to allow BPF sub-system to do anything */ 80 | bump_memlock_rlimit(); 81 | 82 | /* Open BPF application (generated by bpftool gen skeleton) */ 83 | skel = execsnoop_bpf__open(); 84 | if (!skel) { 85 | fprintf(stderr, "Failed to open BPF skeleton\n"); 86 | return 1; 87 | } 88 | 89 | /* Load & verify BPF programs (generated by bpftool gen skeleton) */ 90 | err = execsnoop_bpf__load(skel); 91 | if (err) { 92 | fprintf(stderr, "Failed to load and verify BPF skeleton\n"); 93 | goto cleanup; 94 | } 95 | 96 | /* Attach tracepoint handler (generated by bpftool gen skeleton) */ 97 | err = execsnoop_bpf__attach(skel); 98 | if (err) { 99 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 100 | goto cleanup; 101 | } 102 | 103 | pb = perf_buffer__new(bpf_map__fd(skel->maps.events), 64, handle_event, 104 | handle_lost_events, NULL, NULL); 105 | err = libbpf_get_error(pb); 106 | if (err) { 107 | pb = NULL; 108 | fprintf(stderr, "failed to open perf buffer: %d\n", err); 109 | goto cleanup; 110 | } 111 | 112 | printf("%-16s %-6s %3s %s\n", "COMM", "PID", "RET", "ARGS"); 113 | 114 | /* Main polling loop */ 115 | while ((err = perf_buffer__poll(pb, 100)) >= 0) ; 116 | printf("Error polling perf buffer: %d\n", err); 117 | 118 | cleanup: 119 | perf_buffer__free(pb); 120 | execsnoop_bpf__destroy(skel); 121 | return err != 0; 122 | } 123 | -------------------------------------------------------------------------------- /bpf-apps/execsnoop.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __HELLO_H 3 | #define __HELLO_H 4 | 5 | #define ARGSIZE 128 6 | #define TASK_COMM_LEN 16 7 | #define TOTAL_MAX_ARGS 60 8 | #define FULL_MAX_ARGS_ARR (TOTAL_MAX_ARGS * ARGSIZE) 9 | #define BASE_EVENT_SIZE (size_t)(&((struct event*)0)->args) 10 | #define EVENT_SIZE(e) (BASE_EVENT_SIZE + e->args_size) 11 | #define LAST_ARG (FULL_MAX_ARGS_ARR - ARGSIZE) 12 | 13 | struct event { 14 | char comm[TASK_COMM_LEN]; 15 | pid_t pid; 16 | int retval; 17 | int args_count; 18 | unsigned int args_size; 19 | char args[FULL_MAX_ARGS_ARR]; 20 | }; 21 | 22 | #endif /* __HELLO_H */ 23 | -------------------------------------------------------------------------------- /bpf-apps/execsnoop_v2.bpf.c: -------------------------------------------------------------------------------- 1 | // #define BPF_NO_PRESERVE_ACCESS_INDEX 2 | // Refer https://github.com/iovisor/bcc/blob/master/libbpf-tools/execsnoop.bpf.c 3 | 4 | #include "vmlinux.h" 5 | #include 6 | #include 7 | #include "execsnoop_v2.h" 8 | 9 | static const struct event empty_event = { }; 10 | 11 | const volatile uid_t targ_uid = INVALID_UID; 12 | 13 | struct { 14 | __uint(type, BPF_MAP_TYPE_HASH); 15 | __uint(max_entries, 10240); 16 | __type(key, pid_t); 17 | __type(value, struct event); 18 | } execs SEC(".maps"); 19 | 20 | struct { 21 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 22 | __uint(key_size, sizeof(u32)); 23 | __uint(value_size, sizeof(u32)); 24 | } events SEC(".maps"); 25 | 26 | static __always_inline bool valid_uid(uid_t uid) 27 | { 28 | return uid != INVALID_UID; 29 | } 30 | 31 | SEC("tracepoint/syscalls/sys_enter_execve") 32 | int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter 33 | *ctx) 34 | { 35 | u64 id; 36 | pid_t pid, tgid; 37 | unsigned int ret; 38 | 39 | struct event *event; 40 | struct task_struct *task; 41 | const char **args = (const char **)(ctx->args[1]); 42 | 43 | const char *argp; 44 | 45 | uid_t uid = (u32) bpf_get_current_uid_gid(); 46 | 47 | id = bpf_get_current_pid_tgid(); 48 | pid = (pid_t) id; 49 | tgid = id >> 32; 50 | 51 | if (bpf_map_update_elem(&execs, &pid, &empty_event, BPF_NOEXIST)) 52 | return 0; 53 | 54 | event = bpf_map_lookup_elem(&execs, &pid); 55 | if (!event) 56 | return 0; 57 | 58 | event->pid = pid; 59 | event->tgid = tgid; 60 | event->uid = uid; 61 | task = (struct task_struct *)bpf_get_current_task(); 62 | event->ppid = (pid_t) BPF_CORE_READ(task, real_parent, tgid); 63 | event->args_count = 0; 64 | event->args_size = 0; 65 | 66 | ret = 67 | bpf_probe_read_user_str(event->args, ARGSIZE, 68 | (const char *)ctx->args[0]); 69 | if (ret <= ARGSIZE) { 70 | event->args_size += ret; 71 | } else { 72 | /* write an empty string */ 73 | event->args[0] = '\0'; 74 | event->args_size++; 75 | } 76 | 77 | event->args_count++; 78 | #pragma unroll 79 | for (int i = 1; i < TOTAL_MAX_ARGS; i++) { 80 | bpf_probe_read_user(&argp, sizeof(argp), &args[i]); 81 | if (!argp) 82 | return 0; 83 | 84 | if (event->args_size > LAST_ARG) 85 | return 0; 86 | 87 | ret = 88 | bpf_probe_read_user_str(&event->args[event->args_size], 89 | ARGSIZE, argp); 90 | if (ret > ARGSIZE) 91 | return 0; 92 | 93 | event->args_count++; 94 | event->args_size += ret; 95 | } 96 | 97 | /* try to read one more argument to check if there is one */ 98 | bpf_probe_read_user(&argp, sizeof(argp), &args[TOTAL_MAX_ARGS]); 99 | if (!argp) 100 | return 0; 101 | 102 | /* pointer to max_args+1 isn't null, assume we have more arguments */ 103 | event->args_count++; 104 | 105 | return 0; 106 | } 107 | 108 | SEC("tracepoint/syscalls/sys_exit_execve") 109 | int tracepoint__syscalls__sys_exit_execve(struct trace_event_raw_sys_exit *ctx) 110 | { 111 | u64 id; 112 | pid_t pid; 113 | int ret; 114 | struct event *event; 115 | 116 | u32 uid = (u32) bpf_get_current_uid_gid(); 117 | if (valid_uid(targ_uid) && targ_uid != uid) 118 | return 0; 119 | 120 | id = bpf_get_current_pid_tgid(); 121 | pid = (pid_t) id; 122 | event = bpf_map_lookup_elem(&execs, &pid); 123 | if (!event) 124 | return 0; 125 | 126 | ret = ctx->ret; 127 | event->retval = ret; 128 | bpf_get_current_comm(&event->comm, sizeof(event->comm)); 129 | size_t len = EVENT_SIZE(event); 130 | if (len <= sizeof(*event)) 131 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, event, 132 | len); 133 | 134 | cleanup: 135 | bpf_map_delete_elem(&execs, &pid); 136 | return 0; 137 | } 138 | 139 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 140 | -------------------------------------------------------------------------------- /bpf-apps/execsnoop_v2.c: -------------------------------------------------------------------------------- 1 | // Based on execsnoop(8) from BCC by Brendan Gregg and others. 2 | // Refer https://github.com/iovisor/bcc/blob/master/libbpf-tools/execsnoop.c 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "execsnoop_v2.h" 10 | #include "execsnoop_v2.skel.h" 11 | 12 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, 13 | va_list args) 14 | { 15 | #ifdef DEBUGBPF 16 | return vfprintf(stderr, format, args); 17 | #else 18 | return 0; 19 | #endif 20 | } 21 | 22 | void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt) 23 | { 24 | fprintf(stderr, "Lost %llu events on CPU #%d!\n", lost_cnt, cpu); 25 | } 26 | 27 | static void inline quoted_symbol(char c) 28 | { 29 | switch (c) { 30 | case '"': 31 | putchar('\\'); 32 | putchar('"'); 33 | break; 34 | case '\t': 35 | putchar('\\'); 36 | putchar('t'); 37 | break; 38 | case '\n': 39 | putchar('\\'); 40 | putchar('n'); 41 | break; 42 | default: 43 | putchar(c); 44 | break; 45 | } 46 | } 47 | 48 | static void print_args(const struct event *e, bool quote) 49 | { 50 | int args_counter = 0; 51 | 52 | if (quote) 53 | putchar('"'); 54 | 55 | for (int i = 0; i < e->args_size && args_counter < e->args_count; i++) { 56 | char c = e->args[i]; 57 | if (quote) { 58 | if (c == '\0') { 59 | args_counter++; 60 | putchar('"'); 61 | putchar(' '); 62 | if (args_counter < e->args_count) { 63 | putchar('"'); 64 | } 65 | } else { 66 | quoted_symbol(c); 67 | } 68 | } else { 69 | if (c == '\0') { 70 | args_counter++; 71 | putchar(' '); 72 | } else { 73 | putchar(c); 74 | } 75 | } 76 | } 77 | if (e->args_count > TOTAL_MAX_ARGS) { 78 | fputs(" ...", stdout); 79 | } 80 | } 81 | 82 | void handle_event(void *ctx, int cpu, void *data, __u32 data_sz) 83 | { 84 | const struct event *e = data; 85 | time_t t; 86 | struct tm *tm; 87 | char ts[32]; 88 | 89 | time(&t); 90 | tm = localtime(&t); 91 | strftime(ts, sizeof(ts), "%H:%M:%S", tm); 92 | 93 | printf("%-16s %-6d %-6d %3d ", e->comm, e->pid, e->ppid, e->retval); 94 | print_args(e, true); 95 | putchar('\n'); 96 | } 97 | 98 | static void bump_memlock_rlimit(void) 99 | { 100 | struct rlimit rlim_new = { 101 | .rlim_cur = RLIM_INFINITY, 102 | .rlim_max = RLIM_INFINITY, 103 | }; 104 | 105 | if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) { 106 | fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n"); 107 | exit(1); 108 | } 109 | } 110 | 111 | int main(int argc, char **argv) 112 | { 113 | struct execsnoop_v2_bpf *skel; 114 | struct perf_buffer *pb = NULL; 115 | int err; 116 | 117 | /* Set up libbpf errors and debug info callback */ 118 | libbpf_set_print(libbpf_print_fn); 119 | 120 | /* Bump RLIMIT_MEMLOCK to allow BPF sub-system to do anything */ 121 | bump_memlock_rlimit(); 122 | 123 | /* Open BPF application */ 124 | skel = execsnoop_v2_bpf__open(); 125 | if (!skel) { 126 | fprintf(stderr, "Failed to open BPF skeleton\n"); 127 | return 1; 128 | } 129 | 130 | /* Load & verify BPF programs */ 131 | err = execsnoop_v2_bpf__load(skel); 132 | if (err) { 133 | fprintf(stderr, "Failed to load and verify BPF skeleton\n"); 134 | goto cleanup; 135 | } 136 | 137 | /* Attach tracepoint handler */ 138 | err = execsnoop_v2_bpf__attach(skel); 139 | if (err) { 140 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 141 | goto cleanup; 142 | } 143 | 144 | pb = perf_buffer__new(bpf_map__fd(skel->maps.events), 64, handle_event, 145 | handle_lost_events, NULL, NULL); 146 | err = libbpf_get_error(pb); 147 | if (err) { 148 | pb = NULL; 149 | fprintf(stderr, "failed to open perf buffer: %d\n", err); 150 | goto cleanup; 151 | } 152 | 153 | printf("Successfully started!\n"); 154 | 155 | /* main: poll */ 156 | while ((err = perf_buffer__poll(pb, 100)) >= 0) ; 157 | printf("Error polling perf buffer: %d\n", err); 158 | 159 | cleanup: 160 | perf_buffer__free(pb); 161 | execsnoop_v2_bpf__destroy(skel); 162 | return err != 0; 163 | } 164 | -------------------------------------------------------------------------------- /bpf-apps/execsnoop_v2.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __HELLO_H 3 | #define __HELLO_H 4 | 5 | #define ARGSIZE 128 6 | #define TASK_COMM_LEN 16 7 | #define TOTAL_MAX_ARGS 60 8 | #define FULL_MAX_ARGS_ARR (TOTAL_MAX_ARGS * ARGSIZE) 9 | #define INVALID_UID ((uid_t)-1) 10 | #define BASE_EVENT_SIZE (size_t)(&((struct event*)0)->args) 11 | #define EVENT_SIZE(e) (BASE_EVENT_SIZE + e->args_size) 12 | #define LAST_ARG (FULL_MAX_ARGS_ARR - ARGSIZE) 13 | 14 | struct event { 15 | char comm[TASK_COMM_LEN]; 16 | pid_t pid; 17 | pid_t tgid; 18 | pid_t ppid; 19 | uid_t uid; 20 | int retval; 21 | int args_count; 22 | unsigned int args_size; 23 | char args[FULL_MAX_ARGS_ARR]; 24 | }; 25 | 26 | #endif /* __HELLO_H */ 27 | -------------------------------------------------------------------------------- /bpf-apps/hello.bpf.c: -------------------------------------------------------------------------------- 1 | // Uncomment this line if don't have BTF on the running machine. 2 | // #define BPF_NO_PRESERVE_ACCESS_INDEX 3 | #include "vmlinux.h" 4 | #include 5 | 6 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 7 | 8 | SEC("tp/syscalls/sys_enter_execve") 9 | int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter 10 | *ctx) 11 | { 12 | const char *filename = (const char *)(ctx->args[0]); 13 | pid_t pid = bpf_get_current_pid_tgid(); 14 | 15 | bpf_printk("Process[%d]: %s\n", pid, filename); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /bpf-apps/hello.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "hello.skel.h" 6 | 7 | int main(int argc, char **argv) 8 | { 9 | struct hello_bpf *obj; 10 | int err = 0; 11 | 12 | struct rlimit rlim_new = { 13 | .rlim_cur = RLIM_INFINITY, 14 | .rlim_max = RLIM_INFINITY, 15 | }; 16 | 17 | err = setrlimit(RLIMIT_MEMLOCK, &rlim_new); 18 | if (err) { 19 | fprintf(stderr, "failed to change rlimit\n"); 20 | return 1; 21 | } 22 | obj = hello_bpf__open(); 23 | if (!obj) { 24 | fprintf(stderr, "failed to open and/or load BPF object\n"); 25 | return 1; 26 | } 27 | 28 | err = hello_bpf__load(obj); 29 | if (err) { 30 | fprintf(stderr, "failed to load BPF object %d\n", err); 31 | goto cleanup; 32 | } 33 | 34 | err = hello_bpf__attach(obj); 35 | if (err) { 36 | fprintf(stderr, "failed to attach BPF programs\n"); 37 | goto cleanup; 38 | } 39 | 40 | printf 41 | ("Successfully started! Tracing /sys/kernel/debug/tracing/trace_pipe...\n"); 42 | 43 | system("cat /sys/kernel/debug/tracing/trace_pipe"); 44 | 45 | cleanup: 46 | hello_bpf__destroy(obj); 47 | return err != 0; 48 | } 49 | -------------------------------------------------------------------------------- /bpf-apps/hello_btf.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | 4 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 5 | 6 | SEC("tp/syscalls/sys_enter_execve") 7 | int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter 8 | *ctx) 9 | { 10 | const char *filename = (const char *)(ctx->args[0]); 11 | pid_t pid = bpf_get_current_pid_tgid(); 12 | 13 | bpf_printk("Process[%d]: %s\n", pid, filename); 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /bpf-apps/hello_btf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Hello world custom BTF file for kernel without built-in BTF support 3 | * 4 | * The BTF file could be got by 5 | * * either 1) download from https://github.com/aquasecurity/btfhub-archive 6 | * * or 2) pahole --btf_encode . 7 | * 8 | * Run with the following command: 9 | * export BPF_CUSTOM_BTF=.btf 10 | * ./hello-btf 11 | */ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "hello_btf.skel.h" 17 | 18 | int main(int argc, char **argv) 19 | { 20 | struct hello_btf_bpf *obj; 21 | int err = 0; 22 | 23 | struct rlimit rlim_new = { 24 | .rlim_cur = RLIM_INFINITY, 25 | .rlim_max = RLIM_INFINITY, 26 | }; 27 | 28 | err = setrlimit(RLIMIT_MEMLOCK, &rlim_new); 29 | if (err) { 30 | fprintf(stderr, "failed to change rlimit\n"); 31 | return 1; 32 | } 33 | struct bpf_object_open_opts openopts = { 34 | .sz = sizeof(struct bpf_object_open_opts), 35 | .btf_custom_path = getenv("BPF_CUSTOM_BTF"), 36 | }; 37 | obj = hello_btf_bpf__open_opts(&openopts); 38 | if (!obj) { 39 | fprintf(stderr, "failed to open and/or load BPF object\n"); 40 | return 1; 41 | } 42 | 43 | err = hello_btf_bpf__load(obj); 44 | if (err) { 45 | fprintf(stderr, "failed to load BPF object %d\n", err); 46 | goto cleanup; 47 | } 48 | 49 | err = hello_btf_bpf__attach(obj); 50 | if (err) { 51 | fprintf(stderr, "failed to attach BPF programs\n"); 52 | goto cleanup; 53 | } 54 | 55 | printf 56 | ("Successfully started! Tracing /sys/kernel/debug/tracing/trace_pipe...\n"); 57 | 58 | system("cat /sys/kernel/debug/tracing/trace_pipe"); 59 | 60 | cleanup: 61 | hello_btf_bpf__destroy(obj); 62 | return err != 0; 63 | } 64 | -------------------------------------------------------------------------------- /bpf-apps/http_trace.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include "http_trace.h" 7 | 8 | #define ETH_HLEN 14 9 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 10 | #define IP_MF 0x2000 /* More Fragments */ 11 | #define IP_OFFSET 0x1FFF /* Mask for fragmenting bits */ 12 | 13 | struct { 14 | __uint(type, BPF_MAP_TYPE_RINGBUF); 15 | __uint(max_entries, 256 * 1024); 16 | } events SEC(".maps"); 17 | 18 | static inline int ip_is_fragment(struct __sk_buff *skb) 19 | { 20 | __u16 frag_off; 21 | bpf_skb_load_bytes(skb, ETH_HLEN + offsetof(struct iphdr, frag_off), 22 | &frag_off, sizeof(frag_off)); 23 | frag_off = bpf_ntohs(frag_off); 24 | return frag_off & (IP_MF | IP_OFFSET); 25 | } 26 | 27 | SEC("socket") 28 | int http_trace(struct __sk_buff *skb) 29 | { 30 | struct event_t *event; 31 | __u8 ip_proto; 32 | __u16 h_proto; 33 | 34 | // 只跟踪 IP 协议的数据包 35 | bpf_skb_load_bytes(skb, offsetof(struct ethhdr, h_proto), &h_proto, 2); 36 | if (h_proto != bpf_htons(ETH_P_IP)) { 37 | return 0; 38 | } 39 | 40 | // 如果是分片包则不跟踪 41 | if (ip_is_fragment(skb)) { 42 | return 0; 43 | } 44 | 45 | // 只跟踪 TCP 协议的数据包 46 | bpf_skb_load_bytes(skb, ETH_HLEN + offsetof(struct iphdr, protocol), 47 | &ip_proto, 1); 48 | if (ip_proto != IPPROTO_TCP) { 49 | return 0; 50 | } 51 | 52 | // 计算IP头部长度(ihl单位为4字节,所以需要乘以4) 53 | struct iphdr iph; 54 | bpf_skb_load_bytes(skb, ETH_HLEN, &iph, sizeof(iph)); 55 | __u32 ip_total_length = iph.tot_len; 56 | __u32 iph_len = iph.ihl; 57 | iph_len = iph_len << 2; 58 | 59 | // 根据TCP数据偏移(doff)计算TCP头部长度(doff单位为4字节,所以需要乘以4) 60 | struct tcphdr tcph; 61 | bpf_skb_load_bytes(skb, ETH_HLEN + sizeof(iph), &tcph, sizeof(tcph)); 62 | __u32 tcp_hlen = tcph.doff; 63 | tcp_hlen = tcp_hlen << 2; 64 | 65 | // 只跟踪 TCP 80 端口的数据包 66 | if (tcph.source != bpf_htons(80) && tcph.dest != bpf_htons(80)) { 67 | return 0; 68 | } 69 | 70 | // 计算HTTP payload的偏移和长度 71 | __u32 payload_offset = ETH_HLEN + iph_len + tcp_hlen; 72 | __u32 payload_length = bpf_ntohs(ip_total_length) - iph_len - tcp_hlen; 73 | // HTTP 报文最短为7个字节 74 | if (payload_length < 7) { 75 | return 0; 76 | } 77 | 78 | // 只跟踪 GET、POST、PUT、DELETE 方法的数据包 79 | // HTTP 开头的数据包是服务器端的响应 80 | char start_buffer[7] = { }; 81 | bpf_skb_load_bytes(skb, payload_offset, start_buffer, 7); 82 | if (bpf_strncmp(start_buffer, 3, "GET") != 0 && 83 | bpf_strncmp(start_buffer, 4, "POST") != 0 && 84 | bpf_strncmp(start_buffer, 3, "PUT") != 0 && 85 | bpf_strncmp(start_buffer, 6, "DELETE") != 0 && 86 | bpf_strncmp(start_buffer, 4, "HTTP") != 0) { 87 | return 0; 88 | } 89 | 90 | // 读取HTTP信息并将其提交到环形缓冲区 91 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 92 | if (!event) { 93 | return 0; 94 | } 95 | event->sport = bpf_ntohs(tcph.source); 96 | event->dport = bpf_ntohs(tcph.dest); 97 | event->payload_length = payload_length; 98 | bpf_skb_load_bytes(skb, payload_offset, event->payload, sizeof(event->payload)); 99 | bpf_skb_load_bytes(skb, ETH_HLEN + offsetof(struct iphdr, saddr), 100 | &event->saddr, 4); 101 | bpf_skb_load_bytes(skb, ETH_HLEN + offsetof(struct iphdr, daddr), 102 | &event->daddr, 4); 103 | bpf_ringbuf_submit(event, 0); 104 | 105 | return 0; 106 | } 107 | 108 | char _license[] SEC("license") = "Dual BSD/GPL"; 109 | -------------------------------------------------------------------------------- /bpf-apps/http_trace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "http_trace.h" 18 | #include "http_trace.skel.h" 19 | 20 | // 用于打印调试信息 21 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, 22 | va_list args) 23 | { 24 | #ifdef DEBUGBPF 25 | return vfprintf(stderr, format, args); 26 | #else 27 | return 0; 28 | #endif 29 | } 30 | 31 | static volatile bool exiting = false; 32 | 33 | static void sig_handler(int signo) 34 | { 35 | exiting = true; 36 | } 37 | 38 | // 创建原始套接字 39 | static inline int open_raw_sock(const char *name) 40 | { 41 | struct sockaddr_ll sll; 42 | int sock; 43 | 44 | sock = 45 | socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, 46 | htons(ETH_P_ALL)); 47 | if (sock < 0) { 48 | printf("cannot create raw socket\n"); 49 | return -1; 50 | } 51 | 52 | memset(&sll, 0, sizeof(sll)); 53 | sll.sll_family = AF_PACKET; 54 | sll.sll_ifindex = if_nametoindex(name); 55 | sll.sll_protocol = htons(ETH_P_ALL); 56 | if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { 57 | printf("bind to %s: %s\n", name, strerror(errno)); 58 | close(sock); 59 | return -1; 60 | } 61 | 62 | return sock; 63 | } 64 | 65 | // 输出 HTTP 请求和响应信息(注意:长度截断至MAX_LENGTH) 66 | static int handle_event(void *ctx, void *data, size_t data_sz) 67 | { 68 | const struct event_t *e = data; 69 | char saddr[16] = { }, daddr[16] = { }; 70 | 71 | inet_ntop(AF_INET, &e->saddr, saddr, sizeof(saddr)); 72 | inet_ntop(AF_INET, &e->daddr, daddr, sizeof(daddr)); 73 | printf("%s:%d -> %s:%d (length: %d)\n%s\n\n", saddr, e->sport, daddr, 74 | e->dport, e->payload_length, e->payload); 75 | return 0; 76 | } 77 | 78 | // 提升RLIMIT_MEMLOCK以允许BPF子系统执行任何需要的操作 79 | static void bump_memlock_rlimit(void) 80 | { 81 | struct rlimit rlim_new = { 82 | .rlim_cur = RLIM_INFINITY, 83 | .rlim_max = RLIM_INFINITY, 84 | }; 85 | 86 | if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) { 87 | fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit!\n"); 88 | exit(1); 89 | } 90 | } 91 | 92 | int main(int argc, char **argv) 93 | { 94 | struct http_trace_bpf *skel; 95 | struct ring_buffer *rb = NULL; 96 | int err = 0; 97 | 98 | // 设置libbpf的错误和调试信息回调 99 | libbpf_set_print(libbpf_print_fn); 100 | 101 | // 注册信号处理程序 102 | signal(SIGINT, sig_handler); 103 | signal(SIGTERM, sig_handler); 104 | 105 | // 提升RLIMIT_MEMLOCK以允许BPF子系统执行任何需要的操作 106 | bump_memlock_rlimit(); 107 | 108 | // 加载BPF程序 109 | skel = http_trace_bpf__open_and_load(); 110 | if (!skel) { 111 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 112 | return 1; 113 | } 114 | // 创建ring buffer并绑定事件处理回调 115 | rb = ring_buffer__new(bpf_map__fd(skel->maps.events), handle_event, 116 | NULL, NULL); 117 | if (!rb) { 118 | fprintf(stderr, "Failed to create ring buffer\n"); 119 | goto cleanup; 120 | } 121 | // 将eBPF程序挂载到原始套接字 122 | int sock = open_raw_sock("eth0"); 123 | if (sock < 0) { 124 | fprintf(stderr, "Failed to open raw socket\n"); 125 | goto cleanup; 126 | } 127 | int prog_fd = bpf_program__fd(skel->progs.http_trace); 128 | if (setsockopt 129 | (sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))) { 130 | fprintf(stderr, "Failed to attach eBPF prog\n"); 131 | goto cleanup; 132 | } 133 | // 从ring buffer中读取数据 134 | printf("Tracing HTTP traffic... Hit Ctrl-C to end.\n"); 135 | while (!exiting) { 136 | err = ring_buffer__poll(rb, 100); 137 | if (err == -EINTR) { 138 | err = 0; 139 | break; 140 | } 141 | if (err < 0) { 142 | fprintf(stderr, "Error polling ring buffer: %d\n", err); 143 | break; 144 | } 145 | } 146 | 147 | cleanup: 148 | // 释放资源 149 | ring_buffer__free(rb); 150 | http_trace_bpf__destroy(skel); 151 | return -err; 152 | } 153 | -------------------------------------------------------------------------------- /bpf-apps/http_trace.h: -------------------------------------------------------------------------------- 1 | #ifndef __HTTP_TRACE_H 2 | #define __HTTP_TRACE_H 3 | 4 | #define MAX_LENGTH 100 5 | 6 | struct event_t { 7 | __u32 saddr; 8 | __u32 daddr; 9 | __u16 sport; 10 | __u16 dport; 11 | __u32 payload_length; 12 | __u8 payload[MAX_LENGTH]; 13 | }; 14 | 15 | #endif /* __HTTP_TRACE_H */ 16 | -------------------------------------------------------------------------------- /bpf-apps/https_trace.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include "https_trace.h" 8 | 9 | // 用户态和内核态的数据缓冲区 10 | struct { 11 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 12 | __uint(key_size, sizeof(__u32)); 13 | __uint(value_size, sizeof(__u32)); 14 | __uint(max_entries, 1024); 15 | } 16 | events SEC(".maps"); 17 | 18 | // 用于存储大量数据的缓冲区(避免在BPF程序中分配大量内存) 19 | struct { 20 | __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 21 | __type(key, __u32); 22 | __type(value, struct event_t); 23 | __uint(max_entries, 1); 24 | } data_buffer_heap SEC(".maps"); 25 | 26 | // 用于存储SSL读写缓冲区的哈希映射 27 | struct { 28 | __uint(type, BPF_MAP_TYPE_HASH); 29 | __uint(max_entries, 1024); 30 | __type(key, __u32); 31 | __type(value, __u64); 32 | } bufs SEC(".maps"); 33 | 34 | // 存储SSL读写缓冲区的地址到哈希映射 35 | static int SSL_rw_entry(struct pt_regs *ctx, void *ssl, void *buf, int num) 36 | { 37 | u64 pid_tgid = bpf_get_current_pid_tgid(); 38 | u32 tid = (u32) pid_tgid; 39 | bpf_map_update_elem(&bufs, &tid, (u64 *) & buf, BPF_ANY); 40 | return 0; 41 | } 42 | 43 | static int SSL_rw_exit(struct pt_regs *ctx, int rw) 44 | { 45 | u64 pid_tgid = bpf_get_current_pid_tgid(); 46 | u32 pid = pid_tgid >> 32; 47 | u32 tid = (u32) pid_tgid; 48 | 49 | // 从哈希映射中读取SSL读写缓冲区的地址 50 | u64 *bufp = bpf_map_lookup_elem(&bufs, &tid); 51 | if (!bufp) { 52 | return 0; 53 | } 54 | // 从寄存器中读取函数调用的返回值 55 | int len = PT_REGS_RC(ctx); 56 | if (len <= 0) { 57 | return 0; 58 | } 59 | // 分配一个数据缓冲区 60 | __u32 zero = 0; 61 | struct event_t *event = bpf_map_lookup_elem(&data_buffer_heap, &zero); 62 | if (!event) { 63 | return 0; 64 | } 65 | 66 | event->rw = rw; 67 | event->pid = pid; 68 | event->uid = bpf_get_current_uid_gid(); 69 | bpf_get_current_comm(&event->comm, sizeof(event->comm)); 70 | 71 | // 读取SSL读写缓冲区的数据 72 | event->len = 73 | (size_t)MAX_BUF_LENGTH < 74 | (size_t)len ? (size_t)MAX_BUF_LENGTH : (size_t)len; 75 | if (bufp != NULL) { 76 | bpf_probe_read_user(event->buf, event->len, 77 | (const char *)*bufp); 78 | } 79 | // 将数据缓冲区的数据发送到perf event 80 | bpf_map_delete_elem(&bufs, &tid); 81 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, event, 82 | sizeof(struct event_t)); 83 | return 0; 84 | } 85 | 86 | SEC("uprobe/SSL_read") 87 | int BPF_UPROBE(probe_SSL_read_entry, void *ssl, void *buf, int num) 88 | { 89 | return SSL_rw_entry(ctx, ssl, buf, num); 90 | } 91 | 92 | SEC("uretprobe/SSL_read") 93 | int BPF_URETPROBE(probe_SSL_read_exit) 94 | { 95 | return SSL_rw_exit(ctx, 0); 96 | } 97 | 98 | SEC("uprobe/SSL_write") 99 | int BPF_UPROBE(probe_SSL_write_entry, void *ssl, void *buf, int num) 100 | { 101 | return SSL_rw_entry(ctx, ssl, buf, num); 102 | } 103 | 104 | SEC("uretprobe/SSL_write") 105 | int BPF_URETPROBE(probe_SSL_write_exit) 106 | { 107 | return SSL_rw_exit(ctx, 1); 108 | } 109 | 110 | char _license[] SEC("license") = "Dual BSD/GPL"; 111 | -------------------------------------------------------------------------------- /bpf-apps/https_trace.h: -------------------------------------------------------------------------------- 1 | #ifndef __HTTPS_TRACE_H 2 | #define __HTTPS_TRACE_H 3 | 4 | #define COMM_LEN 32 5 | #define MAX_BUF_LENGTH 8192 6 | 7 | struct event_t { 8 | __u32 pid; 9 | __u32 uid; 10 | __u8 buf[MAX_BUF_LENGTH]; 11 | char comm[COMM_LEN]; 12 | __u32 len; 13 | __u8 rw; // 0: read, 1: write 14 | }; 15 | 16 | #endif /* __HTTPS_TRACE_H */ 17 | -------------------------------------------------------------------------------- /bpf-apps/https_trace_bad.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include "http_trace.h" 7 | 8 | #define ETH_HLEN 14 9 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 10 | #define IP_MF 0x2000 /* More Fragments */ 11 | #define IP_OFFSET 0x1FFF /* Mask for fragmenting bits */ 12 | 13 | struct { 14 | __uint(type, BPF_MAP_TYPE_RINGBUF); 15 | __uint(max_entries, 256 * 1024); 16 | } events SEC(".maps"); 17 | 18 | static inline int ip_is_fragment(struct __sk_buff *skb) 19 | { 20 | __u16 frag_off; 21 | bpf_skb_load_bytes(skb, ETH_HLEN + offsetof(struct iphdr, frag_off), 22 | &frag_off, sizeof(frag_off)); 23 | frag_off = bpf_ntohs(frag_off); 24 | return frag_off & (IP_MF | IP_OFFSET); 25 | } 26 | 27 | SEC("socket") 28 | int https_trace(struct __sk_buff *skb) 29 | { 30 | struct event_t *event; 31 | __u8 ip_proto; 32 | __u16 h_proto; 33 | 34 | // 只跟踪 IP 协议的数据包 35 | bpf_skb_load_bytes(skb, offsetof(struct ethhdr, h_proto), &h_proto, 2); 36 | if (h_proto != bpf_htons(ETH_P_IP)) { 37 | return 0; 38 | } 39 | 40 | // 如果是分片包则不跟踪 41 | if (ip_is_fragment(skb)) { 42 | return 0; 43 | } 44 | 45 | // 只跟踪 TCP 协议的数据包 46 | bpf_skb_load_bytes(skb, ETH_HLEN + offsetof(struct iphdr, protocol), 47 | &ip_proto, 1); 48 | if (ip_proto != IPPROTO_TCP) { 49 | return 0; 50 | } 51 | 52 | // 计算IP头部长度(ihl单位为4字节,所以需要乘以4) 53 | struct iphdr iph; 54 | bpf_skb_load_bytes(skb, ETH_HLEN, &iph, sizeof(iph)); 55 | __u32 ip_total_length = iph.tot_len; 56 | __u32 iph_len = iph.ihl; 57 | iph_len = iph_len << 2; 58 | 59 | // 根据TCP数据偏移(doff)计算TCP头部长度(doff单位为4字节,所以需要乘以4) 60 | struct tcphdr tcph; 61 | bpf_skb_load_bytes(skb, ETH_HLEN + sizeof(iph), &tcph, sizeof(tcph)); 62 | __u32 tcp_hlen = tcph.doff; 63 | tcp_hlen = tcp_hlen << 2; 64 | 65 | // 只跟踪 TCP 443 端口的数据包 66 | if (tcph.source != bpf_htons(443) && tcph.dest != bpf_htons(443)) { 67 | return 0; 68 | } 69 | 70 | // 计算HTTP payload的偏移和长度 71 | __u32 payload_offset = ETH_HLEN + iph_len + tcp_hlen; 72 | __u32 payload_length = bpf_ntohs(ip_total_length) - iph_len - tcp_hlen; 73 | if (payload_length < 7) { 74 | return 0; 75 | } 76 | 77 | // 数据内容不可见,故而无法过滤内容 78 | // char start_buffer[7] = { }; 79 | // bpf_skb_load_bytes(skb, payload_offset, start_buffer, 7); 80 | // if (bpf_strncmp(start_buffer, 3, "GET") != 0 && 81 | // bpf_strncmp(start_buffer, 4, "POST") != 0 && 82 | // bpf_strncmp(start_buffer, 3, "PUT") != 0 && 83 | // bpf_strncmp(start_buffer, 6, "DELETE") != 0 && 84 | // bpf_strncmp(start_buffer, 4, "HTTP") != 0) { 85 | // return 0; 86 | // } 87 | 88 | // 读取HTTPS信息并将其提交到环形缓冲区 89 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 90 | if (!event) { 91 | return 0; 92 | } 93 | event->sport = bpf_ntohs(tcph.source); 94 | event->dport = bpf_ntohs(tcph.dest); 95 | event->payload_length = payload_length; 96 | __u32 read_length = 97 | payload_length > MAX_LENGTH ? MAX_LENGTH : payload_length; 98 | bpf_skb_load_bytes(skb, payload_offset, event->payload, read_length); 99 | bpf_skb_load_bytes(skb, ETH_HLEN + offsetof(struct iphdr, saddr), 100 | &event->saddr, 4); 101 | bpf_skb_load_bytes(skb, ETH_HLEN + offsetof(struct iphdr, daddr), 102 | &event->daddr, 4); 103 | bpf_ringbuf_submit(event, 0); 104 | 105 | return 0; 106 | } 107 | 108 | char _license[] SEC("license") = "Dual BSD/GPL"; 109 | -------------------------------------------------------------------------------- /bpf-apps/libbpf/libbpf.pc: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | 3 | prefix=/usr 4 | libdir=${prefix}/lib64 5 | includedir=${prefix}/include 6 | 7 | Name: libbpf 8 | Description: BPF library 9 | Version: 1.5.0 10 | Libs: -L${libdir} -lbpf 11 | Requires.private: libelf zlib 12 | Cflags: -I${includedir} 13 | -------------------------------------------------------------------------------- /bpf-apps/libbpf/usr/include/bpf/libbpf_common.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | /* 4 | * Common user-facing libbpf helpers. 5 | * 6 | * Copyright (c) 2019 Facebook 7 | */ 8 | 9 | #ifndef __LIBBPF_LIBBPF_COMMON_H 10 | #define __LIBBPF_LIBBPF_COMMON_H 11 | 12 | #include 13 | #include "libbpf_version.h" 14 | 15 | #ifndef LIBBPF_API 16 | #define LIBBPF_API __attribute__((visibility("default"))) 17 | #endif 18 | 19 | #define LIBBPF_DEPRECATED(msg) __attribute__((deprecated(msg))) 20 | 21 | /* Mark a symbol as deprecated when libbpf version is >= {major}.{minor} */ 22 | #define LIBBPF_DEPRECATED_SINCE(major, minor, msg) \ 23 | __LIBBPF_MARK_DEPRECATED_ ## major ## _ ## minor \ 24 | (LIBBPF_DEPRECATED("libbpf v" # major "." # minor "+: " msg)) 25 | 26 | #define __LIBBPF_CURRENT_VERSION_GEQ(major, minor) \ 27 | (LIBBPF_MAJOR_VERSION > (major) || \ 28 | (LIBBPF_MAJOR_VERSION == (major) && LIBBPF_MINOR_VERSION >= (minor))) 29 | 30 | /* Add checks for other versions below when planning deprecation of API symbols 31 | * with the LIBBPF_DEPRECATED_SINCE macro. 32 | */ 33 | #if __LIBBPF_CURRENT_VERSION_GEQ(1, 0) 34 | #define __LIBBPF_MARK_DEPRECATED_1_0(X) X 35 | #else 36 | #define __LIBBPF_MARK_DEPRECATED_1_0(X) 37 | #endif 38 | 39 | /* This set of internal macros allows to do "function overloading" based on 40 | * number of arguments provided by used in backwards-compatible way during the 41 | * transition to libbpf 1.0 42 | * It's ugly but necessary evil that will be cleaned up when we get to 1.0. 43 | * See bpf_prog_load() overload for example. 44 | */ 45 | #define ___libbpf_cat(A, B) A ## B 46 | #define ___libbpf_select(NAME, NUM) ___libbpf_cat(NAME, NUM) 47 | #define ___libbpf_nth(_1, _2, _3, _4, _5, _6, N, ...) N 48 | #define ___libbpf_cnt(...) ___libbpf_nth(__VA_ARGS__, 6, 5, 4, 3, 2, 1) 49 | #define ___libbpf_overload(NAME, ...) ___libbpf_select(NAME, ___libbpf_cnt(__VA_ARGS__))(__VA_ARGS__) 50 | 51 | /* Helper macro to declare and initialize libbpf options struct 52 | * 53 | * This dance with uninitialized declaration, followed by memset to zero, 54 | * followed by assignment using compound literal syntax is done to preserve 55 | * ability to use a nice struct field initialization syntax and **hopefully** 56 | * have all the padding bytes initialized to zero. It's not guaranteed though, 57 | * when copying literal, that compiler won't copy garbage in literal's padding 58 | * bytes, but that's the best way I've found and it seems to work in practice. 59 | * 60 | * Macro declares opts struct of given type and name, zero-initializes, 61 | * including any extra padding, it with memset() and then assigns initial 62 | * values provided by users in struct initializer-syntax as varargs. 63 | */ 64 | #define LIBBPF_OPTS(TYPE, NAME, ...) \ 65 | struct TYPE NAME = ({ \ 66 | memset(&NAME, 0, sizeof(struct TYPE)); \ 67 | (struct TYPE) { \ 68 | .sz = sizeof(struct TYPE), \ 69 | __VA_ARGS__ \ 70 | }; \ 71 | }) 72 | 73 | /* Helper macro to clear and optionally reinitialize libbpf options struct 74 | * 75 | * Small helper macro to reset all fields and to reinitialize the common 76 | * structure size member. Values provided by users in struct initializer- 77 | * syntax as varargs can be provided as well to reinitialize options struct 78 | * specific members. 79 | */ 80 | #define LIBBPF_OPTS_RESET(NAME, ...) \ 81 | do { \ 82 | typeof(NAME) ___##NAME = ({ \ 83 | memset(&___##NAME, 0, sizeof(NAME)); \ 84 | (typeof(NAME)) { \ 85 | .sz = sizeof(NAME), \ 86 | __VA_ARGS__ \ 87 | }; \ 88 | }); \ 89 | memcpy(&NAME, &___##NAME, sizeof(NAME)); \ 90 | } while (0) 91 | 92 | #endif /* __LIBBPF_LIBBPF_COMMON_H */ 93 | -------------------------------------------------------------------------------- /bpf-apps/libbpf/usr/include/bpf/libbpf_version.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (C) 2021 Facebook */ 3 | #ifndef __LIBBPF_VERSION_H 4 | #define __LIBBPF_VERSION_H 5 | 6 | #define LIBBPF_MAJOR_VERSION 1 7 | #define LIBBPF_MINOR_VERSION 5 8 | 9 | #endif /* __LIBBPF_VERSION_H */ 10 | -------------------------------------------------------------------------------- /bpf-apps/libbpf/usr/lib64/pkgconfig/libbpf.pc: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | 3 | prefix=/usr 4 | libdir=${prefix}/lib64 5 | includedir=${prefix}/include 6 | 7 | Name: libbpf 8 | Description: BPF library 9 | Version: 1.5.0 10 | Libs: -L${libdir} -lbpf 11 | Requires.private: libelf zlib 12 | Cflags: -I${includedir} 13 | -------------------------------------------------------------------------------- /bpf-apps/tc_block_tcp.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | SEC("action/egress") 12 | int tc_egress(struct __sk_buff *skb) 13 | { 14 | void *data = (void *)(long)skb->data; 15 | void *data_end = (void *)(long)skb->data_end; 16 | 17 | struct ethhdr *eth = data; 18 | if (data + sizeof(struct ethhdr) > data_end) { 19 | return TC_ACT_OK; /* continue the kernel network stack */ 20 | } 21 | 22 | if (eth->h_proto != bpf_htons(ETH_P_IP)) { 23 | return TC_ACT_OK; 24 | } 25 | 26 | struct iphdr *iph = data + sizeof(struct ethhdr); 27 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) { 28 | return TC_ACT_OK; 29 | } 30 | 31 | if (iph->protocol != IPPROTO_TCP) { 32 | return TC_ACT_OK; 33 | } 34 | //bpf_printk("Tracing TCP packet %ld => %ld\n", iph->saddr, iph->daddr); 35 | 36 | struct tcphdr *tcp = 37 | (data + sizeof(struct ethhdr) + sizeof(struct iphdr)); 38 | if (tcp + 1 > (struct tcphdr *)data_end) { 39 | return TC_ACT_OK; 40 | } 41 | //bpf_printk("TCP Ports: %d -> %d", bpf_htons(tcp->source), bpf_htons(tcp->dest)); 42 | 43 | /* drop tcp 80 packets */ 44 | if (tcp->dest == bpf_htons(80)) { 45 | bpf_printk("Dropping packets to %ld:%d\n", iph->daddr, 46 | bpf_htons(tcp->dest)); 47 | return TC_ACT_SHOT; 48 | } 49 | 50 | return TC_ACT_OK; 51 | } 52 | 53 | SEC("action/ingress") 54 | int tc_ingress(struct __sk_buff *sk) 55 | { 56 | return TC_ACT_OK; 57 | } 58 | 59 | char _license[] SEC("license") = "Dual BSD/GPL"; 60 | -------------------------------------------------------------------------------- /bpf-apps/tc_block_tcp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char **argv) 7 | { 8 | if (system("tc qdisc add dev eth0 clsact")) { 9 | perror("tc qdisc add failed"); 10 | exit(1); 11 | } 12 | 13 | if (system 14 | ("tc filter add dev eth0 egress bpf da obj tc_block_tcp.bpf.o sec action/egress")) 15 | { 16 | perror("tc qdisc add failed"); 17 | goto cleanup; 18 | } 19 | 20 | printf("Tracing /sys/kernel/debug/tracing/trace_pipe...\n"); 21 | system("cat /sys/kernel/debug/tracing/trace_pipe"); 22 | 23 | cleanup: 24 | return system("tc qdisc del dev eth0 clsact"); 25 | } 26 | -------------------------------------------------------------------------------- /bpf-apps/xdp_drop.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #include "vmlinux.h" 3 | #include 4 | #include 5 | 6 | SEC("xdp") 7 | int xdp_prog_drop(struct xdp_md *ctx) 8 | { 9 | void *data = (void *)(long)ctx->data; 10 | void *data_end = (void *)(long)ctx->data_end; 11 | int pkt_sz = data_end - data; 12 | 13 | bpf_printk("[xdp_prog_drop] packet size: %d", pkt_sz); 14 | 15 | // struct ethhdr *eth = data; 16 | struct iphdr *iph = data + sizeof(struct ethhdr); 17 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) { 18 | return XDP_PASS; 19 | } 20 | 21 | if (iph->protocol == IPPROTO_ICMP) { 22 | return XDP_DROP; 23 | } 24 | 25 | return XDP_PASS; 26 | } 27 | 28 | char _license[] SEC("license") = "Dual BSD/GPL"; 29 | -------------------------------------------------------------------------------- /bpf-apps/xdp_drop.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "xdp_drop.skel.h" 12 | 13 | #define DEV_NAME "wlp0s20f3" 14 | 15 | int main(int argc, char **argv) 16 | { 17 | __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_SKB_MODE; 18 | struct xdp_drop_bpf *obj; 19 | LIBBPF_OPTS(bpf_xdp_attach_opts, attach_opts); 20 | int err = 0; 21 | 22 | struct rlimit rlim_new = { 23 | .rlim_cur = RLIM_INFINITY, 24 | .rlim_max = RLIM_INFINITY, 25 | }; 26 | 27 | err = setrlimit(RLIMIT_MEMLOCK, &rlim_new); 28 | if (err) { 29 | fprintf(stderr, "failed to change rlimit\n"); 30 | return 1; 31 | } 32 | 33 | unsigned int ifindex = if_nametoindex(DEV_NAME); 34 | if (ifindex == 0) { 35 | fprintf(stderr, "failed to find interface %s\n", DEV_NAME); 36 | return 1; 37 | } 38 | 39 | obj = xdp_drop_bpf__open(); 40 | if (!obj) { 41 | fprintf(stderr, "failed to open and/or load BPF object\n"); 42 | return 1; 43 | } 44 | 45 | err = xdp_drop_bpf__load(obj); 46 | if (err) { 47 | fprintf(stderr, "failed to load XDP BPF object %d\n", err); 48 | goto cleanup; 49 | } 50 | 51 | /* Attach the XDP program to the specified network interface */ 52 | int prog_id = bpf_program__fd(obj->progs.xdp_prog_drop); 53 | err = bpf_xdp_attach(ifindex, prog_id, xdp_flags, &attach_opts); 54 | if (err) { 55 | fprintf(stderr, "failed to attach BPF programs\n"); 56 | goto cleanup; 57 | } 58 | 59 | printf("Successfully started! ...\n"); 60 | // system("cat /sys/kernel/debug/tracing/trace_pipe"); 61 | while(1) { 62 | sleep(1); 63 | } 64 | 65 | cleanup: 66 | bpf_xdp_detach(ifindex, xdp_flags, &attach_opts); 67 | xdp_drop_bpf__destroy(obj); 68 | return err != 0; 69 | } 70 | -------------------------------------------------------------------------------- /bpf-apps/xdp_drop_test.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #include "vmlinux.h" 3 | #include 4 | 5 | SEC("xdp") 6 | int xdp_prog_drop(struct xdp_md *ctx) 7 | { 8 | void *data = (void *)(long)ctx->data; 9 | void *data_end = (void *)(long)ctx->data_end; 10 | int pkt_sz = data_end - data; 11 | 12 | bpf_printk("packet size: %d", pkt_sz); 13 | 14 | struct ethhdr *eth = data; 15 | struct iphdr *iph = data + sizeof(struct ethhdr); 16 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) { 17 | return XDP_PASS; 18 | } 19 | 20 | if (iph->protocol == IPPROTO_ICMP) { 21 | return XDP_DROP; 22 | } 23 | 24 | return XDP_PASS; 25 | } 26 | 27 | char _license[] SEC("license") = "Dual BSD/GPL"; 28 | -------------------------------------------------------------------------------- /bpf-apps/xdp_drop_trace.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #include "vmlinux.h" 3 | #include 4 | #include 5 | #include 6 | 7 | SEC("fentry/xdp_prog_drop") 8 | int BPF_PROG(trace_on_entry, struct xdp_md *xdp) 9 | { 10 | void *data = (void *)(long)BPF_CORE_READ(xdp, data); 11 | void *data_end = (void *)(long)BPF_CORE_READ(xdp, data_end); 12 | 13 | struct ethhdr *eth = (struct ethhdr *)data; 14 | __be16 proto = BPF_CORE_READ(eth, h_proto); 15 | bpf_printk("[fentry/xdp] packet proto %d", proto); 16 | return 0; 17 | } 18 | 19 | SEC("fexit/xdp_prog_drop") 20 | int BPF_PROG(trace_on_exit, struct xdp_md *xdp, int ret) 21 | { 22 | bpf_printk("[fexit/xdp] ret: %d", ret); 23 | return 0; 24 | } 25 | 26 | char _license[] SEC("license") = "Dual BSD/GPL"; 27 | -------------------------------------------------------------------------------- /bpf-apps/xdp_drop_trace.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "xdp_drop_trace.skel.h" 15 | 16 | int main(int argc, char **argv) 17 | { 18 | unsigned int id = 0; 19 | int target_fd = -1; 20 | while(1) { 21 | int err = bpf_prog_get_next_id(id, &id); 22 | if (err) { 23 | fprintf(stderr, "can't get next prog id: %s\n", strerror(errno)); 24 | break; 25 | } 26 | 27 | int fd = bpf_prog_get_fd_by_id(id); 28 | if (fd < 0) { 29 | fprintf(stderr, "can't get fd for prog id %d: %s\n", id, strerror(errno)); 30 | continue; 31 | } 32 | 33 | struct bpf_prog_info info = {}; 34 | __u32 info_len = sizeof(info); 35 | err = bpf_prog_get_info_by_fd(fd, &info, &info_len); 36 | if (err) { 37 | fprintf(stderr, "can't get info for prog id %d: %s\n", id, strerror(errno)); 38 | continue; 39 | } 40 | 41 | if (strncmp(info.name, "xdp_prog_drop", sizeof(info.name)) == 0) { 42 | target_fd = fd; 43 | break; 44 | } 45 | } 46 | 47 | if (target_fd < 0) 48 | { 49 | fprintf(stderr, "can't find xdp_prog_drop\n"); 50 | return 1; 51 | } 52 | 53 | ////////////////////////////////////////// 54 | struct xdp_drop_trace_bpf *obj; 55 | int err = 0; 56 | 57 | struct rlimit rlim_new = { 58 | .rlim_cur = RLIM_INFINITY, 59 | .rlim_max = RLIM_INFINITY, 60 | }; 61 | 62 | err = setrlimit(RLIMIT_MEMLOCK, &rlim_new); 63 | if (err) 64 | { 65 | fprintf(stderr, "failed to change rlimit\n"); 66 | return 1; 67 | } 68 | 69 | /* load tracing eBPF program */ 70 | obj = xdp_drop_trace_bpf__open(); 71 | if (!obj) 72 | { 73 | fprintf(stderr, 74 | "failed to open and/or load tracing BPF object\n"); 75 | perror("xdp_drop_trace_bpf__open failed"); 76 | return 1; 77 | } 78 | 79 | struct bpf_program *trace_prog_fentry = obj->progs.trace_on_entry; 80 | struct bpf_program *trace_prog_fexit = obj->progs.trace_on_exit; 81 | bpf_program__set_expected_attach_type(trace_prog_fentry, 82 | BPF_TRACE_FENTRY); 83 | bpf_program__set_attach_target(trace_prog_fentry, target_fd, 84 | "xdp_prog_drop"); 85 | bpf_program__set_expected_attach_type(trace_prog_fexit, 86 | BPF_TRACE_FEXIT); 87 | bpf_program__set_attach_target(trace_prog_fexit, target_fd, 88 | "xdp_prog_drop"); 89 | 90 | err = xdp_drop_trace_bpf__load(obj); 91 | if (err) 92 | { 93 | fprintf(stderr, "failed to load XDP BPF object %d\n", err); 94 | perror("load XDP BPF object failed"); 95 | goto cleanup; 96 | } 97 | 98 | err = xdp_drop_trace_bpf__attach(obj); 99 | if (err) 100 | { 101 | fprintf(stderr, "failed to attach BPF programs\n"); 102 | perror("attach BPF programs failed"); 103 | goto cleanup; 104 | } 105 | 106 | printf("Successfully started! Tracing /sys/kernel/debug/tracing/trace_pipe...\n"); 107 | 108 | system("cat /sys/kernel/debug/tracing/trace_pipe"); 109 | 110 | cleanup: 111 | xdp_drop_trace_bpf__destroy(obj); 112 | return err != 0; 113 | } 114 | -------------------------------------------------------------------------------- /bpf-apps/xdppass.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #include 3 | #include 4 | 5 | SEC("xdp") 6 | int xdp_prog_simple(struct xdp_md *ctx) 7 | { 8 | void *data = (void *)(long)ctx->data; 9 | void *data_end = (void *)(long)ctx->data_end; 10 | int pkt_sz = data_end - data; 11 | 12 | bpf_printk("packet size: %d", pkt_sz); 13 | return XDP_PASS; 14 | } 15 | 16 | char _license[] SEC("license") = "Dual BSD/GPL"; 17 | -------------------------------------------------------------------------------- /bpf-apps/xdppass.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "xdppass.skel.h" 8 | 9 | /* Attach to eth0 by default */ 10 | #define DEV_NAME "eth0" 11 | 12 | int main(int argc, char **argv) 13 | { 14 | __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_SKB_MODE; 15 | struct xdppass_bpf *obj; 16 | int err = 0; 17 | 18 | struct rlimit rlim_new = { 19 | .rlim_cur = RLIM_INFINITY, 20 | .rlim_max = RLIM_INFINITY, 21 | }; 22 | 23 | err = setrlimit(RLIMIT_MEMLOCK, &rlim_new); 24 | if (err) { 25 | fprintf(stderr, "failed to change rlimit\n"); 26 | return 1; 27 | } 28 | 29 | unsigned int ifindex = if_nametoindex(DEV_NAME); 30 | if (ifindex == 0) { 31 | fprintf(stderr, "failed to find interface %s\n", DEV_NAME); 32 | return 1; 33 | } 34 | 35 | obj = xdppass_bpf__open(); 36 | if (!obj) { 37 | fprintf(stderr, "failed to open and/or load BPF object\n"); 38 | return 1; 39 | } 40 | 41 | err = xdppass_bpf__load(obj); 42 | if (err) { 43 | fprintf(stderr, "failed to load BPF object %d\n", err); 44 | goto cleanup; 45 | } 46 | 47 | /* Attach the XDP program to the specified network interface */ 48 | int prog_id = bpf_program__fd(obj->progs.xdp_prog_simple); 49 | LIBBPF_OPTS(bpf_xdp_attach_opts, attach_opts); 50 | err = bpf_xdp_attach(ifindex, prog_id, xdp_flags, &attach_opts); 51 | if (err) { 52 | fprintf(stderr, "failed to attach BPF programs\n"); 53 | goto cleanup; 54 | } 55 | 56 | printf 57 | ("Successfully started! Tracing /sys/kernel/debug/tracing/trace_pipe...\n"); 58 | 59 | system("cat /sys/kernel/debug/tracing/trace_pipe"); 60 | 61 | cleanup: 62 | bpf_xdp_detach(ifindex, xdp_flags, &attach_opts); 63 | xdppass_bpf__destroy(obj); 64 | return err != 0; 65 | } 66 | -------------------------------------------------------------------------------- /bpftrace/README.md: -------------------------------------------------------------------------------- 1 | # eBPF apps with bpftrace 2 | 3 | bpftrace is a high-level tracing language for Linux enhanced Berkeley Packet Filter (eBPF) available in recent Linux kernels (4.x+). bpftrace uses LLVM as a backend to compile scripts to BPF-bytecode and makes use of [BCC](https://github.com/iovisor/bcc) for interacting with the Linux BPF system, as well as existing Linux tracing capabilities: kernel dynamic tracing (kprobes), user-level dynamic tracing (uprobes), and tracepoints. The bpftrace language is inspired by awk and C, and predecessor tracers such as DTrace and SystemTap. 4 | 5 | ## Install 6 | 7 | ```sh 8 | # Ubuntu 19.04+ 9 | sudo apt-get install -y bpftrace 10 | 11 | # RHEL8+/CentOS8+ 12 | sudo dnf install -y bpftrace 13 | 14 | # Other distro via Docker 15 | docker pull quay.io/iovisor/bpftrace 16 | docker run -v /usr/local/bin:/usr/local/bin quay.io/iovisor/bpftrace /bin/bash -c "cp /usr/bin/bpftrace /usr/local/bin/bpftrace" 17 | ``` 18 | -------------------------------------------------------------------------------- /bpftrace/block-container-shell.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | /* Block running sh or bash inside containers. */ 3 | /* Run with "sudo bpftrace --unsafe block-container-shell.bt" */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | tracepoint:syscalls:sys_enter_execve, 11 | tracepoint:syscalls:sys_enter_execveat 12 | /comm == "bash" || comm == "sh"/ { 13 | $task = (struct task_struct *)curtask; 14 | $pidns = $task->nsproxy->pid_ns_for_children->ns.inum; 15 | $cname = $task->nsproxy->uts_ns->name.nodename; 16 | if ($pidns != 4026531836) /* TODO: replace 4026531836 with host pidns (lsns -t pid) */ 17 | { 18 | printf("Killing shell command in container %s (pidns: %ld): %s ", $cname, $pidns, comm); join(args->argv); 19 | signal(9); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /bpftrace/bpf_bpf.bt: -------------------------------------------------------------------------------- 1 | // Executed when we enter the bpf() syscall 2 | tracepoint:syscalls:sys_enter_bpf { 3 | if (args.cmd == 5) { 4 | printf("%d:%d: bpf() entering, loading prog %s\n", pid, tid, args.uattr->prog_name) 5 | } 6 | } 7 | 8 | // Executed right before bpf_check() returns 9 | kretprobe:bpf_check { 10 | printf("%d:%d: Verification error: %d\n", pid, tid, retval) 11 | } 12 | 13 | // Executed right before bpf_int_jit_compile() returns 14 | kretprobe:bpf_int_jit_compile { 15 | printf("%d:%d: JIT: Done, compiled?: %d\n", pid, tid, ((struct bpf_prog *) retval)->jited) 16 | } 17 | 18 | // Executed right before the bpf() syscall returns 19 | tracepoint:syscalls:sys_exit_bpf { 20 | if (args.ret < 0) { 21 | printf("%d:%d: bpf() returning, ret: %d\n", pid, tid, args.ret) 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /bpftrace/dropwatch-v2.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | /* tcp drop watch for any processes by probing kfree_skb */ 3 | 4 | // Add required kernel headers 5 | #include 6 | #include 7 | 8 | 9 | kprobe:kfree_skb 10 | { 11 | // Firt arg is sk_buff. 12 | $skb = (struct sk_buff *)arg0; 13 | 14 | // Get network header, src IP and dst IP. 15 | $iph = (struct iphdr *)($skb->head + $skb->network_header); 16 | $sip = ntop(AF_INET, $iph->saddr); 17 | $dip = ntop(AF_INET, $iph->daddr); 18 | 19 | printf("SKB dropped on process %s (PID: %d): %s->%s, kstack: %s\n", comm, pid, $sip, $dip, kstack); 20 | } 21 | -------------------------------------------------------------------------------- /bpftrace/dropwatch.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | /* Watch tcp drop from curl process by probing kfree_skb */ 3 | 4 | // Add required kernel headers 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | kprobe:kfree_skb /comm=="curl"/ 11 | { 12 | // First arg is sk_buff. 13 | $skb = (struct sk_buff *)arg0; 14 | 15 | // Get network header, src IP and dst IP. 16 | $iph = (struct iphdr *)($skb->head + $skb->network_header); 17 | $sip = ntop(AF_INET, $iph->saddr); 18 | $dip = ntop(AF_INET, $iph->daddr); 19 | 20 | // Print kernel stack only when it is TCP. 21 | if ($iph->protocol == IPPROTO_TCP) 22 | { 23 | printf("SKB dropped: %s->%s, kstack: %s\n", $sip, $dip, kstack); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /bpftrace/execsnoop-container.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | /* Tracing execve and execveat with container pidns and uts name. */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | BEGIN { 10 | printf("%-12s %-8s %-6s %-6s %-8s %s\n", "PIDNS", "CONTAINER", "PPID", "PID", "COMM", "ARGS"); 11 | } 12 | 13 | tracepoint:syscalls:sys_enter_execve, 14 | tracepoint:syscalls:sys_enter_execveat { 15 | $task = (struct task_struct *)curtask; 16 | $pidns = $task->nsproxy->pid_ns_for_children->ns.inum; 17 | $cname = $task->nsproxy->uts_ns->name.nodename; 18 | printf("%-12ld %-8s %-6d %-6d %-8s", (uint64)$pidns, $cname, curtask->parent->pid, pid, comm); join(args->argv); 19 | } 20 | -------------------------------------------------------------------------------- /bpftrace/execsnoop-v1.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | /* Basic tracing for execve and execveat syscalls. */ 3 | 4 | tracepoint:syscalls:sys_enter_execve, 5 | tracepoint:syscalls:sys_enter_execveat { 6 | printf("%-6d %-8s", pid, comm); join(args->argv); 7 | } 8 | -------------------------------------------------------------------------------- /bpftrace/execsnoop-v2.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | /* Basic tracing for execve and execveat syscalls with ret values. */ 3 | /* Note that the arguments would not be shown in the outputs. */ 4 | 5 | BEGIN 6 | { 7 | printf("%-6s %-8s %-6s %s\n", "PID", "COMM", "RET", "ARGS"); 8 | } 9 | 10 | tracepoint:syscalls:sys_enter_execve, 11 | tracepoint:syscalls:sys_enter_execveat 12 | { 13 | @execs[tid] = args->argv; 14 | } 15 | 16 | 17 | tracepoint:syscalls:sys_exit_execve, 18 | tracepoint:syscalls:sys_exit_execveat 19 | /@execs[tid]/{ 20 | printf("%-6d %-8s %-6d", pid, comm, args->ret); 21 | join(@execs[tid]); 22 | delete(@execs[tid]); 23 | } 24 | 25 | END 26 | { 27 | clear(@execs); 28 | } 29 | -------------------------------------------------------------------------------- /bpftrace/execsnoop-v3.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | /* Basic tracing for execve and execveat syscalls with ret values. */ 3 | /* It would fail with error: ERROR: join() should not be used in an assignment or as a map key */ 4 | 5 | BEGIN 6 | { 7 | printf("%-6s %-8s %-6s %s\n", "PID", "COMM", "RET", "ARGS"); 8 | } 9 | 10 | tracepoint:syscalls:sys_enter_execve, 11 | tracepoint:syscalls:sys_enter_execveat 12 | { 13 | @execs[tid] = join(args->argv); 14 | } 15 | 16 | 17 | tracepoint:syscalls:sys_exit_execve, 18 | tracepoint:syscalls:sys_exit_execveat 19 | /@execs[tid]/{ 20 | printf("%-6d %-8s %-6d %s", pid, comm, args->ret, @execs[tid]); 21 | delete(@execs[tid]); 22 | } 23 | 24 | END 25 | { 26 | clear(@execs); 27 | } 28 | -------------------------------------------------------------------------------- /bpftrace/execsnoop-v4.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | /* Tracing for execve and execveat syscalls with timestamp and ppid. */ 3 | /* Require bpftrace v0.12.0 or above */ 4 | 5 | #include 6 | 7 | BEGIN { 8 | printf("%-16s %-6s %-6s %-8s\n", "TIME", "PPID", "PID", "COMM"); 9 | } 10 | 11 | tracepoint:syscalls:sys_enter_execve, 12 | tracepoint:syscalls:sys_enter_execveat { 13 | printf("%-16s %-6d %-6d %-8s", strftime("%H:%M:%S:%f", nsecs), curtask->parent->pid, pid, comm); join(args->argv); 14 | } 15 | -------------------------------------------------------------------------------- /bpftrace/sslsnoop.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | /* 3 | * sslsnoop Trace SSL/TLS handshake for OpenSSL. 4 | * For Linux, uses bpftrace and eBPF. 5 | * 6 | * sslsnoop shows handshake latency and retval. This is useful for SSL/TLS 7 | * performance analysis. 8 | * 9 | * Copyright (c) 2021 Tao Xu. 10 | * Licensed under the Apache License, Version 2.0 (the "License") 11 | * 12 | * 15-Dec-2021 Tao Xu created this. 13 | */ 14 | 15 | // config = { missing_probes = "ignore" } 16 | 17 | BEGIN 18 | { 19 | printf("Tracing SSL/TLS handshake... Hit Ctrl-C to end.\n"); 20 | printf("%-10s %-8s %-8s %7s %5s %s\n", "TIME(us)", "TID", 21 | "COMM", "LAT(us)", "RET", "FUNC"); 22 | } 23 | 24 | uprobe:libssl:SSL_read, 25 | uprobe:libssl:SSL_write, 26 | uprobe:libssl:SSL_do_handshake 27 | { 28 | @start_ssl[tid] = nsecs; 29 | @func_ssl[tid] = func; // store for uretprobe 30 | } 31 | 32 | uretprobe:libssl:SSL_read, 33 | uretprobe:libssl:SSL_write, 34 | uretprobe:libssl:SSL_do_handshake 35 | /@start_ssl[tid] != 0/ 36 | { 37 | printf("%-10u %-8d %-8s %7u %5d %s\n", elapsed/1000, tid, comm, 38 | (nsecs - @start_ssl[tid])/1000, retval, @func_ssl[tid]); 39 | delete(@start_ssl[tid]); delete(@func_ssl[tid]); 40 | } 41 | 42 | // need debug symbol for ossl local functions 43 | uprobe:libcrypto:rsa_ossl_public_encrypt, 44 | uprobe:libcrypto:rsa_ossl_public_decrypt, 45 | uprobe:libcrypto:rsa_ossl_private_encrypt, 46 | uprobe:libcrypto:rsa_ossl_private_decrypt, 47 | uprobe:libcrypto:RSA_sign, 48 | uprobe:libcrypto:RSA_verify, 49 | uprobe:libcrypto:ossl_ecdsa_sign, 50 | uprobe:libcrypto:ossl_ecdsa_verify, 51 | uprobe:libcrypto:ossl_ecdh_compute_key 52 | { 53 | @start_crypto[tid] = nsecs; 54 | @func_crypto[tid] = func; // store for uretprobe 55 | } 56 | 57 | uretprobe:libcrypto:rsa_ossl_public_encrypt, 58 | uretprobe:libcrypto:rsa_ossl_public_decrypt, 59 | uretprobe:libcrypto:rsa_ossl_private_encrypt, 60 | uretprobe:libcrypto:rsa_ossl_private_decrypt, 61 | uretprobe:libcrypto:RSA_sign, 62 | uretprobe:libcrypto:RSA_verify, 63 | uretprobe:libcrypto:ossl_ecdsa_sign, 64 | uretprobe:libcrypto:ossl_ecdsa_verify, 65 | uretprobe:libcrypto:ossl_ecdh_compute_key 66 | /@start_crypto[tid] != 0/ 67 | { 68 | printf("%-10u %-8d %-8s %7u %5d %s\n", elapsed/1000, tid, comm, 69 | (nsecs - @start_crypto[tid])/1000, retval, @func_crypto[tid]); 70 | delete(@start_crypto[tid]); delete(@func_crypto[tid]); 71 | } 72 | -------------------------------------------------------------------------------- /bpftrace/tcpdrop-old.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | /* 3 | * tcpdrop.bt Trace TCP kernel-dropped packets/segments. 4 | * For Linux, uses bpftrace and eBPF. 5 | * 6 | * Source: https://github.com/iovisor/bpftrace/blob/master/tools/old/tcpdrop.bt 7 | * 8 | * USAGE: tcpdrop.bt 9 | * 10 | * This is a bpftrace version of the bcc tool of the same name. 11 | * It is limited to ipv4 addresses, and cannot show tcp flags. 12 | * 13 | * This provides information such as packet details, socket state, and kernel 14 | * stack trace for packets/segments that were dropped via tcp_drop(). 15 | 16 | * WARNING: this script attaches to the tcp_drop kprobe which is likely inlined 17 | * on newer kernels and not replaced by anything else, therefore 18 | * the script will stop working 19 | * 20 | * For Linux <= 5.18. 21 | * 22 | * Copyright (c) 2018 Dale Hamel. 23 | * Licensed under the Apache License, Version 2.0 (the "License") 24 | * 25 | * 23-Nov-2018 Dale Hamel created this. 26 | */ 27 | 28 | #ifndef BPFTRACE_HAVE_BTF 29 | #include 30 | #include 31 | #else 32 | #include 33 | #endif 34 | 35 | BEGIN 36 | { 37 | printf("Tracing tcp drops. Hit Ctrl-C to end.\n"); 38 | printf("%-8s %-8s %-16s %-21s %-21s %-8s\n", "TIME", "PID", "COMM", "SADDR:SPORT", "DADDR:DPORT", "STATE"); 39 | 40 | // See https://github.com/torvalds/linux/blob/master/include/net/tcp_states.h 41 | @tcp_states[1] = "ESTABLISHED"; 42 | @tcp_states[2] = "SYN_SENT"; 43 | @tcp_states[3] = "SYN_RECV"; 44 | @tcp_states[4] = "FIN_WAIT1"; 45 | @tcp_states[5] = "FIN_WAIT2"; 46 | @tcp_states[6] = "TIME_WAIT"; 47 | @tcp_states[7] = "CLOSE"; 48 | @tcp_states[8] = "CLOSE_WAIT"; 49 | @tcp_states[9] = "LAST_ACK"; 50 | @tcp_states[10] = "LISTEN"; 51 | @tcp_states[11] = "CLOSING"; 52 | @tcp_states[12] = "NEW_SYN_RECV"; 53 | } 54 | 55 | kprobe:tcp_drop 56 | { 57 | $sk = ((struct sock *) arg0); 58 | $inet_family = $sk->__sk_common.skc_family; 59 | 60 | if ($inet_family == AF_INET || $inet_family == AF_INET6) { 61 | if ($inet_family == AF_INET) { 62 | $daddr = ntop($sk->__sk_common.skc_daddr); 63 | $saddr = ntop($sk->__sk_common.skc_rcv_saddr); 64 | } else { 65 | $daddr = ntop($sk->__sk_common.skc_v6_daddr.in6_u.u6_addr8); 66 | $saddr = ntop($sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr8); 67 | } 68 | $lport = $sk->__sk_common.skc_num; 69 | $dport = $sk->__sk_common.skc_dport; 70 | 71 | // Destination port is big endian, it must be flipped 72 | $dport = bswap($dport); 73 | 74 | $state = $sk->__sk_common.skc_state; 75 | $statestr = @tcp_states[$state]; 76 | 77 | time("%H:%M:%S "); 78 | printf("%-8d %-16s ", pid, comm); 79 | printf("%39s:%-6d %39s:%-6d %-10s\n", $saddr, $lport, $daddr, $dport, $statestr); 80 | printf("%s\n", kstack); 81 | } 82 | } 83 | 84 | END 85 | { 86 | clear(@tcp_states); 87 | } -------------------------------------------------------------------------------- /chatgpt/README.md: -------------------------------------------------------------------------------- 1 | # Chat eBPF 2 | 3 | ## 运行方法 4 | 5 | 安装依赖: 6 | 7 | ```sh 8 | pip install openai langchain chromadb 9 | ``` 10 | 11 | 配置环境变量: 12 | 13 | ```sh 14 | export OPENAI_API_KEY='' 15 | ``` 16 | 17 | 添加 Markdown 文档到 doc 目录,修改 [app.py](app.py) 提示词再执行下面的命令运行: 18 | 19 | ```sh 20 | python app.py 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /chatgpt/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | 4 | import openai 5 | from chromadb.config import Settings 6 | from langchain.chains import RetrievalQA 7 | from langchain.chat_models import ChatOpenAI 8 | from langchain.document_loaders import DirectoryLoader, TextLoader 9 | from langchain.embeddings import OpenAIEmbeddings 10 | from langchain.text_splitter import CharacterTextSplitter 11 | from langchain.vectorstores import Chroma 12 | 13 | # OpenAI Configuration 14 | default_model = 'gpt-4' 15 | if os.getenv("OPENAI_API_TYPE") == "azure" or (os.getenv("OPENAI_API_BASE") is not None and "azure" in os.getenv("OPENAI_API_BASE")): 16 | openai.api_type = "azure" 17 | openai.api_base = os.getenv("OPENAI_API_BASE") 18 | openai.api_version = "2023-05-15" 19 | openai.api_key = os.getenv("OPENAI_API_KEY") 20 | llm = ChatOpenAI(model_name=default_model, 21 | temperature=0, 22 | model_kwargs={"engine": default_model.replace('.', '')}) 23 | embeddings = OpenAIEmbeddings( 24 | deployment="text-embedding-ada-002", 25 | model="text-embedding-ada-002", 26 | openai_api_type=os.getenv("OPENAI_API_TYPE"), 27 | openai_api_base=os.getenv("OPENAI_API_BASE"), 28 | openai_api_version="2023-05-15", 29 | openai_api_key=os.getenv("OPENAI_API_KEY"), 30 | chunk_size=1 # Only 1 is allowed for Azure OpenAI 31 | ) 32 | else: 33 | openai.api_key = os.getenv("OPENAI_API_KEY") 34 | openai.api_base = os.getenv("OPENAI_API_BASE", "https://api.openai.com/v1") 35 | embeddings = OpenAIEmbeddings( 36 | model="text-embedding-ada-002", 37 | openai_api_key=os.getenv("OPENAI_API_KEY"), 38 | ) 39 | llm = ChatOpenAI(model=default_model, 40 | temperature=0) 41 | 42 | 43 | def load_store(): 44 | '''Load data and embedding.''' 45 | documents = DirectoryLoader("./doc/", 46 | glob="**/*.md", 47 | recursive=True, 48 | show_progress=True, 49 | silent_errors=True, 50 | use_multithreading=True, 51 | loader_cls=TextLoader, 52 | ).load() 53 | text_splitter = CharacterTextSplitter(chunk_size=4096, separator="\n") 54 | docs = [] 55 | for d in documents: 56 | splits = text_splitter.split_text(d.page_content) 57 | docs.extend(splits) 58 | 59 | chroma_store = Chroma.from_texts( 60 | docs, embeddings, persist_directory="./store", 61 | client_settings=Settings(anonymized_telemetry=False)) 62 | return chroma_store 63 | 64 | 65 | # Initialize store and chain 66 | print('Embedding documents...') 67 | store = load_store() 68 | chain = RetrievalQA.from_chain_type( 69 | llm=llm, chain_type="refine", retriever=store.as_retriever()) 70 | 71 | # Run the chain 72 | print('Running chain...') 73 | msg = '作为一名 Linux 内核和 eBPF 的专家,你的任务是开发一个 bpftrace 程序,跟踪系统中进程的系统调用数量。' 74 | print(chain.run(msg)) 75 | -------------------------------------------------------------------------------- /chatgpt/doc/README.md: -------------------------------------------------------------------------------- 1 | # 文档库 2 | 3 | 示例文档来自于 [bpftrace](https://github.com/iovisor/bpftrace)。 -------------------------------------------------------------------------------- /go/README.md: -------------------------------------------------------------------------------- 1 | # eBPF apps in Go 2 | 3 | ## eBPF Go libraries 4 | 5 | | Library | Summary | Dependencies | Stars | 6 | |---------|---------|--------------|-------| 7 | |[cilium/ebpf](https://github.com/cilium/ebpf)|a pure Go library that provides utilities for loading, compiling, and debugging eBPF programs | Linux>=4.4 | 2k | 8 | |[iovisor/gobpf](https://github.com/iovisor/gobpf)|go bindings for the bcc framework as well as low-level routines to load and use eBPF programs from .elf files| [bcc](https://github.com/iovisor/bcc), Runtime LLVM, CGO | 1.5k | 9 | |[aquasecurity/libbpfgo](https://github.com/aquasecurity/libbpfgo)|a Go wrapper around the libbpf project|[libbpf](https://github.com/libbpf/libbpf), CGO| 0.2k | 10 | |[dropbox/goebpf](https://github.com/dropbox/goebpf)|A nice and convenient way to work with eBPF programs / perf events from Go (limited eBPF feature support)|Linux>=4.15| 0.8k | 11 | -------------------------------------------------------------------------------- /go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= 2 | github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= 3 | github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= 4 | github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= 5 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 6 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 7 | github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= 8 | github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= 9 | github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM= 10 | github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE= 11 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 12 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 13 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 14 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 15 | github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= 16 | github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= 17 | github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= 18 | github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= 19 | github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= 20 | github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 21 | golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= 22 | golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= 23 | golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= 24 | golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= 25 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 26 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 27 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 28 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 29 | -------------------------------------------------------------------------------- /go/xdp_trace.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | //go:build ignore 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | 8 | SEC("fentry/xdp") 9 | int BPF_PROG(fentry_xdp, struct xdp_md *xdp) 10 | { 11 | void *data = (void *)(long)BPF_CORE_READ(xdp, data); 12 | void *data_end = (void *)(long)BPF_CORE_READ(xdp, data_end); 13 | 14 | struct ethhdr *eth = (struct ethhdr *)data; 15 | __be16 proto = BPF_CORE_READ(eth, h_proto); 16 | bpf_printk("[fentry/xdp] packet proto %d", proto); 17 | return 0; 18 | } 19 | 20 | SEC("fexit/xdp") 21 | int BPF_PROG(fexit_xdp, struct xdp_md *xdp, int ret) 22 | { 23 | bpf_printk("[fexit/xdp] ret: %d", ret); 24 | return 0; 25 | } 26 | 27 | char _license[] SEC("license") = "Dual BSD/GPL"; 28 | -------------------------------------------------------------------------------- /go/xdp_trace_bpfeb.go: -------------------------------------------------------------------------------- 1 | // Code generated by bpf2go; DO NOT EDIT. 2 | //go:build mips || mips64 || ppc64 || s390x 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | _ "embed" 9 | "fmt" 10 | "io" 11 | 12 | "github.com/cilium/ebpf" 13 | ) 14 | 15 | // loadXdp_trace returns the embedded CollectionSpec for xdp_trace. 16 | func loadXdp_trace() (*ebpf.CollectionSpec, error) { 17 | reader := bytes.NewReader(_Xdp_traceBytes) 18 | spec, err := ebpf.LoadCollectionSpecFromReader(reader) 19 | if err != nil { 20 | return nil, fmt.Errorf("can't load xdp_trace: %w", err) 21 | } 22 | 23 | return spec, err 24 | } 25 | 26 | // loadXdp_traceObjects loads xdp_trace and converts it into a struct. 27 | // 28 | // The following types are suitable as obj argument: 29 | // 30 | // *xdp_traceObjects 31 | // *xdp_tracePrograms 32 | // *xdp_traceMaps 33 | // 34 | // See ebpf.CollectionSpec.LoadAndAssign documentation for details. 35 | func loadXdp_traceObjects(obj interface{}, opts *ebpf.CollectionOptions) error { 36 | spec, err := loadXdp_trace() 37 | if err != nil { 38 | return err 39 | } 40 | 41 | return spec.LoadAndAssign(obj, opts) 42 | } 43 | 44 | // xdp_traceSpecs contains maps and programs before they are loaded into the kernel. 45 | // 46 | // It can be passed ebpf.CollectionSpec.Assign. 47 | type xdp_traceSpecs struct { 48 | xdp_traceProgramSpecs 49 | xdp_traceMapSpecs 50 | } 51 | 52 | // xdp_traceSpecs contains programs before they are loaded into the kernel. 53 | // 54 | // It can be passed ebpf.CollectionSpec.Assign. 55 | type xdp_traceProgramSpecs struct { 56 | FentryXdp *ebpf.ProgramSpec `ebpf:"fentry_xdp"` 57 | FexitXdp *ebpf.ProgramSpec `ebpf:"fexit_xdp"` 58 | } 59 | 60 | // xdp_traceMapSpecs contains maps before they are loaded into the kernel. 61 | // 62 | // It can be passed ebpf.CollectionSpec.Assign. 63 | type xdp_traceMapSpecs struct { 64 | } 65 | 66 | // xdp_traceObjects contains all objects after they have been loaded into the kernel. 67 | // 68 | // It can be passed to loadXdp_traceObjects or ebpf.CollectionSpec.LoadAndAssign. 69 | type xdp_traceObjects struct { 70 | xdp_tracePrograms 71 | xdp_traceMaps 72 | } 73 | 74 | func (o *xdp_traceObjects) Close() error { 75 | return _Xdp_traceClose( 76 | &o.xdp_tracePrograms, 77 | &o.xdp_traceMaps, 78 | ) 79 | } 80 | 81 | // xdp_traceMaps contains all maps after they have been loaded into the kernel. 82 | // 83 | // It can be passed to loadXdp_traceObjects or ebpf.CollectionSpec.LoadAndAssign. 84 | type xdp_traceMaps struct { 85 | } 86 | 87 | func (m *xdp_traceMaps) Close() error { 88 | return _Xdp_traceClose() 89 | } 90 | 91 | // xdp_tracePrograms contains all programs after they have been loaded into the kernel. 92 | // 93 | // It can be passed to loadXdp_traceObjects or ebpf.CollectionSpec.LoadAndAssign. 94 | type xdp_tracePrograms struct { 95 | FentryXdp *ebpf.Program `ebpf:"fentry_xdp"` 96 | FexitXdp *ebpf.Program `ebpf:"fexit_xdp"` 97 | } 98 | 99 | func (p *xdp_tracePrograms) Close() error { 100 | return _Xdp_traceClose( 101 | p.FentryXdp, 102 | p.FexitXdp, 103 | ) 104 | } 105 | 106 | func _Xdp_traceClose(closers ...io.Closer) error { 107 | for _, closer := range closers { 108 | if err := closer.Close(); err != nil { 109 | return err 110 | } 111 | } 112 | return nil 113 | } 114 | 115 | // Do not access this directly. 116 | // 117 | //go:embed xdp_trace_bpfeb.o 118 | var _Xdp_traceBytes []byte 119 | -------------------------------------------------------------------------------- /go/xdp_trace_bpfel.go: -------------------------------------------------------------------------------- 1 | // Code generated by bpf2go; DO NOT EDIT. 2 | //go:build 386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | _ "embed" 9 | "fmt" 10 | "io" 11 | 12 | "github.com/cilium/ebpf" 13 | ) 14 | 15 | // loadXdp_trace returns the embedded CollectionSpec for xdp_trace. 16 | func loadXdp_trace() (*ebpf.CollectionSpec, error) { 17 | reader := bytes.NewReader(_Xdp_traceBytes) 18 | spec, err := ebpf.LoadCollectionSpecFromReader(reader) 19 | if err != nil { 20 | return nil, fmt.Errorf("can't load xdp_trace: %w", err) 21 | } 22 | 23 | return spec, err 24 | } 25 | 26 | // loadXdp_traceObjects loads xdp_trace and converts it into a struct. 27 | // 28 | // The following types are suitable as obj argument: 29 | // 30 | // *xdp_traceObjects 31 | // *xdp_tracePrograms 32 | // *xdp_traceMaps 33 | // 34 | // See ebpf.CollectionSpec.LoadAndAssign documentation for details. 35 | func loadXdp_traceObjects(obj interface{}, opts *ebpf.CollectionOptions) error { 36 | spec, err := loadXdp_trace() 37 | if err != nil { 38 | return err 39 | } 40 | 41 | return spec.LoadAndAssign(obj, opts) 42 | } 43 | 44 | // xdp_traceSpecs contains maps and programs before they are loaded into the kernel. 45 | // 46 | // It can be passed ebpf.CollectionSpec.Assign. 47 | type xdp_traceSpecs struct { 48 | xdp_traceProgramSpecs 49 | xdp_traceMapSpecs 50 | } 51 | 52 | // xdp_traceSpecs contains programs before they are loaded into the kernel. 53 | // 54 | // It can be passed ebpf.CollectionSpec.Assign. 55 | type xdp_traceProgramSpecs struct { 56 | FentryXdp *ebpf.ProgramSpec `ebpf:"fentry_xdp"` 57 | FexitXdp *ebpf.ProgramSpec `ebpf:"fexit_xdp"` 58 | } 59 | 60 | // xdp_traceMapSpecs contains maps before they are loaded into the kernel. 61 | // 62 | // It can be passed ebpf.CollectionSpec.Assign. 63 | type xdp_traceMapSpecs struct { 64 | } 65 | 66 | // xdp_traceObjects contains all objects after they have been loaded into the kernel. 67 | // 68 | // It can be passed to loadXdp_traceObjects or ebpf.CollectionSpec.LoadAndAssign. 69 | type xdp_traceObjects struct { 70 | xdp_tracePrograms 71 | xdp_traceMaps 72 | } 73 | 74 | func (o *xdp_traceObjects) Close() error { 75 | return _Xdp_traceClose( 76 | &o.xdp_tracePrograms, 77 | &o.xdp_traceMaps, 78 | ) 79 | } 80 | 81 | // xdp_traceMaps contains all maps after they have been loaded into the kernel. 82 | // 83 | // It can be passed to loadXdp_traceObjects or ebpf.CollectionSpec.LoadAndAssign. 84 | type xdp_traceMaps struct { 85 | } 86 | 87 | func (m *xdp_traceMaps) Close() error { 88 | return _Xdp_traceClose() 89 | } 90 | 91 | // xdp_tracePrograms contains all programs after they have been loaded into the kernel. 92 | // 93 | // It can be passed to loadXdp_traceObjects or ebpf.CollectionSpec.LoadAndAssign. 94 | type xdp_tracePrograms struct { 95 | FentryXdp *ebpf.Program `ebpf:"fentry_xdp"` 96 | FexitXdp *ebpf.Program `ebpf:"fexit_xdp"` 97 | } 98 | 99 | func (p *xdp_tracePrograms) Close() error { 100 | return _Xdp_traceClose( 101 | p.FentryXdp, 102 | p.FexitXdp, 103 | ) 104 | } 105 | 106 | func _Xdp_traceClose(closers ...io.Closer) error { 107 | for _, closer := range closers { 108 | if err := closer.Close(); err != nil { 109 | return err 110 | } 111 | } 112 | return nil 113 | } 114 | 115 | // Do not access this directly. 116 | // 117 | //go:embed xdp_trace_bpfel.o 118 | var _Xdp_traceBytes []byte 119 | -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | APPS = hello 2 | 3 | .PHONY: all 4 | all: $(APPS) 5 | 6 | KERN_SOURCES = ${APPS:=_kern.c} 7 | USER_SOURCES = ${APPS:=_user.c} 8 | USER_SOURCES += trace_helpers.c 9 | KERN_OBJECTS = ${KERN_SOURCES:.c=.o} 10 | USER_OBJECTS = ${USER_SOURCES:.c=.o} 11 | USER_LIBS += trace_helpers.o 12 | 13 | KERNEL_SOURCE ?= /usr/src/$(shell uname -r) 14 | KERNEL_INCLUDES := -I$(KERNEL_SOURCE)/tools/lib/ 15 | KERNEL_INCLUDES += -I$(KERNEL_SOURCE)/arch/x86/include 16 | KERNEL_INCLUDES += -I$(KERNEL_SOURCE)/arch/x86/include/generated/uapi 17 | KERNEL_INCLUDES += -I$(KERNEL_SOURCE)/arch/x86/include/generated 18 | KERNEL_INCLUDES += -I$(KERNEL_SOURCE)/arch/x86/include/uapi 19 | KERNEL_INCLUDES += -I$(KERNEL_SOURCE)/include/uapi 20 | KERNEL_INCLUDES += -I$(KERNEL_SOURCE)/include/generated/uapi 21 | KERNEL_INCLUDES += -I$(KERNEL_SOURCE)/include/linux/kconfig.h 22 | KERNEL_INCLUDES += -I$(KERNEL_SOURCE)/include 23 | 24 | LIBBPF = -L$(KERNEL_SOURCE)/tools/lib/bpf/ 25 | 26 | $(APPS): %: %_kern.o %_user.o $(USER_LIBS) 27 | clang -Wall -O2 -g $@_user.o $(USER_LIBS) -static $(LIBBPF) -lbpf -lelf -lz -o $@ 28 | 29 | $(USER_OBJECTS): %.o: %.c 30 | clang -g -O2 -Wall -I . -c $< -o $@ 31 | 32 | $(KERN_OBJECTS): %.o: %.c 33 | clang -g -O2 \ 34 | -target bpf \ 35 | -c $< -o $@ \ 36 | -D__TARGET_ARCH_x86 \ 37 | -D__KERNEL__ -D__ASM_SYSREG_H \ 38 | -Wno-unused-value -Wno-pointer-sign \ 39 | -Wno-compare-distinct-pointer-types \ 40 | -Wno-gnu-variable-sized-type-not-at-end \ 41 | -Wno-address-of-packed-member -Wno-tautological-compare \ 42 | -Wno-unknown-warning-option \ 43 | $(KERNEL_INCLUDES) 44 | 45 | format: 46 | VERSION_CONTROL=none indent -linux *.h *.c 47 | 48 | clean: 49 | rm -rf $(APPS) *.o 50 | -------------------------------------------------------------------------------- /kernel/README.md: -------------------------------------------------------------------------------- 1 | # eBPF w/ kernel 2 | 3 | eBPF apps built with kernel source code. 4 | 5 | ## Pre-requisites 6 | 7 | * Download kernel source code and build kernel's BPF lib under `tools/lib/bpf`. 8 | 9 | ## How to build and run 10 | 11 | ```sh 12 | export KERNEL_SOURCE= 13 | make 14 | sudo ./hello 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /kernel/hello_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | SEC("tracepoint/syscalls/sys_enter_execve") 5 | int bpf_prog(void *ctx) 6 | { 7 | char msg[] = "Hello BPF!"; 8 | bpf_trace_printk(msg, sizeof(msg)); 9 | return 0; 10 | } 11 | 12 | char _license[] SEC("license") = "Dual BSD/GPL"; 13 | -------------------------------------------------------------------------------- /kernel/hello_user.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "trace_helpers.h" 5 | 6 | int main(int argc, char **argv) 7 | { 8 | struct bpf_link *link = NULL; 9 | struct bpf_program *prog; 10 | struct bpf_object *obj; 11 | char filename[256]; 12 | 13 | snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); 14 | obj = bpf_object__open_file(filename, NULL); 15 | if (libbpf_get_error(obj)) { 16 | fprintf(stderr, "ERROR: opening BPF object file failed\n"); 17 | return 0; 18 | } 19 | 20 | prog = bpf_object__find_program_by_name(obj, "bpf_prog"); 21 | if (!prog) { 22 | fprintf(stderr, "ERROR: finding a prog in obj file failed\n"); 23 | goto cleanup; 24 | } 25 | 26 | /* load BPF program */ 27 | if (bpf_object__load(obj)) { 28 | fprintf(stderr, "ERROR: loading BPF object file failed\n"); 29 | goto cleanup; 30 | } 31 | 32 | link = bpf_program__attach(prog); 33 | if (libbpf_get_error(link)) { 34 | fprintf(stderr, "ERROR: bpf_program__attach failed\n"); 35 | link = NULL; 36 | goto cleanup; 37 | } 38 | 39 | read_trace_pipe(); 40 | 41 | cleanup: 42 | bpf_link__destroy(link); 43 | bpf_object__close(obj); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /kernel/trace_helpers.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // refer https://elixir.bootlin.com/linux/v5.13/source/tools/testing/selftests/bpf/trace_helpers.c 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "trace_helpers.h" 14 | 15 | #define DEBUGFS "/sys/kernel/debug/tracing/" 16 | 17 | #define MAX_SYMS 300000 18 | static struct ksym syms[MAX_SYMS]; 19 | static int sym_cnt; 20 | 21 | static int ksym_cmp(const void *p1, const void *p2) 22 | { 23 | return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr; 24 | } 25 | 26 | int load_kallsyms(void) 27 | { 28 | FILE *f = fopen("/proc/kallsyms", "r"); 29 | char func[256], buf[256]; 30 | char symbol; 31 | void *addr; 32 | int i = 0; 33 | 34 | if (!f) 35 | return -ENOENT; 36 | 37 | while (fgets(buf, sizeof(buf), f)) { 38 | if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3) 39 | break; 40 | if (!addr) 41 | continue; 42 | syms[i].addr = (long)addr; 43 | syms[i].name = strdup(func); 44 | i++; 45 | } 46 | fclose(f); 47 | sym_cnt = i; 48 | qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp); 49 | return 0; 50 | } 51 | 52 | struct ksym *ksym_search(long key) 53 | { 54 | int start = 0, end = sym_cnt; 55 | int result; 56 | 57 | /* kallsyms not loaded. return NULL */ 58 | if (sym_cnt <= 0) 59 | return NULL; 60 | 61 | while (start < end) { 62 | size_t mid = start + (end - start) / 2; 63 | 64 | result = key - syms[mid].addr; 65 | if (result < 0) 66 | end = mid; 67 | else if (result > 0) 68 | start = mid + 1; 69 | else 70 | return &syms[mid]; 71 | } 72 | 73 | if (start >= 1 && syms[start - 1].addr < key && key < syms[start].addr) 74 | /* valid ksym */ 75 | return &syms[start - 1]; 76 | 77 | /* out of range. return _stext */ 78 | return &syms[0]; 79 | } 80 | 81 | long ksym_get_addr(const char *name) 82 | { 83 | int i; 84 | 85 | for (i = 0; i < sym_cnt; i++) { 86 | if (strcmp(syms[i].name, name) == 0) 87 | return syms[i].addr; 88 | } 89 | 90 | return 0; 91 | } 92 | 93 | /* open kallsyms and read symbol addresses on the fly. Without caching all symbols, 94 | * this is faster than load + find. 95 | */ 96 | int kallsyms_find(const char *sym, unsigned long long *addr) 97 | { 98 | char type, name[500]; 99 | unsigned long long value; 100 | int err = 0; 101 | FILE *f; 102 | 103 | f = fopen("/proc/kallsyms", "r"); 104 | if (!f) 105 | return -EINVAL; 106 | 107 | while (fscanf(f, "%llx %c %499s%*[^\n]\n", &value, &type, name) > 0) { 108 | if (strcmp(name, sym) == 0) { 109 | *addr = value; 110 | goto out; 111 | } 112 | } 113 | err = -ENOENT; 114 | 115 | out: 116 | fclose(f); 117 | return err; 118 | } 119 | 120 | void read_trace_pipe(void) 121 | { 122 | int trace_fd; 123 | 124 | trace_fd = open(DEBUGFS "trace_pipe", O_RDONLY, 0); 125 | if (trace_fd < 0) 126 | return; 127 | 128 | while (1) { 129 | static char buf[4096]; 130 | ssize_t sz; 131 | 132 | sz = read(trace_fd, buf, sizeof(buf) - 1); 133 | if (sz > 0) { 134 | buf[sz] = 0; 135 | puts(buf); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /kernel/trace_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* refer https://elixir.bootlin.com/linux/v5.13/source/tools/testing/selftests/bpf/trace_helpers.h */ 3 | #ifndef __TRACE_HELPER_H 4 | #define __TRACE_HELPER_H 5 | 6 | #include 7 | 8 | struct ksym { 9 | long addr; 10 | char *name; 11 | }; 12 | 13 | int load_kallsyms(void); 14 | struct ksym *ksym_search(long key); 15 | long ksym_get_addr(const char *name); 16 | 17 | /* open kallsyms and find addresses on the fly, faster than load + search. */ 18 | int kallsyms_find(const char *sym, unsigned long long *addr); 19 | 20 | void read_trace_pipe(void); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /loadbalancer/README.md: -------------------------------------------------------------------------------- 1 | # LoadBalancer with eBPF 2 | 3 | Refer README file in subdirectories for more details. 4 | -------------------------------------------------------------------------------- /loadbalancer/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | 3 | ADD nginx.conf /etc/nginx/nginx.conf 4 | -------------------------------------------------------------------------------- /loadbalancer/nginx/README.md: -------------------------------------------------------------------------------- 1 | # LoadBalancer with nginx 2 | 3 | How to update Nginx config dynamically: 4 | 5 | ```sh 6 | # Start a normal nginx container 7 | docker run -itd --name=nginx nginx 8 | 9 | # Update config 10 | docker cp nginx.conf nginx:/etc/nginx/nginx.conf 11 | 12 | # Reload config 13 | docker exec nginx nginx -s reload 14 | ``` 15 | -------------------------------------------------------------------------------- /loadbalancer/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | worker_processes auto; 3 | 4 | error_log /var/log/nginx/error.log notice; 5 | pid /var/run/nginx.pid; 6 | 7 | events { 8 | worker_connections 1024; 9 | } 10 | 11 | http { 12 | include /etc/nginx/mime.types; 13 | default_type application/octet-stream; 14 | 15 | upstream webservers { 16 | server 172.17.0.2; 17 | server 172.17.0.3; 18 | } 19 | 20 | server { 21 | listen 80; 22 | 23 | location / { 24 | proxy_pass http://webservers; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /loadbalancer/sockops/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build 2 | build: 3 | clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -I/usr/include/x86_64-linux-gnu -I. -c sockops.bpf.c -o sockops.bpf.o 4 | clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -I/usr/include/x86_64-linux-gnu -I. -c sockredir.bpf.c -o sockredir.bpf.o 5 | 6 | run: 7 | sudo bpftool prog load sockops.bpf.o /sys/fs/bpf/sockops type sockops pinmaps /sys/fs/bpf 8 | sudo bpftool prog load sockredir.bpf.o /sys/fs/bpf/sockredir type sk_msg map name sock_ops_map pinned /sys/fs/bpf/sock_ops_map 9 | sudo bpftool cgroup attach /sys/fs/cgroup/ sock_ops pinned /sys/fs/bpf/sockops 10 | sudo bpftool prog attach pinned /sys/fs/bpf/sockredir msg_verdict pinned /sys/fs/bpf/sock_ops_map 11 | 12 | map: 13 | sudo bpftool map dump name sock_ops_map 14 | 15 | clean: 16 | sudo bpftool prog detach pinned /sys/fs/bpf/sockredir msg_verdict pinned /sys/fs/bpf/sock_ops_map 17 | sudo bpftool cgroup detach /sys/fs/cgroup/ sock_ops name bpf_sockmap 18 | sudo rm -rf /sys/fs/bpf/sockops /sys/fs/bpf/sockredir /sys/fs/bpf/sock_ops_map 19 | -------------------------------------------------------------------------------- /loadbalancer/sockops/README.md: -------------------------------------------------------------------------------- 1 | # Sockops eBPF 2 | 3 | ```sh 4 | # Build 5 | make build 6 | 7 | # Run (attach to /sys/fs/cgroup/ by default) 8 | make run 9 | 10 | # Cleanup 11 | make clean 12 | ``` 13 | -------------------------------------------------------------------------------- /loadbalancer/sockops/sockops.bpf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "sockops.h" 6 | 7 | SEC("sockops") 8 | int bpf_sockmap(struct bpf_sock_ops *skops) 9 | { 10 | /* skip if the packet is not ipv4 */ 11 | if (skops->family != AF_INET) { 12 | return BPF_OK; 13 | } 14 | 15 | /* skip if it is not established op */ 16 | if (skops->op != BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB 17 | && skops->op != BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB) { 18 | return BPF_OK; 19 | } 20 | 21 | struct sock_key key = { 22 | .dip = skops->remote_ip4, 23 | .sip = skops->local_ip4, 24 | /* convert to network byte order */ 25 | .sport = bpf_htonl(skops->local_port), 26 | .dport = skops->remote_port, 27 | .family = skops->family, 28 | }; 29 | 30 | bpf_sock_hash_update(skops, &sock_ops_map, &key, BPF_NOEXIST); 31 | return BPF_OK; 32 | } 33 | 34 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 35 | -------------------------------------------------------------------------------- /loadbalancer/sockops/sockops.h: -------------------------------------------------------------------------------- 1 | #ifndef __SOCK_OPS_H__ 2 | #define __SOCK_OPS_H__ 3 | 4 | #include 5 | 6 | struct sock_key { 7 | __u32 sip; 8 | __u32 dip; 9 | __u32 sport; 10 | __u32 dport; 11 | __u32 family; 12 | }; 13 | 14 | struct { 15 | __uint(type, BPF_MAP_TYPE_SOCKHASH); 16 | __uint(key_size, sizeof(struct sock_key)); 17 | __uint(value_size, sizeof(int)); 18 | __uint(max_entries, 65535); 19 | __uint(map_flags, 0); 20 | } sock_ops_map SEC(".maps"); 21 | 22 | #endif /* __SOCK_OPS_H__ */ 23 | -------------------------------------------------------------------------------- /loadbalancer/sockops/sockredir.bpf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "sockops.h" 6 | 7 | SEC("sk_msg") 8 | int bpf_redir(struct sk_msg_md *msg) 9 | { 10 | struct sock_key key = { 11 | .sip = msg->remote_ip4, 12 | .dip = msg->local_ip4, 13 | .dport = bpf_htonl(msg->local_port), // convert to network byte order 14 | .sport = msg->remote_port, 15 | .family = msg->family, 16 | }; 17 | 18 | bpf_msg_redirect_hash(msg, &sock_ops_map, &key, BPF_F_INGRESS); 19 | return SK_PASS; 20 | } 21 | 22 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 23 | -------------------------------------------------------------------------------- /loadbalancer/webserver/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM k8s.gcr.io/echoserver:1.10 2 | 3 | ADD nginx.conf /etc/nginx/nginx.conf 4 | -------------------------------------------------------------------------------- /loadbalancer/webserver/README.md: -------------------------------------------------------------------------------- 1 | # Webserver Container 2 | 3 | Sample response: 4 | 5 | ```sh 6 | $ curl http://172.17.0.5 7 | Hostname: http1 8 | ``` 9 | -------------------------------------------------------------------------------- /loadbalancer/webserver/nginx.conf: -------------------------------------------------------------------------------- 1 | events { 2 | worker_connections 1024; 3 | } 4 | 5 | env HOSTNAME; 6 | 7 | http { 8 | default_type 'text/plain'; 9 | # maximum allowed size of the client request body. By default this is 1m. 10 | # Request with bigger bodies nginx will return error code 413. 11 | # http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size 12 | client_max_body_size 10m; 13 | 14 | init_by_lua_block { 15 | local template = require("template") 16 | -- template syntax documented here: 17 | -- https://github.com/bungle/lua-resty-template/blob/master/README.md 18 | tmpl = template.compile([[ 19 | Hostname: {{os.getenv("HOSTNAME") or "N/A"}} 20 | # 21 | # Request Information: 22 | # client_address={{ngx.var.remote_addr}} 23 | # method={{ngx.req.get_method()}} 24 | # real path={{ngx.var.request_uri}} 25 | # query={{ngx.var.query_string or ""}} 26 | # request_version={{ngx.req.http_version()}} 27 | # request_scheme={{ngx.var.scheme}} 28 | # request_uri={{ngx.var.scheme.."://"..ngx.var.host..":"..ngx.var.server_port..ngx.var.request_uri}} 29 | 30 | # Request Headers: 31 | # {% for i, key in ipairs(keys) do %} 32 | # {{key}}={{headers[key]}} 33 | # {% end %} 34 | ]]) 35 | } 36 | 37 | server { 38 | # please check the benefits of reuseport https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1 39 | # basically instructs to create an individual listening socket for each worker process (using the SO_REUSEPORT 40 | # socket option), allowing a kernel to distribute incoming connections between worker processes. 41 | listen 80 default_server reuseport; 42 | listen 443 default_server ssl http2 reuseport; 43 | 44 | ssl_certificate /certs/certificate.crt; 45 | ssl_certificate_key /certs/privateKey.key; 46 | 47 | # Replace '_' with your hostname. 48 | server_name _; 49 | 50 | location / { 51 | lua_need_request_body on; 52 | content_by_lua_block { 53 | ngx.header["Server"] = "echoserver" 54 | 55 | local headers = ngx.req.get_headers() 56 | local keys = {} 57 | for key, val in pairs(headers) do 58 | table.insert(keys, key) 59 | end 60 | table.sort(keys) 61 | 62 | ngx.say(tmpl({os=os, ngx=ngx, keys=keys, headers=headers})) 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /loadbalancer/xdp/Makefile: -------------------------------------------------------------------------------- 1 | APPS= xdp-proxy xdp-proxy-v2 2 | LIBBPF_SRC := $(abspath ../../libbpf/src) 3 | LIBBPF_OBJ := $(abspath ../../bpf-apps/libbpf/libbpf.a) 4 | INCLUDES := -Ilibbpf/usr/include -I../libbpf/include/uapi -I. 5 | 6 | .PHONY: all 7 | all: $(APPS) 8 | 9 | $(APPS): %: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) 10 | clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -I/usr/include/x86_64-linux-gnu -I. -c $@.bpf.c -o $@.bpf.o 11 | bpftool gen skeleton $@.bpf.o > $@.skel.h 12 | clang -g -O2 -Wall $(INCLUDES) -c $@.c -o $@.o 13 | clang -Wall -O2 -g $@.o $(LIBBPF_OBJ) -static -lelf -lz -o $@ 14 | 15 | libbpf: $(LIBBPF_OBJ) 16 | $(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) 17 | make -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 OBJDIR=$(dir $@) DESTDIR=$(dir $@) install 18 | 19 | clean: 20 | rm -rf $(APPS) *.o 21 | -------------------------------------------------------------------------------- /loadbalancer/xdp/README.md: -------------------------------------------------------------------------------- 1 | # XDP eBPF 2 | 3 | ```sh 4 | # Webserver 5 | docker run -itd --name=http1 --hostname=http1 feisky/webserver 6 | docker run -itd --name=http2 --hostname=http2 feisky/webserver 7 | 8 | # Client 9 | docker run -itd --name=client alpine 10 | 11 | # LB (XDP would be setup in xdp-proxy) 12 | docker run -itd --name=lb --privileged -v /sys/kernel/debug:/sys/kernel/debug alpine 13 | docker cp xdp-proxy lb:/ # or for v2, run "docker cp xdp-proxy-v2 lb:/" 14 | docker exec -it lb /xdp-proxy 15 | 16 | # Open a new termnial and test the client 17 | docker exec -it client apk add curl --update 18 | docker exec -it client curl "http://172.17.0.5" 19 | ``` 20 | -------------------------------------------------------------------------------- /loadbalancer/xdp/xdp-proxy-v2.bpf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "xdp-proxy-v2.h" 9 | 10 | /* define a hashmap for userspace to update service endpoints */ 11 | struct 12 | { 13 | __uint(type, BPF_MAP_TYPE_HASH); 14 | __type(key, __be32); 15 | __type(value, struct endpoints); 16 | __uint(max_entries, 1024); 17 | } services SEC(".maps"); 18 | 19 | /* Refer https://github.com/facebookincubator/katran/blob/main/katran/lib/bpf/csum_helpers.h#L30 */ 20 | static __always_inline __u16 csum_fold_helper(__u64 csum) 21 | { 22 | int i; 23 | #pragma unroll 24 | for (i = 0; i < 4; i++) 25 | { 26 | if (csum >> 16) 27 | csum = (csum & 0xffff) + (csum >> 16); 28 | } 29 | return ~csum; 30 | } 31 | 32 | static __always_inline __u16 ipv4_csum(struct iphdr *iph) 33 | { 34 | iph->check = 0; 35 | unsigned long long csum = 36 | bpf_csum_diff(0, 0, (unsigned int *)iph, sizeof(struct iphdr), 0); 37 | return csum_fold_helper(csum); 38 | } 39 | 40 | SEC("xdp") 41 | int xdp_proxy(struct xdp_md *ctx) 42 | { 43 | void *data = (void *)(long)ctx->data; 44 | void *data_end = (void *)(long)ctx->data_end; 45 | 46 | struct ethhdr *eth = data; 47 | /* abort on illegal packets */ 48 | if (data + sizeof(struct ethhdr) > data_end) 49 | { 50 | return XDP_ABORTED; 51 | } 52 | 53 | /* do nothing for non-IP packets */ 54 | if (eth->h_proto != bpf_htons(ETH_P_IP)) 55 | { 56 | return XDP_PASS; 57 | } 58 | 59 | struct iphdr *iph = data + sizeof(struct ethhdr); 60 | /* abort on illegal packets */ 61 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) 62 | { 63 | return XDP_ABORTED; 64 | } 65 | 66 | /* do nothing for non-TCP packets */ 67 | if (iph->protocol != IPPROTO_TCP) 68 | { 69 | return XDP_PASS; 70 | } 71 | 72 | __be32 svc1_key = SVC1_KEY; 73 | struct endpoints *ep = bpf_map_lookup_elem(&services, &svc1_key); 74 | if (!ep) 75 | { 76 | return XDP_PASS; 77 | } 78 | // bpf_printk("Client IP: %ld", ep->client); 79 | // bpf_printk("Endpoint IPs: %ld, %ld", ep->ep1, ep->ep2); 80 | // bpf_printk("New TCP packet %ld => %ld\n", iph->saddr, iph->daddr); 81 | 82 | if (iph->saddr == ep->client) 83 | { 84 | iph->daddr = ep->ep1; 85 | memcpy(eth->h_dest, ep->ep1_mac, ETH_ALEN); 86 | 87 | /* simulate random selection of two endpoints */ 88 | if ((bpf_ktime_get_ns() & 0x1) == 0x1) 89 | { 90 | iph->daddr = ep->ep2; 91 | memcpy(eth->h_dest, ep->ep2_mac, ETH_ALEN); 92 | } 93 | } 94 | else 95 | { 96 | iph->daddr = ep->client; 97 | memcpy(eth->h_dest, ep->client_mac, ETH_ALEN); 98 | } 99 | 100 | /* packet source is always LB itself */ 101 | iph->saddr = ep->vip; 102 | memcpy(eth->h_source, ep->vip_mac, ETH_ALEN); 103 | 104 | /* recalculate IP checksum */ 105 | iph->check = ipv4_csum(iph); 106 | 107 | /* send packet back to network stack */ 108 | return XDP_TX; 109 | } 110 | 111 | char _license[] SEC("license") = "Dual BSD/GPL"; 112 | -------------------------------------------------------------------------------- /loadbalancer/xdp/xdp-proxy-v2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "xdp-proxy-v2.skel.h" 11 | #include "xdp-proxy-v2.h" 12 | 13 | /* Attach to eth0 by default */ 14 | #define DEV_NAME "eth0" 15 | 16 | int main(int argc, char **argv) 17 | { 18 | __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_SKB_MODE; 19 | struct xdp_proxy_v2_bpf *obj; 20 | int err = 0; 21 | 22 | struct rlimit rlim_new = { 23 | .rlim_cur = RLIM_INFINITY, 24 | .rlim_max = RLIM_INFINITY, 25 | }; 26 | 27 | err = setrlimit(RLIMIT_MEMLOCK, &rlim_new); 28 | if (err) 29 | { 30 | fprintf(stderr, "failed to change rlimit\n"); 31 | return 1; 32 | } 33 | 34 | unsigned int ifindex = if_nametoindex(DEV_NAME); 35 | if (ifindex == 0) 36 | { 37 | fprintf(stderr, "failed to find interface %s\n", DEV_NAME); 38 | return 1; 39 | } 40 | 41 | obj = xdp_proxy_v2_bpf__open(); 42 | if (!obj) 43 | { 44 | fprintf(stderr, "failed to open and/or load BPF object\n"); 45 | return 1; 46 | } 47 | 48 | err = xdp_proxy_v2_bpf__load(obj); 49 | if (err) 50 | { 51 | fprintf(stderr, "failed to load BPF object %d\n", err); 52 | goto cleanup; 53 | } 54 | 55 | __be32 svc1_key = SVC1_KEY; 56 | struct endpoints ep = 57 | { 58 | /* fixme: replace with your own values if they are different */ 59 | .ep1 = inet_addr("172.17.0.2"), /* Hex:0x20011ac Dec:33558956 */ 60 | .ep2 = inet_addr("172.17.0.3"), /* Hex:0x30011ac Dec:50336172 */ 61 | .client = inet_addr("172.17.0.4"), /* Hex:0x40011ac Dec:67113388 */ 62 | .vip = inet_addr("172.17.0.5"), /* Hex:0x50011ac Dec:83890604 */ 63 | .ep1_mac = {0x02, 0x42, 0xac, 0x11, 0x00, 0x02}, /* 02:42:ac:11:00:02 */ 64 | .ep2_mac = {0x02, 0x42, 0xac, 0x11, 0x00, 0x03}, /* 02:42:ac:11:00:03 */ 65 | .client_mac = {0x02, 0x42, 0xac, 0x11, 0x00, 0x04}, /* 02:42:ac:11:00:04 */ 66 | .vip_mac = {0x02, 0x42, 0xac, 0x11, 0x00, 0x05}, /* 02:42:ac:11:00:05 */ 67 | }; 68 | int map_id = bpf_map__fd(obj->maps.services); 69 | err = bpf_map_update_elem(map_id, &svc1_key, &ep, BPF_ANY); 70 | if (err) 71 | { 72 | fprintf(stderr, "failed to update maps\n"); 73 | goto cleanup; 74 | } 75 | 76 | /* Attach the XDP program to eth0 */ 77 | int prog_id = bpf_program__fd(obj->progs.xdp_proxy); 78 | LIBBPF_OPTS(bpf_xdp_attach_opts, attach_opts); 79 | err = bpf_xdp_attach(ifindex, prog_id, xdp_flags, &attach_opts); 80 | if (err) 81 | { 82 | fprintf(stderr, "failed to attach BPF programs\n"); 83 | goto cleanup; 84 | } 85 | 86 | printf("Successfully run! Tracing /sys/kernel/debug/tracing/trace_pipe.\n"); 87 | system("cat /sys/kernel/debug/tracing/trace_pipe"); 88 | 89 | cleanup: 90 | /* detach and free XDP program on exit */ 91 | bpf_xdp_detach(ifindex, xdp_flags, &attach_opts); 92 | xdp_proxy_v2_bpf__destroy(obj); 93 | return err != 0; 94 | } 95 | -------------------------------------------------------------------------------- /loadbalancer/xdp/xdp-proxy-v2.h: -------------------------------------------------------------------------------- 1 | #ifndef __XDP_PROXY_H__ 2 | #define __XDP_PROXY_H__ 3 | 4 | #include 5 | #include 6 | 7 | #define SVC1_KEY 0x1 8 | 9 | struct endpoints { 10 | __be32 client; 11 | __be32 ep1; 12 | __be32 ep2; 13 | __be32 vip; 14 | unsigned char ep1_mac[ETH_ALEN]; 15 | unsigned char ep2_mac[ETH_ALEN]; 16 | unsigned char client_mac[ETH_ALEN]; 17 | unsigned char vip_mac[ETH_ALEN]; 18 | } __attribute__((packed)); 19 | 20 | #ifndef memcpy 21 | #define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n)) 22 | #endif 23 | 24 | #endif /* __XDP_PROXY_H__ */ 25 | -------------------------------------------------------------------------------- /loadbalancer/xdp/xdp-proxy.bpf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /* 10 | Assuming IP and MAC addresses are following: 11 | 12 | client => 172.17.0.4 (Hex 0x40011ac) => 02:42:ac:11:00:04 13 | loadbalancer => 172.17.0.5 (Hex 0x50011ac) => 02:42:ac:11:00:05 14 | endpoint1 => 172.17.0.2 (Hex 0x20011ac) => 02:42:ac:11:00:02 15 | endpoint2 => 172.17.0.3 (Hex 0x30011ac) => 02:42:ac:11:00:03 16 | */ 17 | #define CLIENT_IP 0x40011ac 18 | #define LOADBALANCER_IP 0x50011ac 19 | #define ENDPOINT1_IP 0x20011ac 20 | #define ENDPOINT2_IP 0x30011ac 21 | #define CLIENT_MAC_SUFFIX 0x04 22 | #define LOADBALANCER_MAC_SUFFIX 0x05 23 | #define ENDPOINT1_MAC_SUFFIX 0x02 24 | #define ENDPOINT2_MAC_SUFFIX 0x03 25 | 26 | /* Refer https://github.com/facebookincubator/katran/blob/main/katran/lib/bpf/csum_helpers.h#L30 */ 27 | static __always_inline __u16 csum_fold_helper(__u64 csum) 28 | { 29 | int i; 30 | #pragma unroll 31 | for (i = 0; i < 4; i++) 32 | { 33 | if (csum >> 16) 34 | csum = (csum & 0xffff) + (csum >> 16); 35 | } 36 | return ~csum; 37 | } 38 | 39 | static __always_inline __u16 ipv4_csum(struct iphdr *iph) 40 | { 41 | iph->check = 0; 42 | unsigned long long csum = 43 | bpf_csum_diff(0, 0, (unsigned int *)iph, sizeof(struct iphdr), 0); 44 | return csum_fold_helper(csum); 45 | } 46 | 47 | SEC("xdp") 48 | int xdp_proxy(struct xdp_md *ctx) 49 | { 50 | void *data = (void *)(long)ctx->data; 51 | void *data_end = (void *)(long)ctx->data_end; 52 | 53 | struct ethhdr *eth = data; 54 | /* abort on illegal packets */ 55 | if (data + sizeof(struct ethhdr) > data_end) 56 | { 57 | return XDP_ABORTED; 58 | } 59 | 60 | /* do nothing for non-IP packets */ 61 | if (eth->h_proto != bpf_htons(ETH_P_IP)) 62 | { 63 | return XDP_PASS; 64 | } 65 | 66 | struct iphdr *iph = data + sizeof(struct ethhdr); 67 | /* abort on illegal packets */ 68 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) 69 | { 70 | return XDP_ABORTED; 71 | } 72 | 73 | /* do nothing for non-TCP packets */ 74 | if (iph->protocol != IPPROTO_TCP) 75 | { 76 | return XDP_PASS; 77 | } 78 | 79 | if (iph->saddr == CLIENT_IP) 80 | { 81 | iph->daddr = ENDPOINT1_IP; 82 | /* Only need to update the last byte */ 83 | eth->h_dest[5] = ENDPOINT1_MAC_SUFFIX; 84 | 85 | /* simulate random selection of two endpoints */ 86 | if ((bpf_ktime_get_ns() & 0x1) == 0x1) 87 | { 88 | iph->daddr = ENDPOINT2_IP; 89 | eth->h_dest[5] = ENDPOINT2_MAC_SUFFIX; 90 | } 91 | } 92 | else 93 | { 94 | iph->daddr = CLIENT_IP; 95 | eth->h_dest[5] = CLIENT_MAC_SUFFIX; 96 | } 97 | 98 | /* packet source is always LB itself */ 99 | iph->saddr = LOADBALANCER_IP; 100 | eth->h_source[5] = LOADBALANCER_MAC_SUFFIX; 101 | 102 | /* recalculate IP checksum */ 103 | iph->check = ipv4_csum(iph); 104 | 105 | /* send packet back to network stack */ 106 | return XDP_TX; 107 | } 108 | 109 | char _license[] SEC("license") = "Dual BSD/GPL"; 110 | -------------------------------------------------------------------------------- /loadbalancer/xdp/xdp-proxy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "xdp-proxy.skel.h" 9 | 10 | /* Attach to eth0 by default */ 11 | #define DEV_NAME "eth0" 12 | 13 | int main(int argc, char **argv) 14 | { 15 | __u32 xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST | XDP_FLAGS_SKB_MODE; 16 | struct xdp_proxy_bpf *obj; 17 | int err = 0; 18 | 19 | struct rlimit rlim_new = { 20 | .rlim_cur = RLIM_INFINITY, 21 | .rlim_max = RLIM_INFINITY, 22 | }; 23 | 24 | err = setrlimit(RLIMIT_MEMLOCK, &rlim_new); 25 | if (err) 26 | { 27 | fprintf(stderr, "failed to change rlimit\n"); 28 | return 1; 29 | } 30 | 31 | unsigned int ifindex = if_nametoindex(DEV_NAME); 32 | if (ifindex == 0) 33 | { 34 | fprintf(stderr, "failed to find interface %s\n", DEV_NAME); 35 | return 1; 36 | } 37 | 38 | obj = xdp_proxy_bpf__open(); 39 | if (!obj) 40 | { 41 | fprintf(stderr, "failed to open and/or load BPF object\n"); 42 | return 1; 43 | } 44 | 45 | err = xdp_proxy_bpf__load(obj); 46 | if (err) 47 | { 48 | fprintf(stderr, "failed to load BPF object %d\n", err); 49 | goto cleanup; 50 | } 51 | 52 | /* Attach the XDP program to eth0 */ 53 | int prog_id = bpf_program__fd(obj->progs.xdp_proxy); 54 | LIBBPF_OPTS(bpf_xdp_attach_opts, attach_opts); 55 | err = bpf_xdp_attach(ifindex, prog_id, xdp_flags, &attach_opts); 56 | if (err) 57 | { 58 | fprintf(stderr, "failed to attach BPF programs\n"); 59 | goto cleanup; 60 | } 61 | 62 | printf("Successfully run! Tracing /sys/kernel/debug/tracing/trace_pipe.\n"); 63 | system("cat /sys/kernel/debug/tracing/trace_pipe"); 64 | 65 | cleanup: 66 | /* detach and free XDP program on exit */ 67 | bpf_xdp_detach(ifindex, xdp_flags, &attach_opts); 68 | xdp_proxy_bpf__destroy(obj); 69 | return err != 0; 70 | } 71 | -------------------------------------------------------------------------------- /rust/README.md: -------------------------------------------------------------------------------- 1 | # eBPF apps in Rust 2 | 3 | ## eBPF Rust libraries 4 | 5 | | Library | Summary | Starts | 6 | | ------------------------------------------------ | ------------------------------------------------------------ | ------ | 7 | | [libbpf-rs](https://github.com/libbpf/libbpf-rs) | Idiomatic rust wrapper around [libbpf](https://github.com/libbpf/libbpf). libbpf-rs, together with libbpf-cargo (libbpf cargo plugin) allows to write 'compile once run everywhere' (CO-RE) eBPF programs. | 0.3k | 8 | | [redbpf](https://github.com/redsift/redbpf) | A Rust eBPF toolchain that contains a collection of Rust libraries to work with eBPF programs. | 1k | 9 | | [Aya](https://github.com/aya-rs/aya) | An eBPF library built with a focus on operability and developer experience. It allows for both eBPF programs and their userspace programs to be written in Rust. | 0.7k | 10 | | [rust-bcc](https://github.com/rust-bpf/rust-bcc) | Idiomatic Rust bindings for the BPF compiler collection. The goal is to mimic the Python BCC bindings in https://github.com/iovisor/bcc in a Rusty way. | 0.3k | 11 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/master/Rust.gitignore 2 | 3 | # Generated by Cargo 4 | # will have compiled files and executables 5 | debug/ 6 | target/ 7 | 8 | # These are backup files generated by rustfmt 9 | **/*.rs.bk 10 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["hello-aya", "hello-aya-common", "hello-aya-ebpf"] 4 | default-members = ["hello-aya", "hello-aya-common"] 5 | 6 | [workspace.dependencies] 7 | aya = { version = "0.13.1", default-features = false } 8 | aya-build = { version = "0.1.2", default-features = false } 9 | aya-ebpf = { version = "0.1.1", default-features = false } 10 | aya-log = { version = "0.2.1", default-features = false } 11 | aya-log-ebpf = { version = "0.1.1", default-features = false } 12 | 13 | anyhow = { version = "1", default-features = false } 14 | # `std` feature is currently required to build `clap`. 15 | # 16 | # See https://github.com/clap-rs/clap/blob/61f5ee5/clap_builder/src/lib.rs#L15. 17 | clap = { version = "4.5.20", default-features = false, features = ["std"] } 18 | env_logger = { version = "0.11.5", default-features = false } 19 | libc = { version = "0.2.159", default-features = false } 20 | log = { version = "0.4.22", default-features = false } 21 | tokio = { version = "1.44.2", default-features = false } 22 | which = { version = "6.0.0", default-features = false } 23 | 24 | [profile.release.package.hello-aya-ebpf] 25 | debug = 2 26 | codegen-units = 1 27 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/README.md: -------------------------------------------------------------------------------- 1 | # hello-aya 2 | 3 | ## Prerequisites 4 | 5 | 1. stable rust toolchains: `rustup toolchain install stable` 6 | 1. nightly rust toolchains: `rustup toolchain install nightly --component rust-src` 7 | 1. (if cross-compiling) rustup target: `rustup target add ${ARCH}-unknown-linux-musl` 8 | 1. (if cross-compiling) LLVM: (e.g.) `brew install llvm` (on macOS) 9 | 1. (if cross-compiling) C toolchain: (e.g.) [`brew install filosottile/musl-cross/musl-cross`](https://github.com/FiloSottile/homebrew-musl-cross) (on macOS) 10 | 1. bpf-linker: `cargo install bpf-linker` (`--no-default-features` on macOS) 11 | 12 | ## Build & Run 13 | 14 | Use `cargo build`, `cargo check`, etc. as normal. Run your program with: 15 | 16 | ```shell 17 | cargo run --release --config 'target."cfg(all())".runner="sudo -E"' 18 | ``` 19 | 20 | Cargo build scripts are used to automatically build the eBPF correctly and include it in the 21 | program. 22 | 23 | ## Cross-compiling on macOS 24 | 25 | Cross compilation should work on both Intel and Apple Silicon Macs. 26 | 27 | ```shell 28 | CC=${ARCH}-linux-musl-gcc cargo build --package hello-aya --release \ 29 | --target=${ARCH}-unknown-linux-musl \ 30 | --config=target.${ARCH}-unknown-linux-musl.linker=\"${ARCH}-linux-musl-gcc\" 31 | ``` 32 | The cross-compiled program `target/${ARCH}-unknown-linux-musl/release/hello-aya` can be 33 | copied to a Linux server or VM and run there. 34 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/hello-aya-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-aya-common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [features] 7 | default = [] 8 | user = ["aya"] 9 | 10 | [dependencies] 11 | aya = { workspace = true, optional = true } 12 | 13 | [lib] 14 | path = "src/lib.rs" 15 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/hello-aya-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/hello-aya-ebpf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-aya-ebpf" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-aya-common = { path = "../hello-aya-common" } 8 | aya-ebpf = { workspace = true } 9 | aya-log-ebpf = { workspace = true } 10 | network-types = "0.0.7" 11 | 12 | [build-dependencies] 13 | which = { workspace = true } 14 | 15 | [[bin]] 16 | name = "hello-aya" 17 | path = "src/main.rs" 18 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/hello-aya-ebpf/build.rs: -------------------------------------------------------------------------------- 1 | use which::which; 2 | 3 | /// Building this crate has an undeclared dependency on the `bpf-linker` binary. This would be 4 | /// better expressed by [artifact-dependencies][bindeps] but issues such as 5 | /// https://github.com/rust-lang/cargo/issues/12385 make their use impractical for the time being. 6 | /// 7 | /// This file implements an imperfect solution: it causes cargo to rebuild the crate whenever the 8 | /// mtime of `which bpf-linker` changes. Note that possibility that a new bpf-linker is added to 9 | /// $PATH ahead of the one used as the cache key still exists. Solving this in the general case 10 | /// would require rebuild-if-changed-env=PATH *and* rebuild-if-changed={every-directory-in-PATH} 11 | /// which would likely mean far too much cache invalidation. 12 | /// 13 | /// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies 14 | fn main() { 15 | let bpf_linker = which("bpf-linker").unwrap(); 16 | println!("cargo:rerun-if-changed={}", bpf_linker.to_str().unwrap()); 17 | } 18 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/hello-aya-ebpf/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | // This file exists to enable the library target. 4 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/hello-aya-ebpf/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | use aya_ebpf::{bindings::xdp_action, macros::xdp, programs::XdpContext}; 5 | use aya_log_ebpf::info; 6 | 7 | use core::mem; 8 | use network_types::{ 9 | eth::{EthHdr, EtherType}, 10 | ip::{IpProto, Ipv4Hdr}, 11 | tcp::TcpHdr, 12 | udp::UdpHdr, 13 | }; 14 | 15 | #[cfg(not(test))] 16 | #[panic_handler] 17 | fn panic(_info: &core::panic::PanicInfo) -> ! { 18 | loop {} 19 | } 20 | 21 | #[xdp] 22 | pub fn hello_aya(ctx: XdpContext) -> u32 { 23 | match try_xdp_firewall(ctx) { 24 | Ok(ret) => ret, 25 | Err(_) => xdp_action::XDP_ABORTED, 26 | } 27 | } 28 | 29 | #[inline(always)] // 30 | fn ptr_at(ctx: &XdpContext, offset: usize) -> Result<*const T, ()> { 31 | let start = ctx.data(); 32 | let end = ctx.data_end(); 33 | let len = mem::size_of::(); 34 | 35 | if start + offset + len > end { 36 | return Err(()); 37 | } 38 | 39 | Ok((start + offset) as *const T) 40 | } 41 | 42 | fn try_xdp_firewall(ctx: XdpContext) -> Result { 43 | let ethhdr: *const EthHdr = ptr_at(&ctx, 0)?; // 44 | match unsafe { (*ethhdr).ether_type } { 45 | EtherType::Ipv4 => {} 46 | _ => return Ok(xdp_action::XDP_PASS), 47 | } 48 | 49 | let ipv4hdr: *const Ipv4Hdr = ptr_at(&ctx, EthHdr::LEN)?; 50 | let source_addr = u32::from_be(unsafe { (*ipv4hdr).src_addr }); 51 | let dest_addr: u32 = u32::from_be(unsafe { (*ipv4hdr).dst_addr }); 52 | 53 | let source_port = match unsafe { (*ipv4hdr).proto } { 54 | IpProto::Tcp => { 55 | let tcphdr: *const TcpHdr = 56 | ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)?; 57 | u16::from_be(unsafe { (*tcphdr).source }) 58 | } 59 | IpProto::Udp => { 60 | let udphdr: *const UdpHdr = 61 | ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)?; 62 | u16::from_be(unsafe { (*udphdr).source }) 63 | } 64 | _ => return Err(()), 65 | }; 66 | let dest_port = match unsafe { (*ipv4hdr).proto } { 67 | IpProto::Tcp => { 68 | let tcphdr: *const TcpHdr = 69 | ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)?; 70 | u16::from_be(unsafe { (*tcphdr).dest }) 71 | } 72 | IpProto::Udp => { 73 | let udphdr: *const UdpHdr = 74 | ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)?; 75 | u16::from_be(unsafe { (*udphdr).dest }) 76 | } 77 | _ => return Err(()), 78 | }; 79 | 80 | 81 | info!(&ctx, "{:i}:{} -> {:i}:{}", source_addr, source_port, dest_addr, dest_port); 82 | 83 | Ok(xdp_action::XDP_PASS) 84 | } 85 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/hello-aya/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-aya" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-aya-common = { path = "../hello-aya-common", features = ["user"] } 8 | 9 | anyhow = { workspace = true, default-features = true } 10 | aya = { workspace = true } 11 | aya-log = { workspace = true } 12 | env_logger = { workspace = true } 13 | libc = { workspace = true } 14 | log = { workspace = true } 15 | tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread", "net", "signal"] } 16 | 17 | clap = { workspace = true, features = ["derive"] } 18 | [build-dependencies] 19 | anyhow = { workspace = true } 20 | aya-build = { workspace = true } 21 | # TODO(https://github.com/rust-lang/cargo/issues/12375): this should be an artifact dependency, but 22 | # it's not possible to tell cargo to use `-Z build-std` to build it. We cargo-in-cargo in the build 23 | # script to build this, but we want to teach cargo about the dependecy so that cache invalidation 24 | # works properly. 25 | # 26 | # Note also that https://github.com/rust-lang/cargo/issues/10593 occurs when `target = ...` is added 27 | # to an artifact dependency; it seems possible to work around that by setting `resolver = "1"` in 28 | # Cargo.toml in the workspace root. 29 | # 30 | # Finally note that *any* usage of `artifact = ...` in *any* Cargo.toml in the workspace breaks 31 | # workflows with stable cargo; stable cargo outright refuses to load manifests that use unstable 32 | # features. 33 | hello-aya-ebpf = { path = "../hello-aya-ebpf" } 34 | 35 | [[bin]] 36 | name = "hello-aya" 37 | path = "src/main.rs" 38 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/hello-aya/build.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Context as _}; 2 | use aya_build::cargo_metadata; 3 | 4 | fn main() -> anyhow::Result<()> { 5 | let cargo_metadata::Metadata { packages, .. } = cargo_metadata::MetadataCommand::new() 6 | .no_deps() 7 | .exec() 8 | .context("MetadataCommand::exec")?; 9 | let ebpf_package = packages 10 | .into_iter() 11 | .find(|cargo_metadata::Package { name, .. }| name == "hello-aya-ebpf") 12 | .ok_or_else(|| anyhow!("hello-aya-ebpf package not found"))?; 13 | aya_build::build_ebpf([ebpf_package]) 14 | } 15 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/hello-aya/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Context as _; 2 | use aya::programs::{Xdp, XdpFlags}; 3 | use clap::Parser; 4 | #[rustfmt::skip] 5 | use log::{debug, warn}; 6 | use tokio::signal; 7 | 8 | #[derive(Debug, Parser)] 9 | struct Opt { 10 | #[clap(short, long, default_value = "eth0")] 11 | iface: String, 12 | } 13 | 14 | #[tokio::main] 15 | async fn main() -> anyhow::Result<()> { 16 | let opt = Opt::parse(); 17 | 18 | env_logger::init(); 19 | 20 | // Bump the memlock rlimit. This is needed for older kernels that don't use the 21 | // new memcg based accounting, see https://lwn.net/Articles/837122/ 22 | let rlim = libc::rlimit { 23 | rlim_cur: libc::RLIM_INFINITY, 24 | rlim_max: libc::RLIM_INFINITY, 25 | }; 26 | let ret = unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; 27 | if ret != 0 { 28 | debug!("remove limit on locked memory failed, ret is: {}", ret); 29 | } 30 | 31 | // This will include your eBPF object file as raw bytes at compile-time and load it at 32 | // runtime. This approach is recommended for most real-world use cases. If you would 33 | // like to specify the eBPF program at runtime rather than at compile-time, you can 34 | // reach for `Bpf::load_file` instead. 35 | let mut ebpf = aya::Ebpf::load(aya::include_bytes_aligned!(concat!( 36 | env!("OUT_DIR"), 37 | "/hello-aya" 38 | )))?; 39 | if let Err(e) = aya_log::EbpfLogger::init(&mut ebpf) { 40 | // This can happen if you remove all log statements from your eBPF program. 41 | warn!("failed to initialize eBPF logger: {}", e); 42 | } 43 | let Opt { iface } = opt; 44 | let program: &mut Xdp = ebpf.program_mut("hello_aya").unwrap().try_into()?; 45 | program.load()?; 46 | program.attach(&iface, XdpFlags::default()) 47 | .context("failed to attach the XDP program with default flags - try changing XdpFlags::default() to XdpFlags::SKB_MODE")?; 48 | 49 | let ctrl_c = signal::ctrl_c(); 50 | println!("Waiting for Ctrl-C..."); 51 | ctrl_c.await?; 52 | println!("Exiting..."); 53 | 54 | Ok(()) 55 | } 56 | -------------------------------------------------------------------------------- /rust/hello-aya-xdp/rustfmt.toml: -------------------------------------------------------------------------------- 1 | group_imports = "StdExternalCrate" 2 | imports_granularity = "Crate" 3 | reorder_imports = true 4 | unstable_features = true 5 | -------------------------------------------------------------------------------- /rust/hello-aya/.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/master/Rust.gitignore 2 | 3 | # Generated by Cargo 4 | # will have compiled files and executables 5 | debug/ 6 | target/ 7 | 8 | # These are backup files generated by rustfmt 9 | **/*.rs.bk 10 | -------------------------------------------------------------------------------- /rust/hello-aya/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["hello-aya", "hello-aya-common", "hello-aya-ebpf"] 4 | default-members = ["hello-aya", "hello-aya-common"] 5 | 6 | [workspace.dependencies] 7 | aya = { version = "0.13.1", default-features = false } 8 | aya-build = { version = "0.1.2", default-features = false } 9 | aya-ebpf = { version = "0.1.1", default-features = false } 10 | aya-log = { version = "0.2.1", default-features = false } 11 | aya-log-ebpf = { version = "0.1.1", default-features = false } 12 | 13 | anyhow = { version = "1", default-features = false } 14 | # `std` feature is currently required to build `clap`. 15 | # 16 | # See https://github.com/clap-rs/clap/blob/61f5ee5/clap_builder/src/lib.rs#L15. 17 | clap = { version = "4.5.20", default-features = false, features = ["std"] } 18 | env_logger = { version = "0.11.5", default-features = false } 19 | libc = { version = "0.2.159", default-features = false } 20 | log = { version = "0.4.22", default-features = false } 21 | tokio = { version = "1.44.2", default-features = false } 22 | which = { version = "6.0.0", default-features = false } 23 | 24 | [profile.release.package.hello-aya-ebpf] 25 | debug = 2 26 | codegen-units = 1 27 | -------------------------------------------------------------------------------- /rust/hello-aya/README.md: -------------------------------------------------------------------------------- 1 | # hello-aya 2 | 3 | ## Prerequisites 4 | 5 | 1. stable rust toolchains: `rustup toolchain install stable` 6 | 1. nightly rust toolchains: `rustup toolchain install nightly --component rust-src` 7 | 1. (if cross-compiling) rustup target: `rustup target add ${ARCH}-unknown-linux-musl` 8 | 1. (if cross-compiling) LLVM: (e.g.) `brew install llvm` (on macOS) 9 | 1. (if cross-compiling) C toolchain: (e.g.) [`brew install filosottile/musl-cross/musl-cross`](https://github.com/FiloSottile/homebrew-musl-cross) (on macOS) 10 | 1. bpf-linker: `cargo install bpf-linker` (`--no-default-features` on macOS) 11 | 12 | ## Build & Run 13 | 14 | Use `cargo build`, `cargo check`, etc. as normal. Run your program with: 15 | 16 | ```shell 17 | cargo run --release --config 'target."cfg(all())".runner="sudo -E"' 18 | ``` 19 | 20 | Cargo build scripts are used to automatically build the eBPF correctly and include it in the 21 | program. 22 | 23 | ## Cross-compiling on macOS 24 | 25 | Cross compilation should work on both Intel and Apple Silicon Macs. 26 | 27 | ```shell 28 | CC=${ARCH}-linux-musl-gcc cargo build --package hello-aya --release \ 29 | --target=${ARCH}-unknown-linux-musl \ 30 | --config=target.${ARCH}-unknown-linux-musl.linker=\"${ARCH}-linux-musl-gcc\" 31 | ``` 32 | The cross-compiled program `target/${ARCH}-unknown-linux-musl/release/hello-aya` can be 33 | copied to a Linux server or VM and run there. 34 | -------------------------------------------------------------------------------- /rust/hello-aya/hello-aya-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-aya-common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [features] 7 | default = [] 8 | user = ["aya"] 9 | 10 | [dependencies] 11 | aya = { workspace = true, optional = true } 12 | 13 | [lib] 14 | path = "src/lib.rs" 15 | -------------------------------------------------------------------------------- /rust/hello-aya/hello-aya-common/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | -------------------------------------------------------------------------------- /rust/hello-aya/hello-aya-ebpf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-aya-ebpf" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-aya-common = { path = "../hello-aya-common" } 8 | 9 | aya-ebpf = { workspace = true } 10 | aya-log-ebpf = { workspace = true } 11 | network-types = "0.0.7" 12 | 13 | [build-dependencies] 14 | which = { workspace = true } 15 | 16 | [[bin]] 17 | name = "hello-aya" 18 | path = "src/main.rs" 19 | -------------------------------------------------------------------------------- /rust/hello-aya/hello-aya-ebpf/build.rs: -------------------------------------------------------------------------------- 1 | use which::which; 2 | 3 | /// Building this crate has an undeclared dependency on the `bpf-linker` binary. This would be 4 | /// better expressed by [artifact-dependencies][bindeps] but issues such as 5 | /// https://github.com/rust-lang/cargo/issues/12385 make their use impractical for the time being. 6 | /// 7 | /// This file implements an imperfect solution: it causes cargo to rebuild the crate whenever the 8 | /// mtime of `which bpf-linker` changes. Note that possibility that a new bpf-linker is added to 9 | /// $PATH ahead of the one used as the cache key still exists. Solving this in the general case 10 | /// would require rebuild-if-changed-env=PATH *and* rebuild-if-changed={every-directory-in-PATH} 11 | /// which would likely mean far too much cache invalidation. 12 | /// 13 | /// [bindeps]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html?highlight=feature#artifact-dependencies 14 | fn main() { 15 | let bpf_linker = which("bpf-linker").unwrap(); 16 | println!("cargo:rerun-if-changed={}", bpf_linker.to_str().unwrap()); 17 | } 18 | -------------------------------------------------------------------------------- /rust/hello-aya/hello-aya-ebpf/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | // This file exists to enable the library target. 4 | -------------------------------------------------------------------------------- /rust/hello-aya/hello-aya-ebpf/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 不使用标准库和main函数 */ 2 | #![no_std] 3 | #![no_main] 4 | 5 | /* 导入依赖库 */ 6 | use aya_ebpf::{ 7 | bindings::{TC_ACT_UNSPEC, TC_ACT_SHOT}, 8 | macros::{classifier, map}, 9 | maps::Array, 10 | programs::TcContext, 11 | }; 12 | use aya_log_ebpf::info; 13 | use core::mem; 14 | use network_types::{ 15 | eth::{EthHdr, EtherType}, 16 | ip::{IpProto, Ipv4Hdr, Ipv6Hdr}, 17 | tcp::TcpHdr, 18 | udp::UdpHdr, 19 | }; 20 | 21 | /* 定义BPF映射,用于用户空间配置允许的端口号 */ 22 | #[map] 23 | static ALLOW_PORTS: Array = 24 | Array::::with_max_entries(10, 0); 25 | 26 | 27 | /* TC程序主处理函数 */ 28 | #[classifier] 29 | pub fn hello_aya(ctx: TcContext) -> i32 { 30 | match try_hello_aya(ctx) { 31 | Ok(ret) => ret, 32 | Err(_) => TC_ACT_UNSPEC, 33 | } 34 | } 35 | 36 | /* 网络数据头定位函数 */ 37 | #[inline(always)] 38 | fn ptr_at(ctx: &TcContext, offset: usize) -> Result<*const T, ()> { 39 | let start = ctx.data(); 40 | let end = ctx.data_end(); 41 | let len = mem::size_of::(); 42 | 43 | if start + offset + len > end { 44 | return Err(()); 45 | } 46 | 47 | Ok((start + offset) as *const T) 48 | } 49 | 50 | /* TC处理函数 */ 51 | fn try_hello_aya(ctx: TcContext) -> Result { 52 | let mut rc: i32 = TC_ACT_SHOT; 53 | let mut tcphdr: *const TcpHdr = core::ptr::null(); 54 | let mut udphdr: *const UdpHdr = core::ptr::null(); 55 | let proto: IpProto; 56 | 57 | /* 获取TCP/UDP头 */ 58 | let ethhdr: *const EthHdr = ptr_at(&ctx, 0)?; 59 | match unsafe { (*ethhdr).ether_type } { 60 | EtherType::Ipv4 => { 61 | let ipv4hdr: *const Ipv4Hdr = ptr_at(&ctx, EthHdr::LEN)?; 62 | proto = unsafe { (*ipv4hdr).proto }; 63 | match proto { 64 | IpProto::Tcp => { 65 | tcphdr = ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)?; 66 | } 67 | IpProto::Udp => { 68 | udphdr = ptr_at(&ctx, EthHdr::LEN + Ipv4Hdr::LEN)?; 69 | } 70 | _ => return Ok(TC_ACT_SHOT), 71 | }; 72 | } 73 | EtherType::Ipv6 => { 74 | let ipv6hdr: *const Ipv6Hdr = ptr_at(&ctx, EthHdr::LEN)?; 75 | proto = unsafe { (*ipv6hdr).next_hdr }; 76 | match proto { 77 | IpProto::Tcp => { 78 | tcphdr = ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN)?; 79 | } 80 | IpProto::Udp => { 81 | udphdr = ptr_at(&ctx, EthHdr::LEN + Ipv6Hdr::LEN)?; 82 | } 83 | _ => return Ok(TC_ACT_SHOT), 84 | }; 85 | } 86 | _ => return Ok(TC_ACT_SHOT), 87 | } 88 | 89 | /* 获取源端口号和目的端口号 */ 90 | let source_port: u16; 91 | let dest_port: u16; 92 | match proto { 93 | IpProto::Tcp => { 94 | source_port = u16::from_be(unsafe { (*tcphdr).source }); 95 | dest_port = u16::from_be(unsafe { (*tcphdr).dest }); 96 | } 97 | IpProto::Udp => { 98 | source_port = u16::from_be(unsafe { (*udphdr).source }); 99 | dest_port = u16::from_be(unsafe { (*udphdr).dest }); 100 | } 101 | _ => return Ok(TC_ACT_UNSPEC), 102 | } 103 | 104 | /* 检查源端口或目的端口是否被允许 */ 105 | for i in 0..10 { 106 | let port = ALLOW_PORTS.get(i).unwrap_or(&0); 107 | if *port == source_port as u32 || *port == dest_port as u32 { 108 | rc = TC_ACT_UNSPEC; 109 | break; 110 | } 111 | } 112 | 113 | /* 打印日志 */ 114 | info!(&ctx, "Packet {} -> {}", source_port, dest_port); 115 | Ok(rc) 116 | } 117 | 118 | /* 实际上用不到,但是Rust编译必须的 */ 119 | #[cfg(not(test))] 120 | #[panic_handler] 121 | fn panic(_info: &core::panic::PanicInfo) -> ! { 122 | unsafe { core::hint::unreachable_unchecked() } 123 | } 124 | -------------------------------------------------------------------------------- /rust/hello-aya/hello-aya/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-aya" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | hello-aya-common = { path = "../hello-aya-common", features = ["user"] } 8 | 9 | anyhow = { workspace = true, default-features = true } 10 | aya = { workspace = true } 11 | aya-log = { workspace = true } 12 | env_logger = { workspace = true } 13 | libc = { workspace = true } 14 | log = { workspace = true } 15 | tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread", "net", "signal"] } 16 | 17 | clap = { workspace = true, features = ["derive"] } 18 | [build-dependencies] 19 | anyhow = { workspace = true } 20 | aya-build = { workspace = true } 21 | # TODO(https://github.com/rust-lang/cargo/issues/12375): this should be an artifact dependency, but 22 | # it's not possible to tell cargo to use `-Z build-std` to build it. We cargo-in-cargo in the build 23 | # script to build this, but we want to teach cargo about the dependecy so that cache invalidation 24 | # works properly. 25 | # 26 | # Note also that https://github.com/rust-lang/cargo/issues/10593 occurs when `target = ...` is added 27 | # to an artifact dependency; it seems possible to work around that by setting `resolver = "1"` in 28 | # Cargo.toml in the workspace root. 29 | # 30 | # Finally note that *any* usage of `artifact = ...` in *any* Cargo.toml in the workspace breaks 31 | # workflows with stable cargo; stable cargo outright refuses to load manifests that use unstable 32 | # features. 33 | hello-aya-ebpf = { path = "../hello-aya-ebpf" } 34 | 35 | [[bin]] 36 | name = "hello-aya" 37 | path = "src/main.rs" 38 | -------------------------------------------------------------------------------- /rust/hello-aya/hello-aya/build.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Context as _}; 2 | use aya_build::cargo_metadata; 3 | 4 | fn main() -> anyhow::Result<()> { 5 | let cargo_metadata::Metadata { packages, .. } = cargo_metadata::MetadataCommand::new() 6 | .no_deps() 7 | .exec() 8 | .context("MetadataCommand::exec")?; 9 | let ebpf_package = packages 10 | .into_iter() 11 | .find(|cargo_metadata::Package { name, .. }| name == "hello-aya-ebpf") 12 | .ok_or_else(|| anyhow!("hello-aya-ebpf package not found"))?; 13 | aya_build::build_ebpf([ebpf_package]) 14 | } 15 | -------------------------------------------------------------------------------- /rust/hello-aya/hello-aya/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 导入依赖库 */ 2 | use aya::maps::Array; 3 | use aya::programs::{tc, SchedClassifier, TcAttachType}; 4 | use clap::Parser; 5 | #[rustfmt::skip] 6 | use log::{debug, warn}; 7 | use tokio::signal; 8 | 9 | /* 定义命令行参数 */ 10 | #[derive(Debug, Parser)] 11 | struct Command { 12 | /// list of ports to whitelist 13 | #[arg(short = 'p', long = "ports", default_value = "22,443,53", value_delimiter = ',')] 14 | ports: Vec, 15 | 16 | /// interface to attach to 17 | #[arg(short = 'i', long = "iface", default_value = "eth0")] 18 | iface: String, 19 | } 20 | 21 | #[tokio::main] 22 | async fn main() -> anyhow::Result<()> { 23 | /* 命令行参数解析 */ 24 | let opts = Command::parse(); 25 | env_logger::init(); 26 | 27 | /* 提高 memlock rlimit */ 28 | let rlim = libc::rlimit { 29 | rlim_cur: libc::RLIM_INFINITY, 30 | rlim_max: libc::RLIM_INFINITY, 31 | }; 32 | let ret: i32 = unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlim) }; 33 | if ret != 0 { 34 | debug!("remove limit on locked memory failed, ret is: {}", ret); 35 | } 36 | 37 | /* 加载eBPF程序 */ 38 | let mut ebpf = aya::Ebpf::load(aya::include_bytes_aligned!(concat!( 39 | env!("OUT_DIR"), 40 | "/hello-aya" 41 | )))?; 42 | if let Err(e) = aya_log::EbpfLogger::init(&mut ebpf) { 43 | /* 这里说明eBPF程序不包含任何日志语句 */ 44 | warn!("failed to initialize eBPF logger: {}", e); 45 | } 46 | 47 | /* 根据命令行参数更新BPF映射中的端口号 */ 48 | let mut port_map: Array<_, _> = Array::try_from(ebpf.map_mut("ALLOW_PORTS").unwrap())?; 49 | for (i, port) in opts.ports.iter().enumerate() { 50 | let key = i as u32; 51 | let val = *port as u32; 52 | port_map.set(key, val, 0)?; 53 | } 54 | 55 | /* 挂载TC程序(程序退出时会自动卸载) */ 56 | let program: &mut SchedClassifier = ebpf.program_mut("hello_aya").unwrap().try_into()?; 57 | program.load()?; 58 | let _ = tc::qdisc_add_clsact(&opts.iface); 59 | program.attach(&opts.iface, TcAttachType::Egress)?; 60 | program.attach(&opts.iface, TcAttachType::Ingress)?; 61 | 62 | let ctrl_c = signal::ctrl_c(); 63 | println!("Waiting for Ctrl-C..."); 64 | ctrl_c.await?; 65 | println!("Exiting..."); 66 | 67 | Ok(()) 68 | } 69 | -------------------------------------------------------------------------------- /rust/hello-aya/rustfmt.toml: -------------------------------------------------------------------------------- 1 | group_imports = "StdExternalCrate" 2 | imports_granularity = "Crate" 3 | reorder_imports = true 4 | unstable_features = true 5 | -------------------------------------------------------------------------------- /rust/hello-libbpf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-libbpf" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [build-dependencies] 7 | libbpf-cargo = "0.24.8" 8 | vmlinux = { git = "https://github.com/libbpf/vmlinux.h", version = "0.0.0" } 9 | 10 | [dependencies] 11 | anyhow = "1.0.97" 12 | libbpf-rs = {version="0.24.8"} 13 | clap = { version = "4.5.32", default-features = false, features = ["std", "derive", "help", "usage"] } 14 | nix = { version = "0.29.0", default-features = false, features = ["net", "user"] } 15 | -------------------------------------------------------------------------------- /rust/hello-libbpf/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::ffi::OsStr; 3 | use std::path::PathBuf; 4 | 5 | use libbpf_cargo::SkeletonBuilder; 6 | 7 | const SRC: &str = "src/bpf/tc.bpf.c"; 8 | 9 | fn main() { 10 | let out = PathBuf::from( 11 | env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set in build script"), 12 | ) 13 | .join("src") 14 | .join("bpf") 15 | .join("tc.skel.rs"); 16 | 17 | let arch = env::var("CARGO_CFG_TARGET_ARCH") 18 | .expect("CARGO_CFG_TARGET_ARCH must be set in build script"); 19 | 20 | SkeletonBuilder::new() 21 | .source(SRC) 22 | .clang_args([ 23 | OsStr::new("-I"), 24 | vmlinux::include_path_root().join(arch).as_os_str(), 25 | ]) 26 | .build_and_generate(&out) 27 | .unwrap(); 28 | println!("cargo:rerun-if-changed={SRC}"); 29 | } -------------------------------------------------------------------------------- /rust/hello-libbpf/src/bpf/tc.bpf.c: -------------------------------------------------------------------------------- 1 | /* 导入头文件 */ 2 | #include "vmlinux.h" 3 | #include 4 | #include 5 | #include 6 | 7 | /* 常量定义 */ 8 | #define ETH_P_IP 0x0800 9 | #define ETH_P_IPV6 0x86DD 10 | #define TC_ACT_UNSPEC (-1) 11 | #define TC_ACT_SHOT 2 12 | 13 | u8 rc_allow = TC_ACT_UNSPEC; 14 | u8 rc_disallow = TC_ACT_SHOT; 15 | 16 | /* 定义BPF映射,用于用户空间配置允许的端口号 */ 17 | struct { 18 | __uint(type, BPF_MAP_TYPE_ARRAY); 19 | __uint(max_entries, 10); 20 | __type(value, u16); 21 | __type(key, u32); 22 | } allow_ports SEC(".maps"); 23 | 24 | /* 如果端口号在允许的端口号列表中,则允许该端口 */ 25 | static bool allow_port(__be16 port) 26 | { 27 | u16 hport = bpf_ntohs(port); 28 | u32 i = 0; 29 | for (i = 0; i < 10; i++) { 30 | u32 key = i; 31 | u16 *allow_port = bpf_map_lookup_elem(&allow_ports, &key); 32 | if (allow_port && hport == *allow_port) { 33 | return true; 34 | } 35 | } 36 | 37 | return false; 38 | } 39 | 40 | /* TC 程序主函数 */ 41 | SEC("tc") 42 | int handle_tc(struct __sk_buff *skb) 43 | { 44 | /* 默认不允许 */ 45 | int rc = rc_disallow; 46 | 47 | /* 定义变量 */ 48 | __be16 dst = 0; 49 | __be16 src = 0; 50 | __be16 port = 0; 51 | __u8 proto = 0; 52 | 53 | /* 检查数据包是否完整 */ 54 | void *data_end = (void*)(long)skb->data_end; 55 | struct ethhdr *eth = (struct ethhdr*)(void*)(long)skb->data; 56 | void *trans_data; 57 | if (eth + 1 > data_end) { 58 | return TC_ACT_UNSPEC; 59 | } 60 | 61 | if (eth->h_proto == bpf_htons(ETH_P_IP)) { // ipv4 62 | struct iphdr *iph = (struct iphdr *)((void*)eth + sizeof(*eth)); 63 | if ((void*)(iph + 1) > data_end) { 64 | return TC_ACT_SHOT; 65 | } 66 | 67 | proto = iph->protocol; 68 | trans_data = (void*)iph + (iph->ihl * 4); 69 | } else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) { // ipv6 70 | struct ipv6hdr *ip6h = (struct ipv6hdr *)((void*)eth + sizeof(*eth)); 71 | if ((void*)(ip6h + 1) > data_end) { 72 | return TC_ACT_SHOT; 73 | } 74 | 75 | proto = ip6h->nexthdr; 76 | trans_data = ip6h + 1; 77 | } 78 | 79 | /* 获取TCP/UDP源端口和目的端口*/ 80 | if (proto == IPPROTO_TCP) { 81 | struct tcphdr *tcph = (struct tcphdr *)trans_data; 82 | 83 | if ((void*)(trans_data + sizeof(*tcph)) > data_end) { 84 | return TC_ACT_SHOT; 85 | } 86 | 87 | dst = tcph->dest; 88 | src = tcph->source; 89 | } else if (proto == IPPROTO_UDP) { 90 | struct udphdr *udph = (struct udphdr *)trans_data; 91 | if ((void*)(trans_data + sizeof(*udph)) > data_end) { 92 | return TC_ACT_SHOT; 93 | } 94 | 95 | dst = udph->dest; 96 | src = udph->source; 97 | } else { 98 | goto found_unknown; 99 | } 100 | 101 | /* 检查源端口或目的端口是否被允许 */ 102 | if (allow_port(src) || allow_port(dst)) { 103 | rc = rc_allow; 104 | } 105 | 106 | /* 打印日志 */ 107 | if (skb->ingress_ifindex) { 108 | bpf_printk("b ingress on -- src %d dst %d", 109 | bpf_ntohs(src), bpf_ntohs(dst)); 110 | } else { 111 | bpf_printk("b egress on -- src %d dst %d", 112 | bpf_ntohs(src), bpf_ntohs(dst)); 113 | } 114 | 115 | return rc; 116 | 117 | found_unknown: 118 | rc = TC_ACT_UNSPEC; 119 | return rc; 120 | } 121 | 122 | /* 定义许可证 */ 123 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /rust/hello-libbpf/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::let_unit_value)] 2 | 3 | /* 导入依赖库 */ 4 | use std::mem::MaybeUninit; 5 | use std::os::unix::io::AsFd as _; 6 | 7 | use anyhow::Context as _; 8 | use anyhow::Result; 9 | 10 | use clap::Parser; 11 | 12 | use libbpf_rs::skel::OpenSkel; 13 | use libbpf_rs::skel::SkelBuilder; 14 | use libbpf_rs::MapCore as _; 15 | use libbpf_rs::MapFlags; 16 | use libbpf_rs::TcHookBuilder; 17 | use libbpf_rs::TC_EGRESS; 18 | use libbpf_rs::TC_INGRESS; 19 | 20 | use nix::net::if_::if_nametoindex; 21 | 22 | /* 导入脚手架框架 */ 23 | mod tc { 24 | include!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/bpf/tc.skel.rs")); 25 | } 26 | use tc::*; 27 | 28 | /* 定义命令行参数 */ 29 | #[derive(Debug, Parser)] 30 | struct Command { 31 | /// list of ports to whitelist 32 | #[arg(short, long)] 33 | ports: Vec, 34 | 35 | /// attach a hook 36 | #[arg(short, long)] 37 | attach: bool, 38 | 39 | /// detach existing hook 40 | #[arg(short, long)] 41 | detach: bool, 42 | 43 | /// interface to attach to 44 | #[arg(short = 'i', long = "interface")] 45 | iface: String, 46 | } 47 | 48 | fn main() -> Result<()> { 49 | let opts = Command::parse(); 50 | 51 | /* 变量定义 */ 52 | let builder = TcSkelBuilder::default(); 53 | let mut open_object = MaybeUninit::uninit(); 54 | let open = builder.open(&mut open_object)?; 55 | let skel = open.load()?; 56 | let ifidx = if_nametoindex(opts.iface.as_str())? as i32; 57 | 58 | /* TC钩子定义 */ 59 | let mut tc_builder = TcHookBuilder::new(skel.progs.handle_tc.as_fd()); 60 | tc_builder 61 | .ifindex(ifidx) 62 | .replace(true) 63 | .handle(1) 64 | .priority(1); 65 | 66 | let mut egress = tc_builder.hook(TC_EGRESS); 67 | let mut ingress = tc_builder.hook(TC_INGRESS); 68 | 69 | /* 卸载TC程序 */ 70 | if opts.detach { 71 | if let Err(e) = ingress.detach() { 72 | println!("failed to detach ingress hook {e}"); 73 | } 74 | if let Err(e) = egress.detach() { 75 | println!("failed to detach egress hook {e}"); 76 | } 77 | } 78 | 79 | /* 挂载TC程序 */ 80 | if opts.attach { 81 | /* 更新BPF映射中的端口号 */ 82 | for (i, port) in opts.ports.iter().enumerate() { 83 | let key = (i as u32).to_ne_bytes(); 84 | let val = port.to_ne_bytes(); 85 | let () = skel 86 | .maps 87 | .allow_ports 88 | .update(&key, &val, MapFlags::ANY) 89 | .context("Example limited to 10 ports")?; 90 | } 91 | 92 | if let Err(e) = ingress.attach() { 93 | println!("failed to attach ingress hook {e}"); 94 | } 95 | 96 | if let Err(e) = egress.attach() { 97 | println!("failed to attach egress hook {e}"); 98 | } 99 | } 100 | 101 | Ok(()) 102 | } -------------------------------------------------------------------------------- /rust/tc_block_tcp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tc_block_tcp" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0" 8 | libbpf-rs = "0.18" 9 | libc = "0.2" 10 | plain = "0.2" 11 | nix = { version = "0.24", default-features = false, features = ["net", "user"] } 12 | clap = { version = "3.1", default-features = false, features = ["std", "derive"] } 13 | -------------------------------------------------------------------------------- /rust/tc_block_tcp/README.md: -------------------------------------------------------------------------------- 1 | # Dropping packets with TC 2 | 3 | This is a simple example of how to use the Traffic Control (TC) tool to drop packets. 4 | 5 | ## Prerequisites 6 | 7 | ```sh 8 | cargo install libbpf-cargo 9 | ``` 10 | 11 | ## Build 12 | 13 | ```sh 14 | cargo libbpf make 15 | ``` 16 | 17 | ## Load 18 | 19 | ```sh 20 | ./target/debug/tc_block_tcp --interface eth0 --attach 21 | ``` 22 | 23 | ## Unload 24 | 25 | ```sh 26 | ./target/debug/tc_block_tcp --interface eth0 --detach 27 | ``` 28 | -------------------------------------------------------------------------------- /rust/tc_block_tcp/src/bpf/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)" 2 | // 3 | // THIS FILE IS AUTOGENERATED BY CARGO-LIBBPF-GEN! 4 | 5 | #[path = "tc.skel.rs"] 6 | mod tc_skel; 7 | 8 | pub use tc_skel::*; 9 | -------------------------------------------------------------------------------- /rust/tc_block_tcp/src/bpf/tc.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | #include 5 | 6 | //#include 7 | #define ETH_P_IP 0x0800 8 | 9 | //#include 10 | #define TC_ACT_OK 0 11 | #define TC_ACT_SHOT 2 12 | 13 | struct 14 | { 15 | __uint(type, BPF_MAP_TYPE_ARRAY); 16 | __uint(max_entries, 10); 17 | __type(value, u16); 18 | __type(key, u32); 19 | } ports SEC(".maps"); 20 | 21 | static bool drop_port(__be16 port) 22 | { 23 | u16 hport = bpf_ntohs(port); 24 | u32 i = 0; 25 | for (i = 0; i < 10; i++) 26 | { 27 | u32 key = i; 28 | u16 *drop_port = bpf_map_lookup_elem(&ports, &key); 29 | if (drop_port && hport == *drop_port) 30 | { 31 | return true; 32 | } 33 | } 34 | 35 | return false; 36 | } 37 | 38 | SEC("tc") 39 | int handle_tc(struct __sk_buff *skb) 40 | { 41 | void *data = (void *)(long)skb->data; 42 | void *data_end = (void *)(long)skb->data_end; 43 | 44 | struct ethhdr *eth = data; 45 | if (data + sizeof(struct ethhdr) > data_end) { 46 | return TC_ACT_OK; /* continue the kernel network stack */ 47 | } 48 | 49 | if (eth->h_proto != bpf_htons(ETH_P_IP)) { 50 | return TC_ACT_OK; 51 | } 52 | 53 | struct iphdr *iph = data + sizeof(struct ethhdr); 54 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) { 55 | return TC_ACT_OK; 56 | } 57 | 58 | if (iph->protocol != IPPROTO_TCP) { 59 | return TC_ACT_OK; 60 | } 61 | //bpf_printk("Tracing TCP packet %ld => %ld\n", iph->saddr, iph->daddr); 62 | 63 | struct tcphdr *tcp = 64 | (data + sizeof(struct ethhdr) + sizeof(struct iphdr)); 65 | if (tcp + 1 > (struct tcphdr *)data_end) { 66 | return TC_ACT_OK; 67 | } 68 | 69 | // bpf_printk("Got TCP connection with ports: %d -> %d", bpf_htons(tcp->source), bpf_htons(tcp->dest)); 70 | 71 | /* drop tcp packets */ 72 | if (drop_port(tcp->dest) || drop_port(tcp->source)) 73 | { 74 | if (skb->ingress_ifindex) { 75 | bpf_printk("Dropping ingress packets to %ld:%d\n", iph->daddr, 76 | bpf_htons(tcp->dest)); 77 | } else { 78 | bpf_printk("Dropping egress packets to %ld:%d\n", iph->daddr, 79 | bpf_htons(tcp->dest)); 80 | } 81 | 82 | return TC_ACT_SHOT; 83 | } 84 | 85 | return TC_ACT_OK; 86 | } 87 | 88 | char _license[] SEC("license") = "Dual BSD/GPL"; 89 | -------------------------------------------------------------------------------- /rust/xdppass/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xdppass" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0" 8 | libbpf-rs = "0.18" 9 | libc = "0.2" 10 | plain = "0.2" 11 | nix = { version = "0.24", default-features = false, features = ["net", "user"] } 12 | clap = { version = "3.1", default-features = false, features = ["std", "derive"] } 13 | -------------------------------------------------------------------------------- /rust/xdppass/README.md: -------------------------------------------------------------------------------- 1 | # Dropping packets with XDP 2 | 3 | This is a simple example of how to use the XDP. 4 | 5 | ## Prerequisites 6 | 7 | ```sh 8 | cargo install libbpf-cargo 9 | ``` 10 | 11 | ## Build 12 | 13 | ```sh 14 | cargo libbpf make 15 | ``` 16 | -------------------------------------------------------------------------------- /rust/xdppass/src/bpf/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)" 2 | // 3 | // THIS FILE IS AUTOGENERATED BY CARGO-LIBBPF-GEN! 4 | 5 | #[path = "xdppass.skel.rs"] 6 | mod xdppass_skel; 7 | 8 | pub use xdppass_skel::*; 9 | -------------------------------------------------------------------------------- /rust/xdppass/src/bpf/xdppass.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | #include 3 | #include 4 | 5 | SEC("xdp") 6 | int xdp_prog_simple(struct xdp_md *ctx) 7 | { 8 | void *data = (void *)(long)ctx->data; 9 | void *data_end = (void *)(long)ctx->data_end; 10 | int pkt_sz = data_end - data; 11 | 12 | bpf_printk("packet size: %d", pkt_sz); 13 | return XDP_PASS; 14 | } 15 | 16 | char _license[] SEC("license") = "Dual BSD/GPL"; 17 | -------------------------------------------------------------------------------- /rust/xdppass/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::{prelude::*, BufReader}; 3 | use anyhow::{bail, Result}; 4 | use clap::Parser; 5 | 6 | mod bpf; 7 | use bpf::*; 8 | 9 | const TRACING_PIPE: &str = "/sys/kernel/debug/tracing/trace_pipe"; 10 | 11 | #[derive(Debug, Parser)] 12 | struct Command { 13 | /// interface to attach to 14 | #[clap(short = 'i', long = "interface", default_value = "eth0")] 15 | iface: String, 16 | } 17 | 18 | fn bump_memlock_rlimit() -> Result<()> { 19 | let rlimit = libc::rlimit { 20 | rlim_cur: 128 << 20, 21 | rlim_max: 128 << 20, 22 | }; 23 | 24 | if unsafe { libc::setrlimit(libc::RLIMIT_MEMLOCK, &rlimit) } != 0 { 25 | bail!("Failed to increase rlimit"); 26 | } 27 | 28 | Ok(()) 29 | } 30 | fn main() -> Result<()> { 31 | let opts = Command::parse(); 32 | 33 | bump_memlock_rlimit()?; 34 | 35 | let builder = XdppassSkelBuilder::default(); 36 | let open = builder.open()?; 37 | let mut skel = open.load()?; 38 | let ifidx = nix::net::if_::if_nametoindex(opts.iface.as_str())? as i32; 39 | let link = skel.progs_mut().xdp_prog_simple().attach_xdp(ifidx)?; 40 | skel.links = XdppassLinks { 41 | xdp_prog_simple: Some(link), 42 | }; 43 | 44 | let file = File::open(TRACING_PIPE).unwrap(); 45 | let mut reader = BufReader::new(file); 46 | let mut line = String::new(); 47 | 48 | loop { 49 | match reader.read_line(&mut line) { 50 | Ok(read) => { 51 | if read == 0 { 52 | break; 53 | } 54 | println!("{}", line); 55 | } 56 | Err(err) => { 57 | println!("error reading {}: {}", TRACING_PIPE, err); 58 | } 59 | }; 60 | } 61 | 62 | // ctrl-c to exit 63 | // XDP program will be automatically removed when the link goes out of scope. 64 | 65 | Ok(()) 66 | } 67 | -------------------------------------------------------------------------------- /tools/bpftool: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feiskyer/ebpf-apps/8149f6de507062e7ef58765d0b217753c1edd5b8/tools/bpftool -------------------------------------------------------------------------------- /windows/README.md: -------------------------------------------------------------------------------- 1 | # eBPF on Windows 2 | 3 | eBPF samples on Windows. 4 | -------------------------------------------------------------------------------- /windows/connection_tracker/README.md: -------------------------------------------------------------------------------- 1 | # eBPF For Windows Demo - Connection Tracking 2 | 3 | This project demonstrates the following features adapted from [eBPF for Windows demo](https://github.com/microsoft/ebpf-for-windows-demo/tree/main/connection_tracker): 4 | 5 | 1) Native eBPF program generation. 6 | 2) The BPF_PROG_TYPE_SOCK_OPS program type. 7 | 3) The bpf_printk helper emitting tracing to ETW. 8 | 4) The BPF_MAP_TYPE_RINGBUF map type. 9 | 10 | The project provides a real-time list of connections that have been completed along with the source, destination, and duration of each connection. 11 | 12 | ## How to run 13 | 14 | ### Build BPF_PROG_TYPE_SOCK_OPS and BPF_MAP_TYPE_RINGBUF demo 15 | 16 | 1) Build the ```ebpf-for-windows-demo``` as outlined in [Getting Started](https://github.com/microsoft/ebpf-for-windows-demo/blob/main/docs/GettingStarted.md). 17 | 2) [Install eBPF-For-Windows with the msi installer](https://github.com/microsoft/ebpf-for-windows/blob/main/docs/InstallEbpf.md#method-1-install-a-release-with-the-msi-installer) on the target machine. This should start netebpfext, ebpfcore and ebpfsvc services. 18 | 3) Copy conn_track.sys and conn_tracker.exe to the target machine. 19 | 4) Launch conn_tracker.exe. 20 | 5) Launch a browser and navigate to any website. 21 | 6) Connection tracker will then show the list of connections. 22 | 23 | ## How to view logs 24 | 25 | 1) Start an ETW session and add the eBPF-For-Windows provider: ```tracelog -start MyTrace -guid C:\ebpf-for-windows\ebpf-printk.guid -rt```. 26 | 2) Start a real-time trace consumer: ```tracefmt -rt MyTrace -displayonly -jsonMeta 0```. 27 | 3) Launch conn_tracker.exe. 28 | 4) Launch a browser and navigate to any website. 29 | 5) The real-time trace consumer will then show all the bpf_printk events being generated by the eBPF program. 30 | -------------------------------------------------------------------------------- /windows/connection_tracker/bpf/bpf.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Debug 7 | Win32 8 | 9 | 10 | Release 11 | Win32 12 | 13 | 14 | Debug 15 | x64 16 | 17 | 18 | Release 19 | x64 20 | 21 | 22 | 23 | {35EE4580-FE14-45AD-9015-153384E3DF73} 24 | 10.0 25 | 26 | 27 | 28 | stdcpp20 29 | 30 | 31 | 32 | 33 | stdc17 34 | 35 | 36 | 37 | 38 | CppCode 39 | $(OutDir)conn_track.sys 40 | 41 | clang -g -target bpf -O2 -Werror $(ClangIncludes) -c %(Filename).c -o $(OutDir)%(Filename).o 42 | pushd $(OutDir) 43 | powershell -NonInteractive -ExecutionPolicy Unrestricted $(EbpfBinPath)\Convert-BpfToNative.ps1 -FileName %(Filename) -IncludeDir $(EbpfIncludePath) -Platform $(Platform) -Configuration $(Configuration) -KernelMode $true 44 | popd 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Utility 53 | v143 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}. 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /windows/connection_tracker/bpf/bpf.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /windows/connection_tracker/bpf/bpf.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /windows/connection_tracker/bpf/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /windows/connection_tracker/conn_track.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34221.43 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "conn_tracker", "conn_track\conn_track.vcxproj", "{D293D511-3498-414C-AC48-4097B9D92759}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bpf", "bpf\bpf.vcxproj", "{35EE4580-FE14-45AD-9015-153384E3DF73}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|x64 = Release|x64 15 | Release|x86 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {D293D511-3498-414C-AC48-4097B9D92759}.Debug|x64.ActiveCfg = Debug|x64 19 | {D293D511-3498-414C-AC48-4097B9D92759}.Debug|x64.Build.0 = Debug|x64 20 | {D293D511-3498-414C-AC48-4097B9D92759}.Debug|x86.ActiveCfg = Debug|Win32 21 | {D293D511-3498-414C-AC48-4097B9D92759}.Debug|x86.Build.0 = Debug|Win32 22 | {D293D511-3498-414C-AC48-4097B9D92759}.Release|x64.ActiveCfg = Release|x64 23 | {D293D511-3498-414C-AC48-4097B9D92759}.Release|x64.Build.0 = Release|x64 24 | {D293D511-3498-414C-AC48-4097B9D92759}.Release|x86.ActiveCfg = Release|Win32 25 | {D293D511-3498-414C-AC48-4097B9D92759}.Release|x86.Build.0 = Release|Win32 26 | {35EE4580-FE14-45AD-9015-153384E3DF73}.Debug|x64.ActiveCfg = Debug|x64 27 | {35EE4580-FE14-45AD-9015-153384E3DF73}.Debug|x64.Build.0 = Debug|x64 28 | {35EE4580-FE14-45AD-9015-153384E3DF73}.Debug|x86.ActiveCfg = Debug|Win32 29 | {35EE4580-FE14-45AD-9015-153384E3DF73}.Debug|x86.Build.0 = Debug|Win32 30 | {35EE4580-FE14-45AD-9015-153384E3DF73}.Release|x64.ActiveCfg = Release|x64 31 | {35EE4580-FE14-45AD-9015-153384E3DF73}.Release|x64.Build.0 = Release|x64 32 | {35EE4580-FE14-45AD-9015-153384E3DF73}.Release|x86.ActiveCfg = Release|Win32 33 | {35EE4580-FE14-45AD-9015-153384E3DF73}.Release|x86.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | GlobalSection(ExtensibilityGlobals) = postSolution 39 | SolutionGuid = {BB9F9C41-79F0-4965-BC7F-E353616B8A17} 40 | EndGlobalSection 41 | EndGlobal 42 | -------------------------------------------------------------------------------- /windows/connection_tracker/conn_track/conn_track.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 10 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 11 | 12 | 13 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 14 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 15 | 16 | 17 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 18 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 19 | 20 | 21 | 22 | 23 | Source Files 24 | 25 | 26 | 27 | 28 | Header Files 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /windows/connection_tracker/conn_track/conn_track.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /windows/connection_tracker/conn_track/conn_tracker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // SPDX-License-Identifier: MIT 3 | #pragma once 4 | 5 | #include "net/ip.h" 6 | 7 | typedef struct _ip_address 8 | { 9 | union 10 | { 11 | uint32_t ipv4; 12 | ipv6_address_t ipv6; 13 | }; 14 | } ip_address_t; 15 | 16 | typedef struct _connection_tuple 17 | { 18 | ip_address_t src_ip; 19 | uint16_t src_port; 20 | ip_address_t dst_ip; 21 | uint16_t dst_port; 22 | uint32_t protocol; 23 | uint32_t compartment_id; 24 | uint64_t interface_luid; 25 | } connection_tuple_t; 26 | 27 | typedef struct _connection_history 28 | { 29 | connection_tuple_t tuple; 30 | bool is_ipv4; 31 | uint64_t start_time; 32 | uint64_t end_time; 33 | } connection_history_t; 34 | -------------------------------------------------------------------------------- /windows/connection_tracker/conn_track/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /windows/hello/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feiskyer/ebpf-apps/8149f6de507062e7ef58765d0b217753c1edd5b8/windows/hello/.DS_Store -------------------------------------------------------------------------------- /windows/hello/README.md: -------------------------------------------------------------------------------- 1 | # Windows eBPF Hello World 2 | 3 | ```powershell 4 | # Build 5 | clang -I 'C:\ebpf-for-windows\include' -target bpf -Werror -O2 -g -c bpf.c -o bpf.o 6 | 7 | # Load 8 | netsh ebpf add program bpf.o 9 | ``` 10 | -------------------------------------------------------------------------------- /windows/hello/bpf.c: -------------------------------------------------------------------------------- 1 | #include "bpf_helpers.h" 2 | 3 | SEC("bind") 4 | int hello(bind_md_t *ctx) 5 | { 6 | bpf_printk("Hello, world!"); 7 | return 0; 8 | } -------------------------------------------------------------------------------- /windows/xdpdrop/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feiskyer/ebpf-apps/8149f6de507062e7ef58765d0b217753c1edd5b8/windows/xdpdrop/.DS_Store -------------------------------------------------------------------------------- /windows/xdpdrop/README.md: -------------------------------------------------------------------------------- 1 | # XDP DropPacket 2 | 3 | ## Compile and Load 4 | 5 | ```sh 6 | clang -I 'C:\ebpf-for-windows\include' -target bpf -Werror -O2 -g -c xdpdrop.c -o xdpdrop.o 7 | netsh ebpf add prog xdpdrop.o xdp # interface="xxx" 8 | ``` 9 | 10 | ## Start http server and curl from a different machine 11 | 12 | ```sh 13 | # Run http server on local machine 14 | python.exe -m http.server 80 15 | 16 | # Curl from a different machine 17 | curl.exe -v http:// 18 | ``` 19 | 20 | ## Unload 21 | 22 | ```sh 23 | netsh ebpf delete program id=65540 24 | ``` 25 | -------------------------------------------------------------------------------- /windows/xdpdrop/drop.c: -------------------------------------------------------------------------------- 1 | #include "bpf_endian.h" 2 | #include "bpf_helpers.h" 3 | #include "net/if_ether.h" 4 | #include "net/ip.h" 5 | #include "net/tcp.h" 6 | 7 | SEC("xdp") 8 | int drop_tcp_80(xdp_md_t *ctx) 9 | { 10 | void *data = (void *)(long)ctx->data; 11 | void *data_end = (void *)(long)ctx->data_end; 12 | 13 | struct ethhdr *eth = data; 14 | /* abort on illegal packets */ 15 | if (data + sizeof(struct ethhdr) > data_end) 16 | { 17 | return XDP_DROP; 18 | } 19 | 20 | /* do nothing for non-IP packets */ 21 | if (eth->h_proto != bpf_htons(ETH_P_IP) && eth->Type != bpf_htons(ETHERNET_TYPE_IPV4)) 22 | { 23 | return XDP_PASS; 24 | } 25 | 26 | struct iphdr *iph = data + sizeof(struct ethhdr); 27 | /* abort on illegal packets */ 28 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) 29 | { 30 | return XDP_DROP; 31 | } 32 | 33 | /* do nothing for non-TCP packets */ 34 | if (iph->protocol != IPPROTO_TCP) 35 | { 36 | return XDP_PASS; 37 | } 38 | 39 | struct tcphdr *tcph = data + sizeof(struct ethhdr) + sizeof(struct iphdr); 40 | /* abort on illegal packets */ 41 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr) > data_end) 42 | { 43 | return XDP_DROP; 44 | } 45 | 46 | /* drop packets to TCP port 80 */ 47 | if (bpf_ntohs(tcph->dest) == 80) 48 | { 49 | return XDP_DROP; 50 | } 51 | 52 | return XDP_PASS; 53 | } 54 | --------------------------------------------------------------------------------