├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── REFERENCE.md ├── docker └── builder │ ├── Dockerfile │ └── Makefile ├── docs ├── bpf-helpers.md ├── probe.md ├── problem.md ├── tc.md ├── xdp.drawio └── xdp.md ├── go.mod ├── go.sum ├── headers ├── LICENSE.BSD-2-Clause ├── bpf_core_read.h ├── bpf_endian.h ├── bpf_helper_defs.h ├── bpf_helpers.h ├── bpf_tracing.h ├── common.h ├── protocol_hdr.h ├── update.sh └── vmlinux.h ├── injection ├── Makefile ├── main.go └── printk_pass.c ├── kprobe ├── README.md ├── sys_execve │ ├── Makefile │ ├── main.go │ └── sys_execve.c └── tcp_connect │ ├── Makefile │ ├── main.go │ └── tcp_connect.c ├── project └── merbridge │ └── merbridge.md ├── socket └── socket_to_socket │ ├── Makefile │ ├── README.md │ ├── main.go │ └── socket_to_socket.c ├── tc ├── tc_filter │ ├── Makefile │ ├── flags.go │ ├── main.go │ ├── nets.go │ ├── os.go │ ├── output.go │ ├── probes.go │ └── tc_filter.c ├── tc_http │ ├── Makefile │ ├── main.go │ ├── merge.go │ ├── parse.go │ └── tc_http.c ├── tc_icmp_ping │ ├── Makefile │ ├── main.go │ └── tc_icmp_ping.c ├── tc_printk │ ├── Makefile │ ├── main.go │ └── tc_printk.c ├── tc_redirect │ ├── Makefile │ ├── main.go │ └── tc_redirect.c └── tc_statistics │ ├── Makefile │ ├── main.go │ └── tc_statistics.c ├── tracepoint ├── README.md ├── connect_accept │ ├── Makefile │ ├── connect_accept.c │ └── main.go ├── sys_enter_execve │ ├── Makefile │ ├── main.go │ └── sys_enter_execve.c ├── sys_enter_fchmodat │ ├── Makefile │ ├── main.go │ └── sys_enter_fchmodat.c ├── sys_enter_openat │ ├── Makefile │ ├── main.go │ └── sys_enter_openat.c └── sys_enter_openat_asm │ ├── Makefile │ ├── gen.go │ ├── main.go │ ├── main_bak.go │ ├── main_bak2.go │ └── sys_enter_openat.c ├── uprobe ├── README.md ├── bash_retval │ ├── Makefile │ ├── bash.c │ └── main.go ├── postgres_query │ ├── Makefile │ ├── main.go │ └── postgres_query.c └── readline │ ├── Makefile │ ├── main.go │ └── readline.c └── xdp ├── dns_cache_server ├── Makefile ├── dns_cache_server.c └── main.go ├── dns_query └── main.go ├── ipv4_firewall_v1 ├── Makefile ├── ipv4_firewall.c └── main.go ├── ipv4_firewall_v2 ├── Makefile ├── ip_protocol.go ├── ipv4_firewall.c └── main.go ├── lb4 ├── Makefile ├── README.md ├── lb4.c ├── lb4.h └── main.go ├── packets_record ├── Makefile ├── main.go └── packets_record.c ├── parse_eth ├── Makefile ├── main.go └── parse_eth.c ├── parse_icmp └── parse_icmp.c ├── parse_ipv4 ├── Makefile ├── main.go └── parse_ipv4.c ├── parse_udp ├── Makefile ├── ip_protocol.go ├── main.go └── parse_udp.c ├── parse_udp_dns ├── Makefile ├── main.go └── parse_udp_dns.c ├── parse_udp_dns_af_xdp ├── Makefile ├── main.go └── parse_udp_dns_af_xdp.c └── printk_pass ├── Makefile ├── README.md ├── main.go └── printk_pass.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.o 8 | *_bpfel.go 9 | *_bpfeb.go 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | vendor/ 19 | test/ 20 | books/ 21 | 22 | # sh 23 | sync.sh -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.18.6 as compiler 2 | ARG DIR_NAME 3 | WORKDIR /app 4 | 5 | COPY . . 6 | 7 | RUN go env -w GO111MODULE=on && go env -w GOPROXY=https://goproxy.cn,direct && go mod tidy 8 | 9 | RUN cd ${DIR_NAME} && go build -ldflags "-s -w" -o xdp . 10 | 11 | FROM ubuntu:22.04 12 | ARG DIR_NAME 13 | WORKDIR / 14 | 15 | COPY --from=compiler /app/${DIR_NAME}/xdp . 16 | RUN chmod +x xdp 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 wenzi 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zhouchaowen/ebpf_labs/311568a376816affc84e8a67e33533b855297510/Makefile -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # ebpf_labs 4 | 5 | A series of ebpf_labs experiments 6 | 7 | # how to compile 8 | 9 | ## require 10 | 11 | - Kernel >= 5.4.0 12 | - ghcr.io/cilium/ebpf-builder@1694533004 13 | 14 | ## clone 15 | ```bash 16 | git clone 17 | ``` 18 | 19 | ## compile 20 | > 示例:编译 ebpf_labs/xdp/printk_pass 21 | ```bash 22 | cd ebpf_labs/xdp/printk_pass && make build 23 | ``` 24 | 25 | ## Reference 26 | 27 | https://github.com/asavie/xdp 28 | 29 | https://github.com/cody0704/xdp-examples 30 | 31 | https://rexrock.github.io/post/af_xdp1/ 32 | 33 | https://github.com/lixiangzhong/xdp/blob/master/example/af_xdp_kern.c 34 | 35 | https://github.com/sudoamin2/sparrow 36 | 37 | https://colobu.com/2023/04/17/use-af-xdp-socket/ 38 | 39 | https://colobu.com/2023/04/02/support-1m-pps-with-zero-cpu-usage/ 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /REFERENCE.md: -------------------------------------------------------------------------------- 1 | https://github.com/apoxy-dev/ebpf 2 | 3 | https://github.com/walkerxiong/ebpf-examples 4 | 5 | https://github.com/Esonhugh/my_durdur 6 | 7 | https://github.com/terassyi/go-xdp-examples -------------------------------------------------------------------------------- /docker/builder/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | RUN apt-get update && apt-get install -y make clang-11 llvm-11 libc6-dev libc6-dev-i386 libz-dev libelf-dev libbpf-dev iproute2 && apt-get clean 3 | RUN ln -s $(which clang-11) /usr/bin/clang && ln -s $(which llc-11) /usr/bin/llc -------------------------------------------------------------------------------- /docker/builder/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | docker build -t ubuntu-bpf:22.04 . 3 | -------------------------------------------------------------------------------- /docs/problem.md: -------------------------------------------------------------------------------- 1 | # eBPF编写场景问题 2 | 3 | ## 1.C结构体转Go结构体 4 | 5 | ```c 6 | const struct event *unused __attribute__((unused)); 7 | ``` 8 | 9 | 这句eBPF的C语言代码是一个声明,用于定义一个名为`unused`的指向`struct event`类型的指针,并通过`__attribute__((unused))`标记该变量未被使用。这个标记告诉编译器,即使在代码中没有使用这个变量,也不要生成任何未使用变量的警告。 10 | 11 | 在eBPF程序中,这种用法可能是为了声明一个占位符变量,以防将来可能需要使用它,同时又避免编译器生成未使用变量的警告信息。这在一些情况下可能有用,因为eBPF程序可能会根据特定条件进行动态修改,因此某些变量可能在某些路径上未被使用。标记为`unused`并使用`__attribute__((unused))`可以帮助保持代码的清晰度,并且在需要时可以轻松地引入变量而不引发编译警告。 12 | 13 | > 注意可能有内存对齐问题。 14 | 15 | ## 2.查找跟踪点 16 | 17 | [参考](https://kiosk007.top/post/ebpf%E8%B6%85%E4%B9%8E%E4%BD%A0%E6%83%B3%E8%B1%A1/#ebpf-%E7%9A%84%E6%9C%AA%E6%9D%A5%E6%88%98%E5%9C%BA-cloud-native) 18 | 19 | - **tracepoint** 20 | 21 | ```bash 22 | # bpftrace -l 列出所有探针 23 | $ sudo bpftrace -l 'tracepoint:syscalls:*' 24 | 25 | # 查看 sys_enter_openat 详细的数据结构 26 | $ sudo bpftrace -lv tracepoint:syscalls:sys_enter_openat 27 | ``` 28 | 29 | - **kprobe & kretprobe** 30 | 31 | kprobes允许开发者在几乎所有的内核指令中以最小的开销设置动态的标记或中断。通过[kprobes](https://www.kernel.org/doc/html/latest/trace/kprobes.html),你可以动态在内核函数执行前设置断点,甚至几乎可以在任何内核代码地址处设置断点,并且指定在断点被执行时要执行的处理函数。kprobe生效原理: 32 | 33 | 1. 创建并设置kprobe回调函数,调用时触发bpf程序执行。 34 | 35 | 2. 把目标地址替换成breakpoint指令(e.g., int3 on i386 and x86_64). 36 | 37 | 3. 当程序指令执行到breakpoint指令时,执行kprobe handler 38 | 39 | 4. bpf指令被执行,执行完成后返回到原来的目标地址。 40 | 41 | 5. 当unload kprobe时把目标地址恢复到原状。 42 | 43 | - **uprobe&uretprobe** 44 | 45 | ```bash 46 | # 如:查找postgres二进制文件的跟踪点,目标跟踪点: pg_parse_query 47 | $ bpftrace -l 'uprobe:/usr/lib/postgresql/12/bin/postgres' 48 | 49 | # 容器内查找 50 | $ find /var/lib/docker/overlay2/ -name 51 | $ bpftrace -l 'uprobe:/var/lib/docker/overlay2/.../bin/postgres:*' 52 | 53 | # 执行跟踪 54 | $ sudo bpftrace -e 'uprobe:/usr/lib/postgresql/12/bin/postgres:pg_parse_query { printf("sql: %s\n", str(arg0)); }' 55 | ``` 56 | 57 | ## 3.符号表寻找跟踪函数 58 | 59 | [参考](http://arthurchiao.art/blog/linux-tracing-basis-zh/) 60 | 61 | ```bash 62 | # 查看有多少函数可以被跟踪 63 | $ readelf -s <二进制文件> | grep "Symbol table" 64 | Symbol table '.dynsym' contains 8 entries: 65 | Symbol table '.symtab' contains 67 entries: 66 | 67 | # 过滤目标函数 68 | $ readelf -s postgres | grep pg_parse_query 69 | ``` 70 | 71 | ## 4.查看编译后的ELF 72 | 73 | ```bash 74 | # 编译时带 -g 参数。编译后文件会携带 debug_info 信息 75 | clang \ 76 | -target bpf \ 77 | -I../../headers \ 78 | -g \ 79 | -O2 -c xxx.c 80 | 81 | # 查看编译后的elf 82 | $ file xxx.o 83 | xxx.o: ELF 64-bit LSB relocatable, eBPF, version 1 (SYSV), with debug_info, not stripped 84 | 85 | # 查看字节码与代码的映射,需要有(with debug_info)的消息才能查看 86 | $ llvm-objdump -S xxx.bpf.o 87 | ``` 88 | 89 | ## 5.verifier验证问题 90 | 91 | [参考](https://zhuanlan.zhihu.com/p/590851484) 92 | 93 | - 在 for 循环前面添加 #pragma unroll,进行循环展开,避免指令回跳,但在5.10内核版本是支持有限循环的,所以5.10以下版本有效。 94 | - verifier 会保存栈内存的状态,所以栈的大小是有限的,目前是 512 字节。当栈内存大小超过 512 字节时,则会被 verifier 拒绝。 95 | - 当访问栈时采用变量偏移,会导致无法推测寄存器的状态。所以 4.19 版本只支持常量偏移。下面是使用变量偏移的错误示例 96 | - 直接使用bpf_trace_printk打印字符串,ebpf运行时问题。替换为:bpf_printk 97 | 98 | ``` 99 | libbpf: prog 'pdm_main': bad map relo against '.rodata.str1.1' in section '.rodata.str1.1' 100 | ERROR: opening BPF object file failed 101 | Unable to load program 102 | ``` 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /docs/tc.md: -------------------------------------------------------------------------------- 1 | # TC 2 | 3 | ```bash 4 | # 为目标网卡创建clsact 5 | tc qdisc add dev [network-device] clsact 6 | 7 | # 加载bpf程序 8 | tc filter add dev [network-device] bpf da obj [object-name] sec [section-name] 9 | 10 | # 查看 11 | tc filter show dev [network-device] 12 | ``` 13 | 14 | ```bash 15 | # 最开始的状态 16 | $ tc qdisc show dev [network-device] 17 | qdisc noqueue 0: root refcnt 2 18 | 19 | # 创建clsact 20 | $ tc qdisc add dev [network-device] clsact 21 | 22 | # 再次查看,观察有什么不同 23 | $ tc qdisc show dev [network-device] 24 | qdisc noqueue 0: root refcnt 2 25 | qdisc clsact ffff: parent ffff:fff1 26 | 27 | # 加载TC BPF程序到容器的veth网卡上 28 | $ tc filter add dev [network-device] bpf da obj [object-name] sec [section-name] 29 | 30 | # 再次查看,观察有什么不同 31 | $ tc qdisc show dev veth09e1d2e 32 | qdisc noqueue 0: root refcnt 2 33 | qdisc clsact ffff: parent ffff:fff1 34 | 35 | $ tc filter show dev veth09e1d2e egress 36 | filter protocol all pref 49152 bpf chain 0 37 | filter protocol all pref 49152 bpf chain 0 handle 0x1 tc-xdp-drop-tcp.o:[tc] direct-action not_in_hw id 24 tag 9c60324798bac8be jited 38 | ``` 39 | 40 | ```bash 41 | # 删除 分别从 ingress 和 egress 删除所有 attach 的程序 42 | $ tc filter del dev [network-device] ingress 43 | $ tc filter del dev [network-device] egress 44 | 45 | # 从 [network-device] 删除整个 clsact qdisc 46 | # 会隐式地删除 attach 到 ingress 和 egress hook 上面的所有程序 47 | $ tc qdisc del dev [network-device] clsact 48 | ``` 49 | 50 | **解释命令:** 51 | 52 | ```sh 53 | ip netns exec ns1 ip link add vxlan1 type vxlan external dev veth1 dstport 0 54 | ``` 55 | 56 | 这个命令涉及到 Linux 中的网络命名空间(network namespace)以及 VXLAN(Virtual Extensible LAN)虚拟网络技术。让我来解释每个部分的含义: 57 | 58 | 1. `ip netns exec ns1`: 这部分命令指示在名为 `ns1` 的网络命名空间中执行后续的命令。网络命名空间是一种隔离网络资源的机制,它允许在同一主机上创建多个独立的网络环境,各自有自己的网络接口、路由表等,从而实现网络资源的隔离。 59 | 2. `ip link add vxlan1 type vxlan external dev veth1 dstport 0`: 这是在 `ns1` 命名空间中执行的具体命令,该命令是在命名空间内添加一个名为 `vxlan1` 的 VXLAN 接口。 60 | - `type vxlan`: 这部分指定要创建的接口类型为 VXLAN。VXLAN 是一种用于在现有网络基础设施上建立虚拟网络的技术,它允许在不同的物理网络中通过隧道进行通信,从而创建一个扁平的、逻辑上隔离的网络。 61 | - `external`: 这表示该 VXLAN 接口是一个外部接口,通常用于连接到物理网络或其他 VXLAN 网络。 62 | - `dev veth1`: 这表示要使用 `veth1` 这个网络设备作为 VXLAN 接口的底层设备。`veth1` 可能是另一个网络命名空间中的虚拟网络设备,连接到该命名空间。 63 | - `dstport 0`: 这指定 VXLAN 封装数据包在传输时使用的目标 UDP 端口。指定为 0 表示由系统自动选择一个合适的端口。 64 | 65 | 综上所述,这个命令将在 `ns1` 这个网络命名空间中创建一个名为 `vxlan1` 的 VXLAN 接口,用于连接到另一个网络设备 `veth1`,并使用自动选择的 UDP 端口进行通信,从而实现了 `ns1` 命名空间与其他网络之间的虚拟隔离网络连接。 66 | 67 | **解释命令:** 68 | 69 | ```shell 70 | ip netns exec ns1 ip link add dummy1 type dummy 71 | ``` 72 | 73 | 1. `ip netns exec ns1`: 这部分命令指示在名为 `ns1` 的网络命名空间中执行后续的命令。网络命名空间是一种隔离网络资源的机制,它允许在同一主机上创建多个独立的网络环境,各自有自己的网络接口、路由表等,从而实现网络资源的隔离。 74 | 2. `ip link add dummy1 type dummy`: 这是在 `ns1` 命名空间中执行的具体命令,该命令是在命名空间内添加一个名为 `dummy1` 的虚拟网络接口。 75 | - `type dummy`: 这部分指定要创建的接口类型为 Dummy。Dummy 接口是一种虚拟网络接口,它没有实际的硬件设备和网络功能,主要用于测试和占位。Dummy 接口通常被用来创建一个虚拟的网络节点,它在逻辑上存在但实际上不进行任何网络通信。 76 | 77 | 综上所述,这个命令将在 `ns1` 这个网络命名空间中创建一个名为 `dummy1` 的虚拟网络接口,这个接口不具备实际的网络功能,主要用于测试或者占位。在 `ns1` 命名空间内,你可以像使用其他网络接口一样配置和管理 `dummy1` 接口,但它并不会进行实际的网络通信 78 | 79 | **解释命令:** 80 | 81 | ```bash 82 | ip netns exec ns1 tc qdisc add dev dummy1 root handle eeee: prio bands 3 83 | 这个命令同样涉及到 Linux 中的网络命名空间(network namespace)。让我来解释每个部分的含义: 84 | ``` 85 | 86 | 1. `ip netns exec ns1`: 这部分命令指示在名为 `ns1` 的网络命名空间中执行后续的命令。网络命名空间是一种隔离网络资源的机制,它允许在同一主机上创建多个独立的网络环境,各自有自己的网络接口、路由表等,从而实现网络资源的隔离。 87 | 2. `tc qdisc add dev dummy1`: 这是在 `ns1` 命名空间中执行的具体命令,该命令是在名为 `dummy1` 的网络设备上添加一个新的队列调度器(queueing discipline,简称 qdisc)。 88 | - `qdisc`: 队列调度器用于管理网络设备上的数据包排队和发送方式,从而影响网络流量的处理方式。 89 | - `add`: 表示要添加一个新的队列调度器。 90 | - `dev dummy1`: 这表示在名为 `dummy1` 的网络设备上添加队列调度器。前面解释过,`dummy1` 是在 `ns1` 命名空间中创建的虚拟网络接口。 91 | 3. `root`: 这部分指定新的队列调度器将作为根调度器,即它将是整个队列调度器层次结构的顶层。 92 | 4. `handle eeee:`: 这表示为根调度器指定一个唯一的标识符 `eeee`。这个标识符在队列调度器层次结构中用于引用根调度器。 93 | 5. `prio bands 3`: 这是根调度器的属性设置,表示它是一个优先级队列调度器,并且有 3 个优先级队列(bands)。优先级队列调度器将数据包根据优先级分配到不同的队列中,以便可以对不同优先级的流量进行不同程度的优先处理。 94 | 95 | 综上所述,这个命令将在 `ns1` 这个网络命名空间中的 `dummy1` 网络设备上添加一个优先级队列调度器,并将它设置为整个队列调度器层次结构的根调度器,这个根调度器包含 3 个优先级队列。 96 | 97 | **解释命令:** 98 | 99 | ``` 100 | ip netns exec ns1 tc qdisc add dev vxlan1 ingress 101 | ``` 102 | 103 | 这个命令同样涉及到 Linux 中的网络命名空间(network namespace)。让我来解释每个部分的含义: 104 | 105 | 1. `ip netns exec ns1`: 这部分命令指示在名为 `ns1` 的网络命名空间中执行后续的命令。网络命名空间是一种隔离网络资源的机制,它允许在同一主机上创建多个独立的网络环境,各自有自己的网络接口、路由表等,从而实现网络资源的隔离。 106 | 2. `tc qdisc add dev vxlan1`: 这是在 `ns1` 命名空间中执行的具体命令,该命令是在名为 `vxlan1` 的网络设备上添加一个新的队列调度器(queueing discipline,简称 qdisc)。 107 | - `qdisc`: 队列调度器用于管理网络设备上的数据包排队和发送方式,从而影响网络流量的处理方式。 108 | - `add`: 表示要添加一个新的队列调度器。 109 | - `dev vxlan1`: 这表示在名为 `vxlan1` 的网络设备上添加队列调度器。前面解释过,`vxlan1` 是在 `ns1` 命名空间中创建的 VXLAN 接口。 110 | 3. `ingress`: 这部分指定新的队列调度器将作为入口(ingress)队列调度器。入口队列调度器用于控制数据包进入网络设备之前的处理,它可以对数据包进行分类、过滤和处理。 111 | 112 | 综上所述,这个命令将在 `ns1` 这个网络命名空间中的 `vxlan1` 网络设备上添加一个入口队列调度器,用于控制进入该接口的数据包的处理方式。通过这个队列调度器,你可以对进入 `vxlan1` 接口的数据包进行不同的处理,比如分类、过滤、或者进行其他网络控制操作。 113 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module ebpf_labs 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/asavie/xdp v0.3.3 7 | github.com/cilium/ebpf v0.10.0 8 | github.com/google/gopacket v1.1.19 9 | github.com/miekg/dns v1.1.53 10 | github.com/vishvananda/netlink v1.1.0 11 | golang.org/x/sys v0.2.0 12 | ) 13 | 14 | require ( 15 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect 16 | golang.org/x/mod v0.7.0 // indirect 17 | golang.org/x/net v0.2.0 // indirect 18 | golang.org/x/tools v0.3.0 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/asavie/xdp v0.3.3 h1:b5Aa3EkMJYBeUO5TxPTIAa4wyUqYcsQr2s8f6YLJXhE= 2 | github.com/asavie/xdp v0.3.3/go.mod h1:Vv5p+3mZiDh7ImdSvdon3E78wXyre7df5V58ATdIYAY= 3 | github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= 4 | github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ= 5 | github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE= 6 | github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= 7 | github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= 8 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 9 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 10 | github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= 11 | github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= 12 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 13 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 14 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 15 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 16 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 17 | github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= 18 | github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= 19 | github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= 20 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 21 | github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= 22 | github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= 23 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k= 24 | github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= 25 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 26 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 27 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 28 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 29 | golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= 30 | golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 31 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 32 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 33 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 34 | golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= 35 | golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 36 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 37 | golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= 38 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 39 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 40 | golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 41 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 42 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 43 | golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 44 | golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= 45 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 46 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 47 | golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 48 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 49 | golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= 50 | golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= 51 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 52 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 53 | -------------------------------------------------------------------------------- /headers/LICENSE.BSD-2-Clause: -------------------------------------------------------------------------------- 1 | Valid-License-Identifier: BSD-2-Clause 2 | SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html 3 | Usage-Guide: 4 | To use the BSD 2-clause "Simplified" License put the following SPDX 5 | tag/value pair into a comment according to the placement guidelines in 6 | the licensing rules documentation: 7 | SPDX-License-Identifier: BSD-2-Clause 8 | License-Text: 9 | 10 | Copyright (c) 2015 The Libbpf Authors. All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | 1. Redistributions of source code must retain the above copyright notice, 16 | this list of conditions and the following disclaimer. 17 | 18 | 2. Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 26 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /headers/bpf_endian.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BPF_ENDIAN__ 3 | #define __BPF_ENDIAN__ 4 | 5 | /* 6 | * Isolate byte #n and put it into byte #m, for __u##b type. 7 | * E.g., moving byte #6 (nnnnnnnn) into byte #1 (mmmmmmmm) for __u64: 8 | * 1) xxxxxxxx nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 9 | * 2) nnnnnnnn xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx mmmmmmmm xxxxxxxx 00000000 10 | * 3) 00000000 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 11 | * 4) 00000000 00000000 00000000 00000000 00000000 00000000 nnnnnnnn 00000000 12 | */ 13 | #define ___bpf_mvb(x, b, n, m) ((__u##b)(x) << (b-(n+1)*8) >> (b-8) << (m*8)) 14 | 15 | #define ___bpf_swab16(x) ((__u16)( \ 16 | ___bpf_mvb(x, 16, 0, 1) | \ 17 | ___bpf_mvb(x, 16, 1, 0))) 18 | 19 | #define ___bpf_swab32(x) ((__u32)( \ 20 | ___bpf_mvb(x, 32, 0, 3) | \ 21 | ___bpf_mvb(x, 32, 1, 2) | \ 22 | ___bpf_mvb(x, 32, 2, 1) | \ 23 | ___bpf_mvb(x, 32, 3, 0))) 24 | 25 | #define ___bpf_swab64(x) ((__u64)( \ 26 | ___bpf_mvb(x, 64, 0, 7) | \ 27 | ___bpf_mvb(x, 64, 1, 6) | \ 28 | ___bpf_mvb(x, 64, 2, 5) | \ 29 | ___bpf_mvb(x, 64, 3, 4) | \ 30 | ___bpf_mvb(x, 64, 4, 3) | \ 31 | ___bpf_mvb(x, 64, 5, 2) | \ 32 | ___bpf_mvb(x, 64, 6, 1) | \ 33 | ___bpf_mvb(x, 64, 7, 0))) 34 | 35 | /* LLVM's BPF target selects the endianness of the CPU 36 | * it compiles on, or the user specifies (bpfel/bpfeb), 37 | * respectively. The used __BYTE_ORDER__ is defined by 38 | * the compiler, we cannot rely on __BYTE_ORDER from 39 | * libc headers, since it doesn't reflect the actual 40 | * requested byte order. 41 | * 42 | * Note, LLVM's BPF target has different __builtin_bswapX() 43 | * semantics. It does map to BPF_ALU | BPF_END | BPF_TO_BE 44 | * in bpfel and bpfeb case, which means below, that we map 45 | * to cpu_to_be16(). We could use it unconditionally in BPF 46 | * case, but better not rely on it, so that this header here 47 | * can be used from application and BPF program side, which 48 | * use different targets. 49 | */ 50 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 51 | # define __bpf_ntohs(x) __builtin_bswap16(x) 52 | # define __bpf_htons(x) __builtin_bswap16(x) 53 | # define __bpf_constant_ntohs(x) ___bpf_swab16(x) 54 | # define __bpf_constant_htons(x) ___bpf_swab16(x) 55 | # define __bpf_ntohl(x) __builtin_bswap32(x) 56 | # define __bpf_htonl(x) __builtin_bswap32(x) 57 | # define __bpf_constant_ntohl(x) ___bpf_swab32(x) 58 | # define __bpf_constant_htonl(x) ___bpf_swab32(x) 59 | # define __bpf_be64_to_cpu(x) __builtin_bswap64(x) 60 | # define __bpf_cpu_to_be64(x) __builtin_bswap64(x) 61 | # define __bpf_constant_be64_to_cpu(x) ___bpf_swab64(x) 62 | # define __bpf_constant_cpu_to_be64(x) ___bpf_swab64(x) 63 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 64 | # define __bpf_ntohs(x) (x) 65 | # define __bpf_htons(x) (x) 66 | # define __bpf_constant_ntohs(x) (x) 67 | # define __bpf_constant_htons(x) (x) 68 | # define __bpf_ntohl(x) (x) 69 | # define __bpf_htonl(x) (x) 70 | # define __bpf_constant_ntohl(x) (x) 71 | # define __bpf_constant_htonl(x) (x) 72 | # define __bpf_be64_to_cpu(x) (x) 73 | # define __bpf_cpu_to_be64(x) (x) 74 | # define __bpf_constant_be64_to_cpu(x) (x) 75 | # define __bpf_constant_cpu_to_be64(x) (x) 76 | #else 77 | # error "Fix your compiler's __BYTE_ORDER__?!" 78 | #endif 79 | 80 | #define bpf_htons(x) \ 81 | (__builtin_constant_p(x) ? \ 82 | __bpf_constant_htons(x) : __bpf_htons(x)) 83 | #define bpf_ntohs(x) \ 84 | (__builtin_constant_p(x) ? \ 85 | __bpf_constant_ntohs(x) : __bpf_ntohs(x)) 86 | #define bpf_htonl(x) \ 87 | (__builtin_constant_p(x) ? \ 88 | __bpf_constant_htonl(x) : __bpf_htonl(x)) 89 | #define bpf_ntohl(x) \ 90 | (__builtin_constant_p(x) ? \ 91 | __bpf_constant_ntohl(x) : __bpf_ntohl(x)) 92 | #define bpf_cpu_to_be64(x) \ 93 | (__builtin_constant_p(x) ? \ 94 | __bpf_constant_cpu_to_be64(x) : __bpf_cpu_to_be64(x)) 95 | #define bpf_be64_to_cpu(x) \ 96 | (__builtin_constant_p(x) ? \ 97 | __bpf_constant_be64_to_cpu(x) : __bpf_be64_to_cpu(x)) 98 | 99 | #endif /* __BPF_ENDIAN__ */ 100 | -------------------------------------------------------------------------------- /headers/common.h: -------------------------------------------------------------------------------- 1 | // This is a compact version of `vmlinux.h` to be used in the examples using C code. 2 | 3 | #pragma once 4 | 5 | typedef unsigned char __u8; 6 | typedef short int __s16; 7 | typedef short unsigned int __u16; 8 | typedef int __s32; 9 | typedef unsigned int __u32; 10 | typedef long long int __s64; 11 | typedef long long unsigned int __u64; 12 | typedef __u8 u8; 13 | typedef __s16 s16; 14 | typedef __u16 u16; 15 | typedef __s32 s32; 16 | typedef __u32 u32; 17 | typedef __s64 s64; 18 | typedef __u64 u64; 19 | typedef __u16 __le16; 20 | typedef __u16 __be16; 21 | typedef __u32 __be32; 22 | typedef __u64 __be64; 23 | typedef __u32 __wsum; 24 | 25 | #include "bpf_helpers.h" 26 | 27 | enum bpf_map_type { 28 | BPF_MAP_TYPE_UNSPEC = 0, 29 | BPF_MAP_TYPE_HASH = 1, 30 | BPF_MAP_TYPE_ARRAY = 2, 31 | BPF_MAP_TYPE_PROG_ARRAY = 3, 32 | BPF_MAP_TYPE_PERF_EVENT_ARRAY = 4, 33 | BPF_MAP_TYPE_PERCPU_HASH = 5, 34 | BPF_MAP_TYPE_PERCPU_ARRAY = 6, 35 | BPF_MAP_TYPE_STACK_TRACE = 7, 36 | BPF_MAP_TYPE_CGROUP_ARRAY = 8, 37 | BPF_MAP_TYPE_LRU_HASH = 9, 38 | BPF_MAP_TYPE_LRU_PERCPU_HASH = 10, 39 | BPF_MAP_TYPE_LPM_TRIE = 11, 40 | BPF_MAP_TYPE_ARRAY_OF_MAPS = 12, 41 | BPF_MAP_TYPE_HASH_OF_MAPS = 13, 42 | BPF_MAP_TYPE_DEVMAP = 14, 43 | BPF_MAP_TYPE_SOCKMAP = 15, 44 | BPF_MAP_TYPE_CPUMAP = 16, 45 | BPF_MAP_TYPE_XSKMAP = 17, 46 | BPF_MAP_TYPE_SOCKHASH = 18, 47 | BPF_MAP_TYPE_CGROUP_STORAGE = 19, 48 | BPF_MAP_TYPE_REUSEPORT_SOCKARRAY = 20, 49 | BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE = 21, 50 | BPF_MAP_TYPE_QUEUE = 22, 51 | BPF_MAP_TYPE_STACK = 23, 52 | BPF_MAP_TYPE_SK_STORAGE = 24, 53 | BPF_MAP_TYPE_DEVMAP_HASH = 25, 54 | BPF_MAP_TYPE_STRUCT_OPS = 26, 55 | BPF_MAP_TYPE_RINGBUF = 27, 56 | BPF_MAP_TYPE_INODE_STORAGE = 28, 57 | }; 58 | 59 | enum xdp_action { 60 | XDP_ABORTED = 0, 61 | XDP_DROP = 1, 62 | XDP_PASS = 2, 63 | XDP_TX = 3, 64 | XDP_REDIRECT = 4, 65 | }; 66 | 67 | struct xdp_md { 68 | __u32 data; 69 | __u32 data_end; 70 | __u32 data_meta; 71 | __u32 ingress_ifindex; 72 | __u32 rx_queue_index; 73 | __u32 egress_ifindex; 74 | }; 75 | 76 | typedef __u16 __sum16; 77 | 78 | #define ETH_P_IP 0x0800 79 | 80 | enum { 81 | BPF_ANY = 0, 82 | BPF_NOEXIST = 1, 83 | BPF_EXIST = 2, 84 | BPF_F_LOCK = 4, 85 | }; 86 | 87 | /* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and 88 | * BPF_FUNC_perf_event_read_value flags. 89 | */ 90 | #define BPF_F_INDEX_MASK 0xffffffffULL 91 | #define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK 92 | 93 | #if defined(__TARGET_ARCH_x86) 94 | struct pt_regs { 95 | /* 96 | * C ABI says these regs are callee-preserved. They aren't saved on kernel entry 97 | * unless syscall needs a complete, fully filled "struct pt_regs". 98 | */ 99 | unsigned long r15; 100 | unsigned long r14; 101 | unsigned long r13; 102 | unsigned long r12; 103 | unsigned long rbp; 104 | unsigned long rbx; 105 | /* These regs are callee-clobbered. Always saved on kernel entry. */ 106 | unsigned long r11; 107 | unsigned long r10; 108 | unsigned long r9; 109 | unsigned long r8; 110 | unsigned long rax; 111 | unsigned long rcx; 112 | unsigned long rdx; 113 | unsigned long rsi; 114 | unsigned long rdi; 115 | /* 116 | * On syscall entry, this is syscall#. On CPU exception, this is error code. 117 | * On hw interrupt, it's IRQ number: 118 | */ 119 | unsigned long orig_rax; 120 | /* Return frame for iretq */ 121 | unsigned long rip; 122 | unsigned long cs; 123 | unsigned long eflags; 124 | unsigned long rsp; 125 | unsigned long ss; 126 | /* top of stack page */ 127 | }; 128 | #endif /* __TARGET_ARCH_x86 */ 129 | -------------------------------------------------------------------------------- /headers/protocol_hdr.h: -------------------------------------------------------------------------------- 1 | #define MAX_DNS_NAME_LENGTH 256 2 | 3 | struct dnshdr { 4 | __u16 transaction_id; 5 | __u8 rd : 1; // Recursion desired 6 | __u8 tc : 1; // Truncated 7 | __u8 aa : 1; // Authoritive answer 8 | __u8 opcode : 4; // Opcode 9 | __u8 qr : 1; // Query/response flag 10 | __u8 r_code : 4; // Response code 11 | __u8 cd : 1; // Checking disabled 12 | __u8 ad : 1; // Authenticated data 13 | __u8 z : 1; // Z reserved bit 14 | __u8 ra : 1; // Recursion available 15 | __u16 q_count; // Number of questions 16 | __u16 ans_count; // Number of answer RRs 17 | __u16 auth_count; // Number of authority RRs 18 | __u16 add_count; // Number of resource RRs 19 | }; 20 | -------------------------------------------------------------------------------- /headers/update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Version of libbpf to fetch headers from 4 | # LIBBPF_VERSION=0.6.1 5 | LIBBPF_VERSION=1.1.0 6 | 7 | # The headers we want 8 | prefix=libbpf-"$LIBBPF_VERSION" 9 | headers=( 10 | "$prefix"/LICENSE.BSD-2-Clause 11 | "$prefix"/src/bpf_endian.h 12 | "$prefix"/src/bpf_helper_defs.h 13 | "$prefix"/src/bpf_helpers.h 14 | "$prefix"/src/bpf_tracing.h 15 | "$prefix"/src/bpf_core_read.h 16 | ) 17 | 18 | # Fetch libbpf release and extract the desired headers 19 | curl -sL "https://github.com/libbpf/libbpf/archive/refs/tags/v${LIBBPF_VERSION}.tar.gz" | \ 20 | tar -xz --xform='s#.*/##' "${headers[@]}" 21 | 22 | bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h -------------------------------------------------------------------------------- /injection/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go -n lo 20 | -------------------------------------------------------------------------------- /injection/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/cilium/ebpf/link" 6 | "log" 7 | "net" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | ) 12 | 13 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 14 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf printk_pass.c -- -I../headers 15 | 16 | var ( 17 | InterfaceName string 18 | ) 19 | 20 | func init() { 21 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 22 | } 23 | 24 | func main() { 25 | flag.Parse() 26 | 27 | if len(InterfaceName) == 0 { 28 | log.Fatalf("Please specify a network interface") 29 | } 30 | // Look up the network interface by name. 31 | iface, err := net.InterfaceByName(InterfaceName) 32 | if err != nil { 33 | log.Fatalf("lookup network iface %s: %s", InterfaceName, err) 34 | } 35 | 36 | // Load pre-compiled programs into the kernel. 37 | sepc, err := loadBpf() 38 | if err != nil { 39 | log.Fatalf("loading objects: %s", err) 40 | } 41 | 42 | err = sepc.RewriteConstants(map[string]interface{}{ 43 | "arg": uint32(1), 44 | }) 45 | if err != nil { 46 | log.Fatalf("rewrite constants: %s", err) 47 | } 48 | 49 | objs := bpfObjects{} 50 | if err := sepc.LoadAndAssign(&objs, nil); err != nil { 51 | log.Fatalf("loading objects: %s", err) 52 | } 53 | defer objs.Close() 54 | 55 | // Attach the program. 56 | l, err := link.AttachXDP(link.XDPOptions{ 57 | Program: objs.XdpSimpleFunc, 58 | Interface: iface.Index, 59 | }) 60 | if err != nil { 61 | log.Fatalf("could not attach XDP program: %s", err) 62 | } 63 | defer l.Close() 64 | 65 | log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index) 66 | log.Printf("Press Ctrl-C to exit and remove the program") 67 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs\n") 68 | 69 | // Wait for a signal and close the XDP program, 70 | stopper := make(chan os.Signal, 1) 71 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 72 | 73 | <-stopper 74 | log.Println("Received signal, exiting XDP program..") 75 | } 76 | -------------------------------------------------------------------------------- /injection/printk_pass.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | #include "bpf_endian.h" 3 | #include "common.h" 4 | 5 | static volatile const __u32 arg; 6 | 7 | SEC("xdp") 8 | int xdp_simple_func(struct xdp_md *ctx) { 9 | bpf_printk("xdp pass, hello xdp %d",arg); 10 | return XDP_PASS; 11 | } 12 | 13 | char __license[] SEC("license") = "Dual MIT/GPL"; 14 | -------------------------------------------------------------------------------- /kprobe/README.md: -------------------------------------------------------------------------------- 1 | # KProbe 2 | 3 | 内核而言,hook 会尽可能选在 tracepoint,如果没有 tracepoint,会考虑使用 kprobe。 4 | 5 | tracepoint 的范围有限,而内核函数又太多,基于各种需求场景,kprobe 的出场机会较多;但需要注意的,并不是所有的内核函数都可以选做 hook 点,inline 函数无法被 hook,static 函数也有可能被优化掉;如果想知道究竟有哪些函数可以选做 hook 点,在 Linux 机器上,可以通过`less /proc/kallsyms`查看。 6 | 7 | 使用 eBPF 时,内核代码 kprobe 的书写范例如下: 8 | 9 | ```c 10 | SEC("kprobe/vfs_write") 11 | int kprobe_vfs_write(struct pt_regs *regs) 12 | { 13 | struct file *file 14 | file = (struct file *)PT_REGS_PARM1(regs); 15 | // ... 16 | } 17 | ``` 18 | 19 | 其中 pt_regs 的结构体如下: 20 | 21 | ```c 22 | struct pt_regs { 23 | /* 24 | * C ABI says these regs are callee-preserved. They aren't saved on kernel entry 25 | * unless syscall needs a complete, fully filled "struct pt_regs". 26 | */ 27 | unsigned long r15; 28 | unsigned long r14; 29 | unsigned long r13; 30 | unsigned long r12; 31 | unsigned long bp; 32 | unsigned long bx; 33 | /* These regs are callee-clobbered. Always saved on kernel entry. */ 34 | unsigned long r11; 35 | unsigned long r10; 36 | unsigned long r9; 37 | unsigned long r8; 38 | unsigned long ax; 39 | unsigned long cx; 40 | unsigned long dx; 41 | unsigned long si; 42 | unsigned long di; 43 | /* 44 | * On syscall entry, this is syscall#. On CPU exception, this is error code. 45 | * On hw interrupt, it's IRQ number: 46 | */ 47 | unsigned long orig_ax; 48 | /* Return frame for iretq */ 49 | unsigned long ip; 50 | unsigned long cs; 51 | unsigned long flags; 52 | unsigned long sp; 53 | unsigned long ss; 54 | /* top of stack page */ 55 | }; 56 | ``` 57 | 58 | 通常来说,我们要获取的参数,均可通过诸如 PT_REGS_PARM1 这样的宏来拿到,宏定义如下: 59 | 60 | ```bash 61 | #define PT_REGS_PARM1(x) ((x)->di) 62 | #define PT_REGS_PARM2(x) ((x)->si) 63 | #define PT_REGS_PARM3(x) ((x)->dx) 64 | #define PT_REGS_PARM4(x) ((x)->cx) 65 | #define PT_REGS_PARM5(x) ((x)->r8) 66 | ``` 67 | 68 | 寄存器如下 (这里对比 x86_64 做说明): 69 | 70 | ```bash 71 | R0: RAX, 存放函数返回值或程序退出状态码 72 | R1: RDI,第一个实参 73 | R2: RSI,第二个实参 74 | R3: RDX,第三个实参 75 | R4: RCX,第四个实参 76 | R5: R8,第五个实参 77 | R6: RBX, callee saved 78 | R7: R13, callee saved 79 | R8: R14, callee saved 80 | R9: R15, callee saved 81 | R10: RBP, 只读栈帧 82 | ``` 83 | 84 | 通过寄存器的设计,我们可以看到,每个函数调用允许5个参数,这些参数只允许立即数或者指向自己的ebpf栈(通用内核栈是不被允许的)上的指针,所有的内存访问必须先把数据放到ebpf自己的栈上(512字节的栈),才能被ebpf程序进一步操作。 85 | 86 | 87 | 88 | ## Reference 89 | 90 | [本文来源] https://www.cnxct.com/using-ebpf-kprobe-to-file-notify/ 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /kprobe/sys_execve/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /kprobe/sys_execve/main.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "encoding/binary" 9 | "errors" 10 | "log" 11 | "os" 12 | "os/signal" 13 | "syscall" 14 | 15 | "github.com/cilium/ebpf/link" 16 | "github.com/cilium/ebpf/ringbuf" 17 | "github.com/cilium/ebpf/rlimit" 18 | "golang.org/x/sys/unix" 19 | ) 20 | 21 | // https://github.com/JamesYYang/learn-ebpf 22 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 23 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -type event bpf sys_execve.c -- -I../../headers 24 | 25 | func main() { 26 | // Name of the kernel function to trace. 27 | fn := "sys_execve" 28 | 29 | // Subscribe to signals for terminating the program. 30 | stopper := make(chan os.Signal, 1) 31 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 32 | 33 | // Allow the current process to lock memory for eBPF resources. 34 | if err := rlimit.RemoveMemlock(); err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | // Load pre-compiled programs and maps into the kernel. 39 | objs := bpfObjects{} 40 | if err := loadBpfObjects(&objs, nil); err != nil { 41 | log.Fatalf("loading objects: %v", err) 42 | } 43 | defer objs.Close() 44 | 45 | // Open a Kprobe at the entry point of the kernel function and attach the 46 | // pre-compiled program. Each time the kernel function enters, the program 47 | // will emit an event containing pid and command of the execved task. 48 | kp, err := link.Kprobe(fn, objs.KprobeExecve, nil) 49 | if err != nil { 50 | log.Fatalf("opening kprobe: %s", err) 51 | } 52 | defer kp.Close() 53 | 54 | // Open a ringbuf reader from userspace RINGBUF map described in the 55 | // eBPF C program. 56 | rd, err := ringbuf.NewReader(objs.Events) 57 | if err != nil { 58 | log.Fatalf("opening ringbuf reader: %s", err) 59 | } 60 | defer rd.Close() 61 | 62 | // Close the reader when the process receives a signal, which will exit 63 | // the read loop. 64 | go func() { 65 | <-stopper 66 | 67 | if err := rd.Close(); err != nil { 68 | log.Fatalf("closing ringbuf reader: %s", err) 69 | } 70 | }() 71 | 72 | log.Println("Waiting for events..") 73 | 74 | // bpfEvent is generated by bpf2go. 75 | var event bpfEvent 76 | for { 77 | record, err := rd.Read() 78 | if err != nil { 79 | if errors.Is(err, ringbuf.ErrClosed) { 80 | log.Println("Received signal, exiting..") 81 | return 82 | } 83 | log.Printf("reading from reader: %s", err) 84 | continue 85 | } 86 | 87 | // Parse the ringbuf event entry into a bpfEvent structure. 88 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 89 | log.Printf("parsing ringbuf event: %s", err) 90 | continue 91 | } 92 | 93 | log.Printf("pid: %d\tcomm: %s\n", event.Pid, unix.ByteSliceToString(event.Comm[:])) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /kprobe/sys_execve/sys_execve.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct event { 8 | int pid; 9 | u8 comm[80]; 10 | }; 11 | 12 | /* BPF ringbuf map */ 13 | struct { 14 | __uint(type, BPF_MAP_TYPE_RINGBUF); 15 | __uint(max_entries, 256 * 1024 /* 256 KB */); 16 | } events SEC(".maps"); 17 | 18 | // Force emitting struct event into the ELF. 19 | const struct event *unused __attribute__((unused)); 20 | 21 | SEC("kprobe/sys_execve") 22 | int kprobe_execve(struct pt_regs *ctx) 23 | { 24 | struct event *e; 25 | 26 | e = bpf_ringbuf_reserve(&events, sizeof(*e), 0); 27 | if (!e) { 28 | return 0; 29 | } 30 | 31 | e->pid = bpf_get_current_pid_tgid() >> 32; 32 | bpf_get_current_comm(&e->comm, 80); 33 | 34 | bpf_ringbuf_submit(e, 0); 35 | return 0; 36 | } 37 | 38 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /kprobe/tcp_connect/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /kprobe/tcp_connect/main.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "encoding/binary" 9 | "errors" 10 | "log" 11 | "net" 12 | "os" 13 | "os/signal" 14 | "syscall" 15 | 16 | "github.com/cilium/ebpf/link" 17 | "github.com/cilium/ebpf/ringbuf" 18 | "github.com/cilium/ebpf/rlimit" 19 | ) 20 | 21 | // https://github.com/JamesYYang/learn-ebpf 22 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 23 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS --target native -type event bpf tcp_connect.c -- -I../../headers 24 | 25 | func main() { 26 | // Name of the kernel function to trace. 27 | fn := "tcp_connect" 28 | 29 | // Subscribe to signals for terminating the program. 30 | stopper := make(chan os.Signal, 1) 31 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 32 | 33 | // Allow the current process to lock memory for eBPF resources. 34 | if err := rlimit.RemoveMemlock(); err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | // Load pre-compiled programs and maps into the kernel. 39 | objs := bpfObjects{} 40 | if err := loadBpfObjects(&objs, nil); err != nil { 41 | log.Fatalf("loading objects: %v", err) 42 | } 43 | defer objs.Close() 44 | 45 | // Open a Kprobe at the entry point of the kernel function and attach the 46 | // pre-compiled program. Each time the kernel function enters, the program 47 | // will emit an event containing pid and command of the execved task. 48 | kp, err := link.Kprobe(fn, objs.KbTcpConnect, nil) 49 | if err != nil { 50 | log.Fatalf("opening kprobe: %s", err) 51 | } 52 | defer kp.Close() 53 | 54 | // Open a ringbuf reader from userspace RINGBUF map described in the 55 | // eBPF C program. 56 | rd, err := ringbuf.NewReader(objs.Events) 57 | if err != nil { 58 | log.Fatalf("opening ringbuf reader: %s", err) 59 | } 60 | defer rd.Close() 61 | 62 | // Close the reader when the process receives a signal, which will exit 63 | // the read loop. 64 | go func() { 65 | <-stopper 66 | 67 | if err := rd.Close(); err != nil { 68 | log.Fatalf("closing ringbuf reader: %s", err) 69 | } 70 | }() 71 | 72 | log.Println("Waiting for events..") 73 | log.Printf("%-6s %-12s %-16s %-6s %-15s %-6s %-6s", "PID", "COMM", "SADDR", "SPORT", "DADDR", "DPORT", "Family") 74 | 75 | // bpfEvent is generated by bpf2go. 76 | var event bpfEvent 77 | for { 78 | record, err := rd.Read() 79 | if err != nil { 80 | if errors.Is(err, ringbuf.ErrClosed) { 81 | log.Println("Received signal, exiting..") 82 | return 83 | } 84 | log.Printf("reading from reader: %s", err) 85 | continue 86 | } 87 | 88 | // Parse the ringbuf event entry into a bpfEvent structure. 89 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 90 | log.Printf("parsing ringbuf event: %s", err) 91 | continue 92 | } 93 | 94 | log.Printf("%-6d %-12s %-16s %-6d -> %-15s %-6d %-6d", 95 | event.Pid, 96 | event.Comm[:], 97 | inet_ntoa(event.Sip), 98 | event.Sport, 99 | inet_ntoa(event.Dip), 100 | event.Dport, 101 | event.Family, 102 | ) 103 | } 104 | } 105 | 106 | func inet_ntoa(in uint32) string { 107 | ip := make(net.IP, net.IPv4len) 108 | binary.LittleEndian.PutUint32(ip, in) 109 | return ip.String() 110 | } 111 | -------------------------------------------------------------------------------- /kprobe/tcp_connect/tcp_connect.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define READ_KERN(ptr) \ 9 | ({ \ 10 | typeof(ptr) _val; \ 11 | __builtin_memset((void *)&_val, 0, sizeof(_val)); \ 12 | bpf_probe_read((void *)&_val, sizeof(_val), &ptr); \ 13 | _val; \ 14 | }) 15 | 16 | #define READ_USER(ptr) \ 17 | ({ \ 18 | typeof(ptr) _val; \ 19 | __builtin_memset((void *)&_val, 0, sizeof(_val)); \ 20 | bpf_probe_read_user((void *)&_val, sizeof(_val), &ptr); \ 21 | _val; \ 22 | }) 23 | 24 | struct event 25 | { 26 | u32 sip; //源IP 27 | u32 dip; //目的IP 28 | u16 sport; //源端口 29 | u16 dport; //目的端口 30 | int family; //协议 31 | int pid; 32 | u8 comm[80]; 33 | }; 34 | 35 | /* BPF ringbuf map */ 36 | struct 37 | { 38 | __uint(type, BPF_MAP_TYPE_RINGBUF); 39 | __uint(max_entries, 256 * 1024 /* 256 KB */); 40 | } events SEC(".maps"); 41 | 42 | // Force emitting struct event into the ELF. 43 | const struct event *unused __attribute__((unused)); 44 | 45 | SEC("kprobe/tcp_connect") 46 | int kb_tcp_connect(struct pt_regs *ctx) 47 | { 48 | struct sock *sk = (struct sock *) PT_REGS_PARM1(ctx); 49 | struct event *event; 50 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 51 | if (!event) 52 | { 53 | return 0; 54 | } 55 | 56 | 57 | struct sock_common sk_common = READ_KERN(sk->__sk_common); 58 | event->dip = READ_KERN(sk_common.skc_daddr); 59 | event->sip = READ_KERN(sk_common.skc_rcv_saddr); 60 | event->sport = READ_KERN(sk_common.skc_num); 61 | event->dport = bpf_ntohs(READ_KERN(sk_common.skc_dport)); 62 | event->family = READ_KERN(sk_common.skc_family); 63 | 64 | event->pid = bpf_get_current_pid_tgid() >> 32; 65 | bpf_get_current_comm(&event->comm, 80); 66 | 67 | bpf_ringbuf_submit(event, 0); 68 | 69 | return 0; 70 | } 71 | 72 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /project/merbridge/merbridge.md: -------------------------------------------------------------------------------- 1 | ```c 2 | struct bpf_sock *bpf_sk_lookup_tcp(void *ctx, 3 | struct bpf_sock_tuple *tuple, 4 | u32 tuple_size, 5 | u64 netns, 6 | u64 flags) 7 | ``` 8 | 9 | 查找 TCP 套接字匹配元组,可以选择在子网络命名空间 netns 中。 必须检查返回值,如果非 NULL,则通过 bpf_sk_release() 释放。ctx应该指向程序的上下文,例如skb或socket(取决于使用的钩子)。 这用于确定查找的基本网络命名空间。 10 | 11 | tuple_size 必须是以下之一: 12 | 13 | sizeof(tuple->ipv4) 查找 IPv4 套接字。 14 | 15 | sizeof(tuple->ipv6) 查找 IPv6 套接字。 16 | 17 | 如果 netns 是带负号的 32 位整数,则将使用与 ctx 关联的 netns 中的套接字查找表。 对于 TC hooks,这是 skb 中设备的 netns。 对于套接字挂钩,这是套接字的网络。 如果 netns 是大于或等于 0 的任何其他有符号 32 位值,则它指定相对于与 ctx 关联的 netns 的 netns 的 ID。 超出 32 位整数范围的 netns 值被保留以供将来使用。标志的所有值都保留供将来使用,并且必须保留为零。仅当使用 CONFIG_NET 配置选项编译内核时,此帮助程序才可用。 -------------------------------------------------------------------------------- /socket/socket_to_socket/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /socket/socket_to_socket/README.md: -------------------------------------------------------------------------------- 1 | # Socket-To-Socket 2 | 3 | 加数网络数据 4 | 5 | ## 编译 6 | 7 | ```bash 8 | make gen 9 | ``` 10 | 11 | ## 加载 12 | 13 | ```bash 14 | make run 15 | ``` 16 | 17 | ## 测试 18 | 19 | - 运行服务器 20 | 21 | ```bash 22 | $ nc -l 5001 23 | ``` 24 | 25 | - 运行客服端 26 | 27 | ```bash 28 | $ nc 127.0.0.1 5001 29 | # 运行后写入数据 30 | test 31 | ``` 32 | 33 | - 抓包 34 | 35 | ```bash 36 | $ tcpdump -i lo port 5001 37 | # 可以看到只有 握手和挥手消息,中间传输数据消息都没有了 38 | ``` 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /socket/socket_to_socket/main.go: -------------------------------------------------------------------------------- 1 | // This program demonstrates attaching an eBPF program to 2 | // a cgroupv2 path and using sockops to process TCP socket events. 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "encoding/binary" 9 | "log" 10 | "net" 11 | "os" 12 | "os/signal" 13 | "path/filepath" 14 | "syscall" 15 | "time" 16 | 17 | "github.com/cilium/ebpf" 18 | "github.com/cilium/ebpf/link" 19 | "github.com/cilium/ebpf/rlimit" 20 | 21 | "golang.org/x/sys/unix" 22 | ) 23 | 24 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -type sock_key bpf socket_to_socket.c -- -I../../headers 25 | 26 | const MapsPinpath = "/sys/fs/bpf/" 27 | 28 | func main() { 29 | // Allow the current process to lock memory for eBPF resources. 30 | if err := rlimit.RemoveMemlock(); err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | // Find the path to a cgroup enabled to version 2 35 | cgroupPath, err := findCgroupPath() 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | var options ebpf.CollectionOptions 41 | options.Maps.PinPath = MapsPinpath 42 | 43 | // Load pre-compiled programs and maps into the kernel. 44 | objs := bpfObjects{} 45 | if err := loadBpfObjects(&objs, &options); err != nil { 46 | log.Fatalf("loading objects: %v", err) 47 | } 48 | defer objs.Close() 49 | 50 | err = link.RawAttachProgram(link.RawAttachProgramOptions{ 51 | Target: objs.SockOpsMap.FD(), 52 | Program: objs.BpfRedir, 53 | Attach: ebpf.AttachSkMsgVerdict, 54 | }) 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | defer func() { 59 | err = link.RawDetachProgram(link.RawDetachProgramOptions{ 60 | Target: objs.SockOpsMap.FD(), 61 | Program: objs.BpfRedir, 62 | Attach: ebpf.AttachSkMsgVerdict, 63 | }) 64 | if err != nil { 65 | log.Fatalf("error detaching '%s'\n", err) 66 | } 67 | 68 | log.Fatal("closing redirect prog...\n") 69 | }() 70 | 71 | // Attach ebpf program to a cgroupv2 72 | linkSockOps, err := link.AttachCgroup(link.CgroupOptions{ 73 | Path: cgroupPath, 74 | Program: objs.BpfSockmap, 75 | Attach: ebpf.AttachCGroupSockOps, 76 | }) 77 | if err != nil { 78 | log.Fatal(err) 79 | } 80 | defer linkSockOps.Close() 81 | 82 | log.Printf("eBPF program loaded and attached on cgroup %s\n", cgroupPath) 83 | log.Printf("Press Ctrl-C to exit and remove the program") 84 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs\n") 85 | 86 | ticker := time.NewTicker(1 * time.Second) 87 | defer ticker.Stop() 88 | 89 | log.Printf("%-15s %-6s -> %-15s %-6s %-6s", 90 | "Src addr", 91 | "Port", 92 | "Dest addr", 93 | "Port", 94 | "Family", 95 | ) 96 | go func() { 97 | //var value int 98 | for range ticker.C { 99 | var key bpfSockKey 100 | data, err := objs.SockOpsMap.NextKeyBytes(key) 101 | for len(data) != 0 && err == nil { 102 | if err := binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &key); err != nil { 103 | log.Printf("parsing data: %s", err) 104 | } 105 | log.Printf("%-15s %-6d -> %-15s %-6d %-6d", 106 | intToIP(key.Sip), 107 | intToPort(key.Sport), 108 | intToIP(key.Dip), 109 | intToPort(key.Dport), 110 | key.Family, 111 | ) 112 | data, err = objs.SockOpsMap.NextKeyBytes(key) 113 | } 114 | 115 | if err != nil { 116 | log.Printf("Empty hash shouldn't return an error:%+v", err) 117 | } else if key.Sip != 0 { 118 | log.Printf("") 119 | } 120 | } 121 | }() 122 | 123 | stopper := make(chan os.Signal, 1) 124 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 125 | 126 | // Wait 127 | <-stopper 128 | 129 | if err := objs.SockOpsMap.Unpin(); err != nil { 130 | log.Printf("Unpin map error:%+v", err) 131 | } 132 | } 133 | 134 | func findCgroupPath() (string, error) { 135 | cgroupPath := "/sys/fs/cgroup" 136 | 137 | var st syscall.Statfs_t 138 | err := syscall.Statfs(cgroupPath, &st) 139 | if err != nil { 140 | return "", err 141 | } 142 | isCgroupV2Enabled := st.Type == unix.CGROUP2_SUPER_MAGIC 143 | if !isCgroupV2Enabled { 144 | cgroupPath = filepath.Join(cgroupPath, "unified") 145 | } 146 | return cgroupPath, nil 147 | } 148 | 149 | // intToIP converts IPv4 number to net.IP 150 | func intToIP(ipNum uint32) net.IP { 151 | ip := make(net.IP, 4) 152 | binary.LittleEndian.PutUint32(ip, ipNum) 153 | return ip 154 | } 155 | 156 | // intToPort converts number to Port 157 | func intToPort(PortNum uint32) uint32 { 158 | var port = make([]byte, 4) 159 | binary.LittleEndian.PutUint32(port, PortNum) 160 | return binary.BigEndian.Uint32(port) 161 | } 162 | -------------------------------------------------------------------------------- /socket/socket_to_socket/socket_to_socket.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct sock_key { 7 | __u32 sip; 8 | __u32 dip; 9 | __u32 sport; 10 | __u32 dport; 11 | __u32 family; 12 | }; 13 | 14 | struct { 15 | __uint(type, BPF_MAP_TYPE_SOCKHASH); 16 | __uint(key_size, sizeof(struct sock_key)); 17 | __uint(value_size, sizeof(int)); 18 | __uint(max_entries, 65535); 19 | __uint(map_flags, 0); 20 | __uint(pinning, 1 /* LIBBPF_PIN_BY_NAME */); 21 | } sock_ops_map SEC(".maps"); 22 | 23 | struct sock_key *unused __attribute__((unused)); 24 | 25 | SEC("sockops") 26 | int bpf_sockmap(struct bpf_sock_ops *skops) 27 | { 28 | /* skip if the packet is not ipv4 */ 29 | if (skops->family != AF_INET) { 30 | return BPF_OK; 31 | } 32 | 33 | /* skip if it is not established op */ 34 | if (skops->op != BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB 35 | && skops->op != BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB) { 36 | return BPF_OK; 37 | } 38 | 39 | struct sock_key key = { 40 | .dip = skops->remote_ip4, 41 | .sip = skops->local_ip4, 42 | /* convert to network byte order */ 43 | .sport = bpf_htonl(skops->local_port), 44 | .dport = skops->remote_port, 45 | .family = skops->family, 46 | }; 47 | 48 | bpf_printk("bpf_sockmap >>> sport: %d dport: %d\n",skops->local_port,bpf_htonl(skops->remote_port)); 49 | 50 | bpf_sock_hash_update(skops, &sock_ops_map, &key, BPF_NOEXIST); 51 | return BPF_OK; 52 | } 53 | 54 | 55 | SEC("sk_msg") 56 | int bpf_redir(struct sk_msg_md *msg) 57 | { 58 | struct sock_key key = { 59 | .sip = msg->remote_ip4, 60 | .dip = msg->local_ip4, 61 | .dport = bpf_htonl(msg->local_port), // convert to network byte order 62 | .sport = msg->remote_port, 63 | .family = msg->family, 64 | }; 65 | 66 | bpf_printk("sk_msg >>> sport: %d dport: %d\n",msg->local_port,bpf_htonl(msg->remote_port)); 67 | 68 | bpf_msg_redirect_hash(msg, &sock_ops_map, &key, BPF_F_INGRESS); 69 | return SK_PASS; 70 | } 71 | 72 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; -------------------------------------------------------------------------------- /tc/tc_filter/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | DEV ?= lo 9 | 10 | format: 11 | find . -type f -name "*.c" | xargs clang-format -i 12 | 13 | # $BPF_CLANG is used in go:generate invocations. 14 | gen: export BPF_CLANG := $(CLANG) 15 | gen: export BPF_CFLAGS := $(CFLAGS) 16 | gen: export GO111MODULE=on 17 | gen: 18 | go generate ./... 19 | 20 | run: 21 | go run -exec sudo main.go bpf_bpfel.go -n lo 22 | -------------------------------------------------------------------------------- /tc/tc_filter/flags.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "net" 7 | "strconv" 8 | "strings" 9 | "syscall" 10 | ) 11 | 12 | // 设置flag获取uint16类型数据 13 | type uint16Value uint16 14 | 15 | func newUint16Value(val uint16, p *uint16) *uint16Value { 16 | *p = val 17 | return (*uint16Value)(p) 18 | } 19 | 20 | func Uint16Var(p *uint16, name string, value uint16, usage string) { 21 | flag.Var(newUint16Value(value, p), name, usage) 22 | } 23 | 24 | func (i *uint16Value) Set(s string) error { 25 | v, err := strconv.ParseUint(s, 0, 16) 26 | *i = uint16Value(v) 27 | return err 28 | } 29 | 30 | func (i *uint16Value) Get() any { return uint16(*i) } 31 | 32 | func (i *uint16Value) String() string { return strconv.FormatUint(uint64(*i), 10) } 33 | 34 | type Flags struct { 35 | KernelBTF string 36 | 37 | FilterInterface string 38 | FilterProto string 39 | FilterSrcIP string 40 | FilterDstIP string 41 | FilterSrcPort uint16 42 | FilterDstPort uint16 43 | FilterPort uint16 44 | 45 | DropPackage bool 46 | } 47 | 48 | type FilterConfig struct { 49 | // Filter l3 50 | FilterSrcIP [4]byte 51 | FilterDstIP [4]byte 52 | 53 | // Filter l4 54 | FilterProto uint8 55 | FilterSrcPort uint16 56 | FilterDstPort uint16 57 | FilterPort uint16 58 | 59 | IsDrop byte 60 | } 61 | 62 | // GetConfig 设置 BPF常量CFG 配置 63 | func GetConfig(flags *Flags) FilterConfig { 64 | cfg := FilterConfig{} 65 | 66 | // 源端口与目录端口 67 | if flags.FilterPort > 0 { 68 | cfg.FilterPort = flags.FilterPort 69 | } else { 70 | if flags.FilterSrcPort > 0 { 71 | cfg.FilterSrcPort = flags.FilterSrcPort 72 | } 73 | if flags.FilterDstPort > 0 { 74 | cfg.FilterDstPort = flags.FilterDstPort 75 | } 76 | } 77 | 78 | // 协议 79 | switch strings.ToLower(flags.FilterProto) { 80 | case "tcp": 81 | cfg.FilterProto = syscall.IPPROTO_TCP 82 | case "udp": 83 | cfg.FilterProto = syscall.IPPROTO_UDP 84 | case "icmp": 85 | cfg.FilterProto = syscall.IPPROTO_ICMP 86 | } 87 | 88 | // 源ip 89 | if flags.FilterSrcIP != "" { 90 | ip := net.ParseIP(flags.FilterSrcIP) 91 | if ip == nil { 92 | log.Fatalf("Failed to parse --filter-src-ip") 93 | } 94 | copy(cfg.FilterSrcIP[:], ip.To4()[:]) 95 | } 96 | 97 | // 目的ip 98 | if flags.FilterDstIP != "" { 99 | ip := net.ParseIP(flags.FilterDstIP) 100 | if ip == nil { 101 | log.Fatalf("Failed to parse --filter-dst-ip") 102 | } 103 | copy(cfg.FilterDstIP[:], ip.To4()[:]) 104 | } 105 | 106 | // 是否Drop 107 | if flags.DropPackage { 108 | cfg.IsDrop = 1 109 | } 110 | 111 | return cfg 112 | } 113 | -------------------------------------------------------------------------------- /tc/tc_filter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "github.com/cilium/ebpf/rlimit" 7 | "log" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | ) 12 | 13 | // https://github.com/JamesYYang/tc-filter 14 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 15 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -type net_packet_event bpf tc_filter.c -- -I../../headers 16 | 17 | var f = &Flags{} 18 | 19 | func init() { 20 | flag.StringVar(&f.KernelBTF, "kernel-btf", "", "specify kernel BTF file") 21 | flag.StringVar(&f.FilterInterface, "filter-if", "", "filter net interface") 22 | flag.StringVar(&f.FilterProto, "filter-proto", "", "filter L4 protocol (tcp, udp, icmp)") 23 | flag.StringVar(&f.FilterSrcIP, "filter-src-ip", "", "filter source IP addr") 24 | flag.StringVar(&f.FilterDstIP, "filter-dst-ip", "", "filter destination IP addr") 25 | Uint16Var(&f.FilterSrcPort, "filter-src-port", 0, "filter source port") 26 | Uint16Var(&f.FilterDstPort, "filter-dst-port", 0, "filter destination port") 27 | Uint16Var(&f.FilterPort, "filter-port", 0, "filter either destination or source port") 28 | flag.BoolVar(&f.DropPackage, "drop-skb", false, "drop filtered skb") 29 | } 30 | 31 | func main() { 32 | flag.Parse() 33 | 34 | fBytes, _ := json.MarshalIndent(f, "", "\t") 35 | log.Printf("\n%s\n", string(fBytes)) 36 | 37 | // 获取所有网卡信息 38 | neti := NewNetInterface() 39 | neti.LoadIfInterface() 40 | for k, v := range neti.interfaces { 41 | log.Printf("interface key:%+v,value:+%v", k, v) 42 | } 43 | 44 | // Allow the current process to lock memory for eBPF resources. 45 | if err := rlimit.RemoveMemlock(); err != nil { 46 | log.Fatal(err) 47 | } 48 | 49 | // 获取主机信息 50 | uname, _ := GetOSUnamer() 51 | unameBytes, _ := json.MarshalIndent(uname, "", "\t") 52 | log.Printf("\n%s\n", string(unameBytes)) 53 | 54 | log.Printf("TC-Filter Start...") 55 | log.Printf("Process PID: %d", os.Getpid()) 56 | 57 | o := NewOutput(neti) 58 | p := NewTcProbe(neti, o) 59 | 60 | p.Start(f) 61 | 62 | stopper := make(chan os.Signal, 1) 63 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 64 | 65 | <-stopper 66 | 67 | p.Stop() 68 | 69 | log.Println("Received signal, exiting program..") 70 | } 71 | -------------------------------------------------------------------------------- /tc/tc_filter/nets.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | type NetInterface struct { 8 | // interfaces atomic.Value 9 | interfaces map[int]NetInfo 10 | } 11 | 12 | type NetInfo struct { 13 | index int 14 | name string 15 | ip string 16 | } 17 | 18 | func NewNetInterface() *NetInterface { 19 | neti := &NetInterface{ 20 | interfaces: make(map[int]NetInfo), 21 | } 22 | 23 | return neti 24 | } 25 | 26 | func (neti *NetInterface) LoadIfInterface() { 27 | 28 | ifaces, err := net.Interfaces() 29 | if err != nil { 30 | return 31 | } 32 | for _, iface := range ifaces { 33 | if iface.Flags&net.FlagUp == 0 { 34 | continue // 忽略禁用的网卡 35 | } 36 | 37 | if iface.Flags&net.FlagLoopback != 0 { 38 | continue // 忽略loopback回路接口 39 | } 40 | 41 | addrs, ierr := iface.Addrs() 42 | if ierr != nil { 43 | err = ierr 44 | return 45 | } 46 | 47 | for _, addr := range addrs { 48 | 49 | var ip net.IP 50 | switch v := addr.(type) { 51 | case *net.IPNet: 52 | ip = v.IP 53 | case *net.IPAddr: 54 | ip = v.IP 55 | } 56 | 57 | if ip == nil || ip.IsLoopback() { 58 | continue 59 | } 60 | 61 | ip = ip.To4() 62 | if ip == nil { 63 | continue // 不是ipv4地址,放弃 64 | } 65 | 66 | n := NetInfo{ 67 | index: iface.Index, 68 | name: iface.Name, 69 | ip: ip.String(), 70 | } 71 | neti.interfaces[n.index] = n 72 | } 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /tc/tc_filter/os.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | type UnameInfo struct { 6 | SysName string `json:"SysName"` 7 | Nodename string `json:"Nodename"` 8 | Release string `json:"Release"` 9 | Version string `json:"Version"` 10 | Machine string `json:"Machine"` 11 | Domainname string `json:"Domainname"` 12 | } 13 | 14 | func GetOSUnamer() (*UnameInfo, error) { 15 | u := unix.Utsname{} 16 | e := unix.Uname(&u) 17 | if e != nil { 18 | return nil, e 19 | } 20 | ui := UnameInfo{} 21 | ui.SysName = charsToString(u.Sysname) 22 | ui.Nodename = charsToString(u.Nodename) 23 | ui.Release = charsToString(u.Release) 24 | ui.Version = charsToString(u.Version) 25 | ui.Machine = charsToString(u.Machine) 26 | ui.Domainname = charsToString(u.Domainname) 27 | 28 | return &ui, nil 29 | } 30 | 31 | func charsToString(ca [65]byte) string { 32 | s := make([]byte, len(ca)) 33 | var lens int 34 | for ; lens < len(ca); lens++ { 35 | if ca[lens] == 0 { 36 | break 37 | } 38 | s[lens] = uint8(ca[lens]) 39 | } 40 | return string(s[0:lens]) 41 | } 42 | -------------------------------------------------------------------------------- /tc/tc_filter/output.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "syscall" 7 | "time" 8 | ) 9 | 10 | const absoluteTS string = "15:04:05.000" 11 | 12 | type Output struct { 13 | tcType map[int]string 14 | neti *NetInterface 15 | } 16 | 17 | func NewOutput(neti *NetInterface) *Output { 18 | o := &Output{} 19 | o.tcType = make(map[int]string) 20 | o.tcType[1] = "INGRESS" 21 | o.tcType[0] = "EGRESS" 22 | o.neti = neti 23 | return o 24 | } 25 | 26 | func (o *Output) PrintHeader() { 27 | fmt.Printf("%-16s %-16s %-10s %-16s %-10s %-10s %-16s %-6s -> %-16s %-6s\n", 28 | "Time", 29 | "Ifindex", 30 | "Protocol", 31 | "Flag", 32 | "Len", 33 | "Direction", 34 | "Src addr", 35 | "Port", 36 | "Dest addr", 37 | "Port", 38 | ) 39 | } 40 | 41 | func (o *Output) Print(event bpfNetPacketEvent) { 42 | 43 | fmt.Printf("%-16s %-16s %-10s %-16s %-10d %-10s %-16s %-6d -> %-16s %-6d\n", 44 | time.Now().Format(absoluteTS), 45 | o.ifIndexToName(int(event.Ifindex)), 46 | protoToStr(event.Protocol), 47 | getFlagString(event), 48 | event.Len, 49 | o.tcType[int(event.Ingress)], 50 | intToIP(event.Sip), 51 | event.Sport, 52 | intToIP(event.Dip), 53 | event.Dport) 54 | } 55 | 56 | func (o *Output) ifIndexToName(ifIndex int) string { 57 | if i, ok := o.neti.interfaces[ifIndex]; ok { 58 | return i.name 59 | } 60 | return "" 61 | } 62 | 63 | func protoToStr(proto uint32) string { 64 | switch proto { 65 | case syscall.IPPROTO_TCP: 66 | return "tcp" 67 | case syscall.IPPROTO_UDP: 68 | return "udp" 69 | case syscall.IPPROTO_ICMP: 70 | return "icmp" 71 | default: 72 | return "" 73 | } 74 | } 75 | 76 | func getFlagString(event bpfNetPacketEvent) string { 77 | fStr := "" 78 | if event.Syn == 1 { 79 | fStr += "SYN|" 80 | } 81 | if event.Ack == 1 { 82 | fStr += "ACK|" 83 | } 84 | if event.Psh == 1 { 85 | fStr += "PSH|" 86 | } 87 | if event.Rst == 1 { 88 | fStr += "RST|" 89 | } 90 | 91 | if event.Fin == 1 { 92 | fStr += "FIN|" 93 | } 94 | 95 | if strings.HasSuffix(fStr, "|") { 96 | return fStr[:strings.LastIndex(fStr, "|")] 97 | } 98 | return fStr 99 | } 100 | -------------------------------------------------------------------------------- /tc/tc_http/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | DEV ?= lo 9 | 10 | format: 11 | find . -type f -name "*.c" | xargs clang-format -i 12 | 13 | # $BPF_CLANG is used in go:generate invocations. 14 | gen: export BPF_CLANG := $(CLANG) 15 | gen: export BPF_CFLAGS := $(CFLAGS) 16 | gen: export GO111MODULE=on 17 | gen: 18 | go generate ./... 19 | 20 | run: 21 | go run -exec sudo main.go bpf_bpfel.go -n lo 22 | -------------------------------------------------------------------------------- /tc/tc_http/tc_http.c: -------------------------------------------------------------------------------- 1 | // go:build ignore 2 | #include "vmlinux.h" 3 | 4 | #include "bpf_helpers.h" 5 | #include "bpf_tracing.h" 6 | #include "bpf_endian.h" 7 | 8 | #define TC_ACT_UNSPEC (-1) 9 | #define TC_ACT_OK 0 10 | #define TC_ACT_SHOT 2 11 | #define TC_ACT_STOLEN 4 12 | #define TC_ACT_REDIRECT 7 13 | 14 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 15 | 16 | #define ETH_HLEN sizeof(struct ethhdr) 17 | #define IP_HLEN sizeof(struct iphdr) 18 | #define TCP_HLEN sizeof(struct tcphdr) 19 | #define UDP_HLEN sizeof(struct udphdr) 20 | #define DNS_HLEN sizeof(struct dns_hdr) 21 | 22 | #define HTTP_DATA_MIN_SIZE 91 23 | #define TC_PACKET_MIN_SIZE 8 24 | enum tc_type { Egress, Ingress }; 25 | 26 | struct http_data_event { 27 | enum tc_type type; 28 | u32 data_len; 29 | }; 30 | 31 | // BPF programs are limited to a 512-byte stack. We store this value per CPU 32 | // and use it as a heap allocated value. 33 | struct { 34 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 35 | __uint(key_size, sizeof(u32)); 36 | __uint(value_size, sizeof(u32)); 37 | __uint(max_entries, 10240); 38 | } skb_events SEC(".maps"); 39 | 40 | static __always_inline bool skb_revalidate_data(struct __sk_buff *skb, 41 | uint8_t **head, uint8_t **tail, 42 | const u32 offset) { 43 | if (*head + offset > *tail) { 44 | if (bpf_skb_pull_data(skb, offset) < 0) { 45 | return false; 46 | } 47 | 48 | *head = (uint8_t *)(long)skb->data; 49 | *tail = (uint8_t *)(long)skb->data_end; 50 | 51 | if (*head + offset > *tail) { 52 | return false; 53 | } 54 | } 55 | 56 | return true; 57 | } 58 | 59 | static __inline int capture_packets(struct __sk_buff *skb,enum tc_type type) { 60 | // packet data 61 | unsigned char *data_start = (void *)(long)skb->data; 62 | unsigned char *data_end = (void *)(long)skb->data_end; 63 | if (data_start + sizeof(struct ethhdr) > data_end) { 64 | return TC_ACT_OK; 65 | } 66 | 67 | uint32_t l4_hdr_off; 68 | 69 | // Ethernet headers 70 | struct ethhdr *eth = (struct ethhdr *)data_start; 71 | 72 | // Simple length check 73 | if ((data_start + sizeof(struct ethhdr) + sizeof(struct iphdr)) > data_end) { 74 | return TC_ACT_OK; 75 | } 76 | 77 | // filter out non-IP packets 78 | // TODO support IPv6 79 | if (eth->h_proto != bpf_htons(ETH_P_IP)) { 80 | return TC_ACT_OK; 81 | } 82 | l4_hdr_off = sizeof(struct ethhdr) + sizeof(struct iphdr); 83 | if (!skb_revalidate_data(skb, &data_start, &data_end, l4_hdr_off)) { 84 | return TC_ACT_OK; 85 | } 86 | 87 | // IP headers 88 | struct iphdr *iph = (struct iphdr *)(data_start + sizeof(struct ethhdr)); 89 | // filter out non-TCP packets 90 | if (iph->protocol != IPPROTO_TCP) { 91 | return TC_ACT_OK; 92 | } 93 | 94 | // In theory this is the minimum packet size of an http packet 95 | if (len <= HTTP_DATA_MIN_SIZE){ 96 | return TC_ACT_OK; 97 | } 98 | 99 | struct http_data_event event = {0}; 100 | 101 | event.type = type; 102 | event.data_len = skb->len; 103 | 104 | u64 flags = BPF_F_CURRENT_CPU; 105 | flags |= (u64)skb->len << 32; 106 | size_t pkt_size = TC_PACKET_MIN_SIZE; 107 | bpf_perf_event_output(skb, &skb_events, flags, &event, pkt_size); 108 | return TC_ACT_OK; 109 | } 110 | 111 | // egress_cls_func is called for packets that are going out of the network 112 | SEC("classifier/egress") 113 | int egress_cls_func(struct __sk_buff *skb) { return capture_packets(skb,Egress); } 114 | 115 | // ingress_cls_func is called for packets that are coming into the network 116 | SEC("classifier/ingress") 117 | int ingress_cls_func(struct __sk_buff *skb) { return capture_packets(skb,Ingress); } 118 | 119 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /tc/tc_icmp_ping/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | DEV ?= lo 9 | 10 | define attach_tc 11 | tc qdisc add dev $(DEV) clsact; 12 | tc filter add dev $(DEV) egress bpf da obj bpf_bpfel.o sec tc; 13 | tc filter show dev $(DEV) egress; 14 | echo "Please run 'sudo cat /sys/kernel/debug/tracing/trace_pipe' to see output of the BPF programs"; 15 | endef 16 | 17 | format: 18 | find . -type f -name "*.c" | xargs clang-format -i 19 | 20 | # $BPF_CLANG is used in go:generate invocations. 21 | gen: export BPF_CLANG := $(CLANG) 22 | gen: export BPF_CFLAGS := $(CFLAGS) 23 | gen: 24 | go generate ./... 25 | 26 | attach: 27 | @$(attach_tc) 28 | 29 | detach: 30 | tc qdisc del dev $(DEV) clsact 31 | 32 | run: 33 | go run -exec sudo main.go bpf_bpfel.go -n lo 34 | -------------------------------------------------------------------------------- /tc/tc_icmp_ping/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "log" 10 | "net" 11 | "os" 12 | "os/signal" 13 | "syscall" 14 | 15 | "github.com/cilium/ebpf" 16 | "github.com/vishvananda/netlink" 17 | "golang.org/x/sys/unix" 18 | ) 19 | 20 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 21 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf tc_icmp_ping.c -- -I../../headers 22 | 23 | var ( 24 | InterfaceName string 25 | ) 26 | 27 | func init() { 28 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 29 | } 30 | 31 | func main() { 32 | flag.Parse() 33 | 34 | if len(InterfaceName) == 0 { 35 | log.Fatalf("Please specify a network interface") 36 | } 37 | // Look up the network interface by name. 38 | iface, err := net.InterfaceByName(InterfaceName) 39 | if err != nil { 40 | log.Fatalf("lookup network iface %s: %s", InterfaceName, err) 41 | } 42 | 43 | // Load pre-compiled programs into the kernel. 44 | objs := bpfObjects{} 45 | if err := loadBpfObjects(&objs, nil); err != nil { 46 | log.Fatalf("loading objects: %s", err) 47 | } 48 | defer objs.Close() 49 | 50 | link, err := netlink.LinkByIndex(iface.Index) 51 | if err != nil { 52 | log.Fatalf("create net link failed: %v", err) 53 | } 54 | 55 | inf, err := attachTC(link, objs.PingPong, "tc", netlink.HANDLE_MIN_INGRESS) 56 | if err != nil { 57 | log.Fatalf("attach tc ingress failed, %v", err) 58 | } 59 | defer netlink.FilterDel(inf) 60 | 61 | cxt, cancel := context.WithCancel(context.Background()) 62 | go func(cxt context.Context) { 63 | f, err := os.Open("/sys/kernel/debug/tracing/trace_pipe") 64 | if err != nil { 65 | log.Panicf("open file failed, %v", err) 66 | } 67 | defer f.Close() 68 | 69 | r := bufio.NewReader(f) 70 | for { 71 | select { 72 | case <-cxt.Done(): 73 | return 74 | default: 75 | // ReadLine is a low-level line-reading primitive. 76 | // Most callers should use ReadBytes('\n') or ReadString('\n') instead or use a Scanner. 77 | bytes, _, err := r.ReadLine() 78 | if err == io.EOF { 79 | break 80 | } 81 | if err != nil { 82 | panic(err) 83 | } 84 | log.Println(string(bytes)) 85 | } 86 | } 87 | }(cxt) 88 | 89 | log.Printf("Attached TC program to iface %q (index %d)", iface.Name, iface.Index) 90 | log.Printf("Press Ctrl-C to exit and remove the program") 91 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs\n") 92 | 93 | // Wait for a signal and close the XDP program, 94 | stopper := make(chan os.Signal, 1) 95 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 96 | 97 | <-stopper 98 | cancel() 99 | log.Println("Received signal, exiting TC program..") 100 | } 101 | 102 | // 替换 Qdisc 队列 103 | func replaceQdisc(link netlink.Link) error { 104 | attrs := netlink.QdiscAttrs{ 105 | LinkIndex: link.Attrs().Index, 106 | Handle: netlink.MakeHandle(0xffff, 0), 107 | Parent: netlink.HANDLE_CLSACT, 108 | } 109 | 110 | qdisc := &netlink.GenericQdisc{ 111 | QdiscAttrs: attrs, 112 | QdiscType: "clsact", 113 | } 114 | 115 | return netlink.QdiscReplace(qdisc) 116 | } 117 | 118 | // 加载 TC 程序 119 | func attachTC(link netlink.Link, prog *ebpf.Program, progName string, qdiscParent uint32) (*netlink.BpfFilter, error) { 120 | if err := replaceQdisc(link); err != nil { 121 | return nil, fmt.Errorf("replacing clsact qdisc for interface %s: %w", link.Attrs().Name, err) 122 | } 123 | 124 | filter := &netlink.BpfFilter{ 125 | FilterAttrs: netlink.FilterAttrs{ 126 | LinkIndex: link.Attrs().Index, 127 | Parent: qdiscParent, 128 | Handle: 1, 129 | Protocol: unix.ETH_P_ALL, 130 | Priority: 1, 131 | }, 132 | Fd: prog.FD(), 133 | Name: fmt.Sprintf("%s-%s", progName, link.Attrs().Name), 134 | DirectAction: true, 135 | } 136 | 137 | if err := netlink.FilterReplace(filter); err != nil { 138 | return nil, fmt.Errorf("replacing tc filter: %w", err) 139 | } 140 | 141 | return filter, nil 142 | } 143 | -------------------------------------------------------------------------------- /tc/tc_icmp_ping/tc_icmp_ping.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | 3 | #include "bpf_helpers.h" 4 | #include "bpf_tracing.h" 5 | #include "bpf_endian.h" 6 | 7 | /* compiler workaround */ 8 | #define bpf_memcpy __builtin_memcpy 9 | 10 | #define ICMP_PING 8 11 | #define ETH_ALEN 6 12 | 13 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 14 | 15 | #define TC_ACT_UNSPEC (-1) 16 | #define TC_ACT_OK 0 17 | #define TC_ACT_SHOT 2 18 | #define TC_ACT_STOLEN 4 19 | #define TC_ACT_REDIRECT 7 20 | 21 | #define ETH_HLEN sizeof(struct ethhdr) 22 | 23 | #define IP_SRC_OFF (ETH_HLEN + offsetof(struct iphdr, saddr)) 24 | #define IP_DST_OFF (ETH_HLEN + offsetof(struct iphdr, daddr)) 25 | 26 | #define ICMP_CSUM_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct icmphdr, checksum)) 27 | #define ICMP_TYPE_OFF (ETH_HLEN + sizeof(struct iphdr) + offsetof(struct icmphdr, type)) 28 | #define ICMP_CSUM_SIZE sizeof(__u16) 29 | 30 | SEC("classifier/ingress") 31 | int ping_pong(struct __sk_buff *skb) 32 | { 33 | /* We will access all data through pointers to structs */ 34 | void *data = (void *)(long)skb->data; 35 | void *data_end = (void *)(long)skb->data_end; 36 | 37 | /* first we check that the packet has enough data, 38 | * so we can access the three different headers of ethernet, ip and icmp 39 | */ 40 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct icmphdr) > data_end) 41 | return TC_ACT_UNSPEC; 42 | 43 | /* for easy access we re-use the Kernel's struct definitions */ 44 | struct ethhdr *eth = data; 45 | struct iphdr *ip = (data + sizeof(struct ethhdr)); 46 | struct icmphdr *icmp = (data + sizeof(struct ethhdr) + sizeof(struct iphdr)); 47 | 48 | /* Only actual IP packets are allowed */ 49 | if (eth->h_proto != bpf_htons(ETH_P_IP)) 50 | return TC_ACT_UNSPEC; 51 | 52 | /* We handle only ICMP traffic */ 53 | if (ip->protocol != IPPROTO_ICMP) 54 | return TC_ACT_UNSPEC; 55 | 56 | /* ...and only if it is an actual incoming ping */ 57 | if (icmp->type != ICMP_PING) 58 | return TC_ACT_UNSPEC; 59 | 60 | /* Let's grab the MAC address. 61 | * We need to copy them out, as they are 48 bits long */ 62 | __u8 src_mac[ETH_ALEN]; 63 | __u8 dst_mac[ETH_ALEN]; 64 | bpf_memcpy(src_mac, eth->h_source, ETH_ALEN); 65 | bpf_memcpy(dst_mac, eth->h_dest, ETH_ALEN); 66 | 67 | /* Let's grab the IP addresses. 68 | * They are 32-bit, so it is easy to access */ 69 | __u32 src_ip = ip->saddr; 70 | __u32 dst_ip = ip->daddr; 71 | 72 | bpf_printk("[action] IP Packet, proto= %d, src= %lu, dst= %lu\n", ip->protocol, src_ip, dst_ip); 73 | 74 | /* Swap the MAC addresses */ 75 | bpf_skb_store_bytes(skb, offsetof(struct ethhdr, h_source), dst_mac, ETH_ALEN, 0); 76 | bpf_skb_store_bytes(skb, offsetof(struct ethhdr, h_dest), src_mac, ETH_ALEN, 0); 77 | 78 | /* Swap the IP addresses. 79 | * IP contains a checksum, but just swapping bytes does not change it. 80 | * so no need to recalculate */ 81 | bpf_skb_store_bytes(skb, IP_SRC_OFF, &dst_ip, sizeof(dst_ip), 0); 82 | bpf_skb_store_bytes(skb, IP_DST_OFF, &src_ip, sizeof(src_ip), 0); 83 | 84 | /* Change the type of the ICMP packet to 0 (ICMP Echo Reply). 85 | * This changes the data, so we need to re-calculate the checksum 86 | */ 87 | __u8 new_type = 0; 88 | /* We need to pass the full size of the checksum here (2 bytes) */ 89 | bpf_l4_csum_replace(skb, ICMP_CSUM_OFF, ICMP_PING, new_type, ICMP_CSUM_SIZE); 90 | bpf_skb_store_bytes(skb, ICMP_TYPE_OFF, &new_type, sizeof(new_type), 0); 91 | 92 | /* Now redirecting the modified skb on the same interface to be transmitted again */ 93 | bpf_clone_redirect(skb, skb->ifindex, 0); 94 | 95 | /* We modified the packet and redirected it, it can be dropped here */ 96 | return TC_ACT_SHOT; 97 | } 98 | 99 | // https://fnordig.de/2017/03/04/send-icmp-echo-replies-using-ebpf/ 100 | char __license[] SEC("license") = "GPL"; 101 | -------------------------------------------------------------------------------- /tc/tc_printk/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | DEV ?= lo 9 | 10 | define attach_tc 11 | tc qdisc add dev $(DEV) clsact; 12 | tc filter add dev $(DEV) egress bpf da obj bpf_bpfel.o sec tc; 13 | tc filter show dev $(DEV) egress; 14 | echo "Please run 'sudo cat /sys/kernel/debug/tracing/trace_pipe' to see output of the BPF programs"; 15 | endef 16 | 17 | format: 18 | find . -type f -name "*.c" | xargs clang-format -i 19 | 20 | # $BPF_CLANG is used in go:generate invocations. 21 | gen: export BPF_CLANG := $(CLANG) 22 | gen: export BPF_CFLAGS := $(CFLAGS) 23 | gen: 24 | go generate ./... 25 | 26 | attach: 27 | @$(attach_tc) 28 | 29 | detach: 30 | tc qdisc del dev $(DEV) clsact 31 | 32 | run: 33 | go run -exec sudo main.go bpf_bpfel.go -n lo 34 | -------------------------------------------------------------------------------- /tc/tc_printk/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "flag" 7 | "fmt" 8 | "github.com/cilium/ebpf" 9 | "github.com/vishvananda/netlink" 10 | "golang.org/x/sys/unix" 11 | "io" 12 | "log" 13 | "net" 14 | "os" 15 | "os/signal" 16 | "syscall" 17 | ) 18 | 19 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 20 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf tc_printk.c -- -I../../headers 21 | 22 | var ( 23 | InterfaceName string 24 | ) 25 | 26 | func init() { 27 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 28 | } 29 | 30 | func main() { 31 | flag.Parse() 32 | 33 | if len(InterfaceName) == 0 { 34 | log.Fatalf("Please specify a network interface") 35 | } 36 | // Look up the network interface by name. 37 | iface, err := net.InterfaceByName(InterfaceName) 38 | if err != nil { 39 | log.Fatalf("lookup network iface %s: %s", InterfaceName, err) 40 | } 41 | 42 | // Load pre-compiled programs into the kernel. 43 | objs := bpfObjects{} 44 | if err := loadBpfObjects(&objs, nil); err != nil { 45 | log.Fatalf("loading objects: %s", err) 46 | } 47 | defer objs.Close() 48 | 49 | link, err := netlink.LinkByIndex(iface.Index) 50 | if err != nil { 51 | log.Fatalf("create net link failed: %v", err) 52 | } 53 | 54 | inf, err := attachTC(link, objs.TcDropTcp, "tc", netlink.HANDLE_MIN_INGRESS) 55 | if err != nil { 56 | log.Fatalf("attach tc ingress failed, %v", err) 57 | } 58 | defer netlink.FilterDel(inf) 59 | 60 | cxt, cancel := context.WithCancel(context.Background()) 61 | go func(cxt context.Context) { 62 | f, err := os.Open("/sys/kernel/debug/tracing/trace_pipe") 63 | if err != nil { 64 | log.Panicf("open file failed, %v", err) 65 | } 66 | defer f.Close() 67 | 68 | r := bufio.NewReader(f) 69 | for { 70 | select { 71 | case <-cxt.Done(): 72 | return 73 | default: 74 | // ReadLine is a low-level line-reading primitive. 75 | // Most callers should use ReadBytes('\n') or ReadString('\n') instead or use a Scanner. 76 | bytes, _, err := r.ReadLine() 77 | if err == io.EOF { 78 | break 79 | } 80 | if err != nil { 81 | panic(err) 82 | } 83 | log.Println(string(bytes)) 84 | } 85 | } 86 | }(cxt) 87 | 88 | log.Printf("Attached TC program to iface %q (index %d)", iface.Name, iface.Index) 89 | log.Printf("Press Ctrl-C to exit and remove the program") 90 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs\n") 91 | 92 | // Wait for a signal and close the XDP program, 93 | stopper := make(chan os.Signal, 1) 94 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 95 | 96 | <-stopper 97 | cancel() 98 | log.Println("Received signal, exiting TC program..") 99 | } 100 | 101 | // 替换 Qdisc 队列 102 | func replaceQdisc(link netlink.Link) error { 103 | attrs := netlink.QdiscAttrs{ 104 | LinkIndex: link.Attrs().Index, 105 | Handle: netlink.MakeHandle(0xffff, 0), 106 | Parent: netlink.HANDLE_CLSACT, 107 | } 108 | 109 | qdisc := &netlink.GenericQdisc{ 110 | QdiscAttrs: attrs, 111 | QdiscType: "clsact", 112 | } 113 | 114 | return netlink.QdiscReplace(qdisc) 115 | } 116 | 117 | // 加载 TC 程序 118 | func attachTC(link netlink.Link, prog *ebpf.Program, progName string, qdiscParent uint32) (*netlink.BpfFilter, error) { 119 | if err := replaceQdisc(link); err != nil { 120 | return nil, fmt.Errorf("replacing clsact qdisc for interface %s: %w", link.Attrs().Name, err) 121 | } 122 | 123 | filter := &netlink.BpfFilter{ 124 | FilterAttrs: netlink.FilterAttrs{ 125 | LinkIndex: link.Attrs().Index, 126 | Parent: qdiscParent, 127 | Handle: 1, 128 | Protocol: unix.ETH_P_ALL, 129 | Priority: 1, 130 | }, 131 | Fd: prog.FD(), 132 | Name: fmt.Sprintf("%s-%s", progName, link.Attrs().Name), 133 | DirectAction: true, 134 | } 135 | 136 | if err := netlink.FilterReplace(filter); err != nil { 137 | return nil, fmt.Errorf("replacing tc filter: %w", err) 138 | } 139 | 140 | return filter, nil 141 | } 142 | -------------------------------------------------------------------------------- /tc/tc_printk/tc_printk.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "bpf_endian.h" 11 | #include "bpf_helpers.h" 12 | 13 | typedef unsigned int u32; 14 | 15 | static __inline bool is_TCP(void *data_begin, void *data_end){ 16 | bpf_printk("Entering is_TCP\n"); 17 | struct ethhdr *eth = data_begin; 18 | 19 | // Check packet's size 20 | // the pointer arithmetic is based on the size of data type, current_address plus int(1) means: 21 | // new_address= current_address + size_of(data type) 22 | if ((void *)(eth + 1) > data_end) // 23 | return false; 24 | 25 | // Check if Ethernet frame has IP packet 26 | if (eth->h_proto == bpf_htons(ETH_P_IP)) 27 | { 28 | struct iphdr *iph = (struct iphdr *)(eth + 1); // or (struct iphdr *)( ((void*)eth) + ETH_HLEN ); 29 | if ((void *)(iph + 1) > data_end) 30 | return false; 31 | 32 | // extract src ip and destination ip 33 | u32 ip_src = iph->saddr; 34 | u32 ip_dst = iph->daddr; 35 | 36 | // 37 | bpf_printk("src ip addr1: %d.%d.%d\n",(ip_src) & 0xFF,(ip_src >> 8) & 0xFF,(ip_src >> 16) & 0xFF); 38 | bpf_printk("src ip addr2:.%d\n",(ip_src >> 24) & 0xFF); 39 | 40 | bpf_printk("dest ip addr1: %d.%d.%d\n",(ip_dst) & 0xFF,(ip_dst >> 8) & 0xFF,(ip_dst >> 16) & 0xFF); 41 | bpf_printk("dest ip addr2: .%d\n",(ip_dst >> 24) & 0xFF); 42 | 43 | // Check if IP packet contains a TCP segment 44 | if (iph->protocol == IPPROTO_TCP) 45 | return true; 46 | } 47 | return false; 48 | } 49 | 50 | SEC("tc") 51 | int tc_drop_tcp(struct __sk_buff *skb) 52 | { 53 | 54 | bpf_printk("Entering tc section\n"); 55 | void *data = (void *)(long)skb->data; 56 | void *data_end = (void *)(long)skb->data_end; 57 | 58 | 59 | if (is_TCP(data, data_end)) 60 | return TC_ACT_SHOT; 61 | else 62 | return TC_ACT_OK; 63 | } 64 | 65 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /tc/tc_redirect/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | DEV ?= lo 9 | 10 | define attach_tc 11 | tc qdisc add dev $(DEV) clsact; 12 | tc filter add dev $(DEV) egress bpf da obj bpf_bpfel.o sec tc; 13 | tc filter show dev $(DEV) egress; 14 | echo "Please run 'sudo cat /sys/kernel/debug/tracing/trace_pipe' to see output of the BPF programs"; 15 | endef 16 | 17 | format: 18 | find . -type f -name "*.c" | xargs clang-format -i 19 | 20 | # $BPF_CLANG is used in go:generate invocations. 21 | gen: export BPF_CLANG := $(CLANG) 22 | gen: export BPF_CFLAGS := $(CFLAGS) 23 | gen: 24 | go generate ./... 25 | 26 | attach: 27 | @$(attach_tc) 28 | 29 | detach: 30 | tc qdisc del dev $(DEV) clsact 31 | 32 | run: 33 | go run -exec sudo main.go bpf_bpfel.go -n lo 34 | -------------------------------------------------------------------------------- /tc/tc_redirect/tc_redirect.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | struct endpointKey { 11 | __u32 ip; 12 | }; 13 | 14 | struct endpointInfo { 15 | __u32 ifIndex; 16 | __u32 lxcIfIndex; 17 | __u8 mac[8]; 18 | __u8 nodeMac[8]; 19 | }; 20 | 21 | struct { 22 | __uint(type, BPF_MAP_TYPE_HASH); 23 | __uint(max_entries, 255); 24 | __type(key, struct endpointKey); 25 | __type(value, struct endpointInfo); 26 | // 加了 SEC(".maps") 的话, clang 在编译时需要加 -g 参数用来生成调试信息 27 | // 这里 ding_lxc 是必须要和 bpftool map list 出来的那个 pinned 28 | // 中路径的名字一样 29 | } ding_lxc SEC(".maps"); 30 | 31 | SEC("classifier/ingress") 32 | int cls_main(struct __sk_buff *skb) { 33 | 34 | void *data = (void *)(long)skb->data; 35 | void *data_end = (void *)(long)skb->data_end; 36 | 37 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) { 38 | return TC_ACT_UNSPEC; 39 | } 40 | 41 | struct ethhdr *eth = data; 42 | struct iphdr *ip = (data + sizeof(struct ethhdr)); 43 | 44 | if (eth->h_proto != __constant_htons(ETH_P_IP)) { 45 | return TC_ACT_UNSPEC; 46 | } 47 | 48 | // 在 go 那头儿往 ebpf 的 map 里存的时候我这个 arm 是按照小端序存的 49 | // 这里给转成网络的大端序 50 | // __u32 src_ip = htonl(ip->saddr); 51 | __u32 dst_ip = ip->daddr; 52 | 53 | bpf_printk("dest ip: %d",dst_ip); 54 | bpf_printk("eth->h_source mac: %02x:%02x:%02x",eth->h_source[0],eth->h_source[1],eth->h_source[2]); 55 | bpf_printk("eth->h_source mac: %02x:%02x:%02x",eth->h_source[3],eth->h_source[4],eth->h_source[5]); 56 | bpf_printk("eth->h_dest mac: %02x:%02x:%02x",eth->h_dest[0],eth->h_dest[1],eth->h_dest[2]); 57 | bpf_printk("eth->h_dest mac: %02x:%02x:%02x",eth->h_dest[3],eth->h_dest[4],eth->h_dest[5]); 58 | 59 | // 拿到 mac 地址 60 | __u8 src_mac[ETH_ALEN]; 61 | __u8 dst_mac[ETH_ALEN]; 62 | 63 | struct endpointKey epKey = {}; 64 | epKey.ip = dst_ip; 65 | 66 | // 在 lxc 中查找 67 | struct endpointInfo *ep = bpf_map_lookup_elem(&ding_lxc, &epKey); 68 | if (ep) { 69 | // bpf_printk("ep->node mac: %02x:%02x:%02x",ep->nodeMac[0],ep->nodeMac[1],ep->nodeMac[2]); 70 | // bpf_printk("ep->node mac: %02x:%02x:%02x",ep->nodeMac[3],ep->nodeMac[4],ep->nodeMac[5]); 71 | // bpf_printk("ep->ns mac: %02x:%02x:%02x",ep->mac[0],ep->mac[1],ep->mac[2]); 72 | // bpf_printk("ep->ns mac: %02x:%02x:%02x",ep->mac[3],ep->mac[4],ep->mac[5]); 73 | 74 | // 如果能找到说明是要发往本机其他 pod 中的,把 mac 地址改成目标 pod 的两对儿 veth 的 mac 地址 75 | __builtin_memcpy(src_mac, ep->nodeMac, ETH_ALEN); 76 | __builtin_memcpy(dst_mac, ep->mac, ETH_ALEN); 77 | bpf_skb_store_bytes(skb, offsetof(struct ethhdr, h_source), dst_mac,ETH_ALEN, 0); 78 | bpf_skb_store_bytes(skb, offsetof(struct ethhdr, h_dest), src_mac, ETH_ALEN,0); 79 | 80 | return bpf_redirect_peer(ep->lxcIfIndex, 0); 81 | } 82 | return TC_ACT_UNSPEC; 83 | } 84 | 85 | char _license[] SEC("license") = "GPL"; 86 | -------------------------------------------------------------------------------- /tc/tc_statistics/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | DEV ?= lo 9 | 10 | define attach_tc 11 | tc qdisc add dev $(DEV) clsact; 12 | tc filter add dev $(DEV) egress bpf da obj bpf_bpfel.o sec tc; 13 | tc filter show dev $(DEV) egress; 14 | echo "Please run 'sudo cat /sys/kernel/debug/tracing/trace_pipe' to see output of the BPF programs"; 15 | endef 16 | 17 | format: 18 | find . -type f -name "*.c" | xargs clang-format -i 19 | 20 | # $BPF_CLANG is used in go:generate invocations. 21 | gen: export BPF_CLANG := $(CLANG) 22 | gen: export BPF_CFLAGS := $(CFLAGS) 23 | gen: 24 | go generate ./... 25 | 26 | attach: 27 | @$(attach_tc) 28 | 29 | detach: 30 | tc qdisc del dev $(DEV) clsact 31 | 32 | run: 33 | go run -exec sudo main.go bpf_bpfel.go -n lo 34 | -------------------------------------------------------------------------------- /tc/tc_statistics/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "flag" 6 | "fmt" 7 | "github.com/cilium/ebpf" 8 | "github.com/vishvananda/netlink" 9 | "golang.org/x/sys/unix" 10 | "log" 11 | "net" 12 | "time" 13 | ) 14 | 15 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 16 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf tc_statistics.c --type pair --type stats -- -I../../headers 17 | 18 | var ( 19 | InterfaceName string 20 | ) 21 | 22 | func init() { 23 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 24 | } 25 | 26 | func main() { 27 | flag.Parse() 28 | 29 | if len(InterfaceName) == 0 { 30 | log.Fatalf("Please specify a network interface") 31 | } 32 | // Look up the network interface by name. 33 | iface, err := net.InterfaceByName(InterfaceName) 34 | if err != nil { 35 | log.Fatalf("lookup network iface %s: %s", InterfaceName, err) 36 | } 37 | 38 | // Load pre-compiled programs into the kernel. 39 | objs := bpfObjects{} 40 | if err := loadBpfObjects(&objs, nil); err != nil { 41 | log.Fatalf("loading objects: %s", err) 42 | } 43 | defer objs.Close() 44 | 45 | link, err := netlink.LinkByIndex(iface.Index) 46 | if err != nil { 47 | log.Fatalf("create net link failed: %v", err) 48 | } 49 | 50 | inf, err := attachTC(link, objs.TrackTx, "tc", netlink.HANDLE_MIN_INGRESS) 51 | if err != nil { 52 | log.Fatalf("attach tc ingress failed, %v", err) 53 | } 54 | defer netlink.FilterDel(inf) 55 | 56 | log.Printf("Attached TC program to iface %q (index %d)", iface.Name, iface.Index) 57 | log.Printf("Press Ctrl-C to exit and remove the program") 58 | log.Printf("Waiting for events..") 59 | 60 | ticker := time.NewTicker(1 * time.Second) 61 | defer ticker.Stop() 62 | 63 | for range ticker.C { 64 | var key bpfPair 65 | var value bpfStats 66 | var entries = objs.bpfMaps.Trackers.Iterate() 67 | for entries.Next(&key, &value) { 68 | log.Printf("src_ip: %+v, des_ip: %+v, cnt: %+v, bytes: %+v\n", 69 | intToIP(key.Lip), intToIP(key.Rip), 70 | value.TxCnt, value.TxBytes) 71 | } 72 | 73 | if err := entries.Err(); err != nil { 74 | panic(fmt.Sprint("Iterator encountered an error:", err)) 75 | } 76 | } 77 | } 78 | 79 | // intToIP converts IPv4 number to net.IP 80 | func intToIP(ipNum uint32) net.IP { 81 | ip := make(net.IP, 4) 82 | binary.LittleEndian.PutUint32(ip, ipNum) 83 | return ip 84 | } 85 | 86 | // 替换 Qdisc 队列 87 | func replaceQdisc(link netlink.Link) error { 88 | attrs := netlink.QdiscAttrs{ 89 | LinkIndex: link.Attrs().Index, 90 | Handle: netlink.MakeHandle(0xffff, 0), 91 | Parent: netlink.HANDLE_CLSACT, 92 | } 93 | 94 | qdisc := &netlink.GenericQdisc{ 95 | QdiscAttrs: attrs, 96 | QdiscType: "clsact", 97 | } 98 | 99 | return netlink.QdiscReplace(qdisc) 100 | } 101 | 102 | // 加载 TC 程序 103 | func attachTC(link netlink.Link, prog *ebpf.Program, progName string, qdiscParent uint32) (*netlink.BpfFilter, error) { 104 | if err := replaceQdisc(link); err != nil { 105 | return nil, fmt.Errorf("replacing clsact qdisc for interface %s: %w", link.Attrs().Name, err) 106 | } 107 | 108 | filter := &netlink.BpfFilter{ 109 | FilterAttrs: netlink.FilterAttrs{ 110 | LinkIndex: link.Attrs().Index, 111 | Parent: qdiscParent, 112 | Handle: 1, 113 | Protocol: unix.ETH_P_ALL, 114 | Priority: 1, 115 | }, 116 | Fd: prog.FD(), 117 | Name: fmt.Sprintf("%s-%s", progName, link.Attrs().Name), 118 | DirectAction: true, 119 | } 120 | 121 | if err := netlink.FilterReplace(filter); err != nil { 122 | return nil, fmt.Errorf("replacing tc filter: %w", err) 123 | } 124 | 125 | return filter, nil 126 | } 127 | -------------------------------------------------------------------------------- /tc/tc_statistics/tc_statistics.c: -------------------------------------------------------------------------------- 1 | // go:build ignore 2 | #include "vmlinux.h" 3 | 4 | #include "bpf_endian.h" 5 | #include "bpf_helpers.h" 6 | #include "bpf_tracing.h" 7 | 8 | #define TC_ACT_OK 0 9 | #define ETH_P_IP 0x0800 /* Internet Protocol packet */ 10 | 11 | struct pair { 12 | __u32 lip; // local IP 13 | __u32 rip; // remote IP 14 | }; 15 | 16 | struct stats { 17 | __u64 tx_cnt; 18 | __u64 tx_bytes; 19 | }; 20 | 21 | struct { 22 | __uint(type, BPF_MAP_TYPE_HASH); 23 | __uint(max_entries, 2048); 24 | __type(key, struct pair); 25 | __type(value, struct stats); 26 | // __uint(pinning, 1 /* LIBBPF_PIN_BY_NAME */); // 钉住让cilium/ebpf可以访问 27 | } trackers SEC(".maps"); 28 | 29 | static bool parse_ipv4(void* data, void* data_end, struct pair *pair){ 30 | bpf_printk("Entering parse_ipv4\n"); 31 | struct ethhdr *eth = data; 32 | struct iphdr *ip; 33 | 34 | if(data + sizeof(struct ethhdr) > data_end) 35 | return false; 36 | 37 | if(bpf_ntohs(eth->h_proto) != ETH_P_IP) 38 | return false; 39 | 40 | ip = data + sizeof(struct ethhdr); 41 | 42 | if ((void*) ip + sizeof(struct iphdr) > data_end) 43 | return false; 44 | 45 | bpf_printk("src ip addr1: %d.%d.%d\n",(ip->saddr) & 0xFF,(ip->saddr >> 8) & 0xFF,(ip->saddr >> 16) & 0xFF); 46 | bpf_printk("src ip addr2:.%d\n",(ip->saddr >> 24) & 0xFF); 47 | 48 | bpf_printk("dest ip addr1: %d.%d.%d\n",(ip->daddr) & 0xFF,(ip->daddr >> 8) & 0xFF,(ip->daddr >> 16) & 0xFF); 49 | bpf_printk("dest ip addr2: .%d\n",(ip->daddr >> 24) & 0xFF); 50 | 51 | pair->lip = ip->saddr; 52 | pair->rip = ip->daddr; 53 | 54 | return true; 55 | } 56 | 57 | static void update_stats(struct pair *key, long long bytes){ 58 | struct stats *stats, newstats = {0,0}; 59 | 60 | stats = bpf_map_lookup_elem(&trackers, key); 61 | if(stats){ 62 | stats->tx_cnt++; 63 | stats->tx_bytes += bytes; 64 | }else{ 65 | newstats.tx_cnt = 1; 66 | newstats.tx_bytes = bytes; 67 | bpf_map_update_elem(&trackers, key, &newstats, BPF_NOEXIST); 68 | } 69 | } 70 | 71 | SEC("tc") 72 | int track_tx(struct __sk_buff *skb) 73 | { 74 | void *data_end = (void *)(long)skb->data_end; 75 | void *data = (void *)(long)skb->data; 76 | struct pair pair; 77 | 78 | if(!parse_ipv4(data,data_end,&pair)) 79 | return TC_ACT_OK; 80 | 81 | // Update TX statistics 82 | update_stats(&pair,data_end-data); 83 | 84 | return TC_ACT_OK; 85 | } 86 | 87 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /tracepoint/README.md: -------------------------------------------------------------------------------- 1 | # Tracepoint 2 | 3 | 可以通过查看 `/sys/kernel/debug/tracing/available_events` 文件的内容找到 tracepoint 可监控的事件。 文件中每行内容的格式是: 4 | 5 | ```bash 6 | # : 7 | syscalls:sys_enter_execve 8 | ``` 9 | 10 | ## 引用方式 11 | 12 | tracepoint 事件对应的 SEC 格式为: 13 | 14 | ```c 15 | // SEC("tracepoint//")/SEC("tp//") 16 | // 比如: 17 | SEC("tracepoint/syscalls/sys_enter_fchmodat") 18 | SEC("tp/syscalls/sys_enter_fchmodat") // 等同 19 | ``` 20 | 21 | > `` 和 `` 的值均取值前面 available_events 文件中列出的内容。 22 | 23 | ## 参数类型 24 | 25 | 如何确定 tracepoint 事件处理函数的参数类型,获取对应的内核调用参数? 26 | 27 | 假设,我们想通过 tracepoint 监控 `chmod` 这个命令涉及的 `fchmodat` 系统调用, 比如拿到操作文件名称以及操作的权限 mode 的值。 28 | 29 | 第一步,先确定 `chmod` 所使用的系统调用,这个比较简单,有很多种方法可以做到,比如通过 `strace` 命令: 30 | 31 | ```bash 32 | $ strace chmod 600 a.txt 33 | ... 34 | fchmodat(AT_FDCWD, "a.txt", 0600) = 0 35 | ... 36 | ``` 37 | 38 | 第二步,找到针对这个系统调用可以使用的 tracepoint 事件: 39 | 40 | ```bash 41 | $ sudo cat /sys/kernel/debug/tracing/available_events |grep fchmodat 42 | syscalls:sys_exit_fchmodat 43 | syscalls:sys_enter_fchmodat 44 | ``` 45 | 46 | 可以看到,有 `sys_enter_fchmodat` (进入)和 `sys_exit_fchmodat` (退出)这两个事件。 47 | 48 | 第三步,确定函数的参数类型:这个需要到 `vmlinux.h` 文件中进行查找, 一般 `sys_enter_xx` 对应 `trace_event_raw_sys_enter` , `sys_exit_xx` 对应 `trace_event_raw_sys_exit` , 其他的一般对应 `trace_event_raw_` ,如果没找到的话,可以参考 `trace_event_raw_sys_enter` 的例子找它相近的 struct。 49 | 50 | 对于 `sys_enter_fchmodat` ,我们使用 `trace_event_raw_sys_enter` 这个 struct: 51 | 52 | ```c 53 | struct trace_event_raw_sys_enter { 54 | struct trace_entry ent; 55 | long int id; 56 | long unsigned int args[6]; 57 | char __data[0]; 58 | }; 59 | ``` 60 | 61 | 其中 `args` 中就存储了事件相关的我们可以获取的信息,至于里面包含了哪些信息就是第四步需要确定的信息。 62 | 63 | 第四步,确定事件本身可以获取到哪些信息,虽然我们知道 `fchmodat` 系统调用需要提供文件名称和 mode 信息, 但是,我们不确定是否可以在 ebpf 程序中获取到这些信息。 64 | 65 | 可以通过查看 `/sys/kernel/debug/tracing/events///format` 文件获取到我们可以获取哪些信息。 比如 `sys_enter_fchmodat` 这个事件的内容如下: 66 | 67 | ```bash 68 | $ sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_fchmodat/format 69 | name: sys_enter_fchmodat 70 | ID: 647 71 | format: 72 | field:unsigned short common_type; offset:0; size:2; signed:0; 73 | field:unsigned char common_flags; offset:2; size:1; signed:0; 74 | field:unsigned char common_preempt_count; offset:3; size:1; signed:0; 75 | field:int common_pid; offset:4; size:4; signed:1; 76 | 77 | field:int __syscall_nr; offset:8; size:4; signed:1; 78 | field:int dfd; offset:16; size:8; signed:0; 79 | field:const char * filename; offset:24; size:8; signed:0; 80 | field:umode_t mode; offset:32; size:8; signed:0; 81 | 82 | print fmt: "dfd: 0x%08lx, filename: 0x%08lx, mode: 0x%08lx", ((unsigned long)(REC->dfd)), ((unsigned long)(REC->filename)), ((unsigned long)(REC->mode)) 83 | ``` 84 | 85 | `print fmt` 中引用的字段都是我们可以在 ebpf 程序中获取的信息。 从上面可以看到,我们可以获取 `sys_enter_fchmodat` 事件的 `dfd` 、 `filename` 以及 `mode` 信息, 这里就包含了前面所说的文件名称以及权限 mode 信息。 86 | 87 | 这些字段的值可以通过 `trace_event_raw_sys_enter` 的 `args` 数组获取,即通过 `args[0]` 获取 `dfd` , `args[1]` 获取 `filename` 以此类推。 88 | 89 | 信息都确定好了,就可以写程序了。比如上面 `sys_enter_fchmodat` 事件的示例 ebpf 程序如下: 90 | 91 | ```c 92 | SEC("tracepoint/syscalls/sys_enter_fchmodat") 93 | int tracepoint__syscalls__sys_enter_fchmodat(struct trace_event_raw_sys_enter *ctx) { 94 | struct event_t *event; 95 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 96 | if (!event) { 97 | return 0; 98 | } 99 | 100 | struct task_struct *task = (struct task_struct *)bpf_get_current_task(); 101 | 102 | event->host_pid = bpf_get_current_pid_tgid() >> 32; 103 | event->host_ppid = BPF_CORE_READ(task, real_parent, tgid); 104 | bpf_get_current_comm(&event->comm, sizeof(event->comm)); 105 | 106 | char *filename_ptr = (char *)BPF_CORE_READ(ctx, args[1]); 107 | bpf_core_read_user_str(&event->filename, sizeof(event->filename), filename_ptr); 108 | event->mode = BPF_CORE_READ(ctx, args[2]); 109 | 110 | bpf_ringbuf_submit(event, 0); 111 | return 0; 112 | } 113 | ``` 114 | 115 | 116 | 117 | ## Reference 118 | 119 | [本文来源] https://mozillazg.com/2022/05/ebpf-libbpf-tracepoint-common-questions.html 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /tracepoint/connect_accept/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /tracepoint/connect_accept/connect_accept.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | #include 5 | 6 | struct { 7 | __uint(type, BPF_MAP_TYPE_RINGBUF); 8 | __uint(max_entries, 256 * 1024); 9 | } conns SEC(".maps"); 10 | 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_HASH); 13 | __uint(max_entries, 8192); 14 | __type(key, int); 15 | __type(value, void*); 16 | } accept_addr SEC(".maps"); 17 | 18 | struct conn_event{ 19 | int pid; 20 | char addr[24]; 21 | }; 22 | 23 | // Force emitting struct event into the ELF. 24 | const struct conn_event *unused __attribute__((unused)); 25 | 26 | struct syscalls_enter_connect_args { 27 | char _[24]; 28 | struct sockaddr *addr; 29 | }; 30 | struct syscalls_enter_accept_args { 31 | char _[24]; 32 | struct sockaddr *addr; 33 | }; 34 | 35 | SEC("tp/syscalls/sys_enter_connect") 36 | int trace_enter_connect(struct syscalls_enter_connect_args *ctx) 37 | { 38 | struct conn_event *event; 39 | event = bpf_ringbuf_reserve(&conns, sizeof(*event), 0); 40 | if (!event) return 0; 41 | event->pid = bpf_get_current_pid_tgid() >> 32; 42 | if (bpf_probe_read_user(&event->addr, sizeof(event->addr), (void*)(ctx->addr))) { 43 | bpf_ringbuf_discard(event, 0); 44 | } else bpf_ringbuf_submit(event, 0); 45 | return 0; 46 | } 47 | 48 | SEC("tp/syscalls/sys_enter_accept") 49 | int trace_enter_accept(struct syscalls_enter_accept_args *ctx) 50 | { 51 | int pid = bpf_get_current_pid_tgid() >> 32; 52 | void *addr = (void*)(ctx->addr); 53 | bpf_map_update_elem(&accept_addr, &pid, &addr, BPF_ANY); 54 | return 0; 55 | } 56 | 57 | SEC("tp/syscalls/sys_exit_accept") 58 | int trace_exit_accept(void *ctx) 59 | { 60 | int pid = bpf_get_current_pid_tgid() >> 32; 61 | void **paddr = (void**)bpf_map_lookup_elem(&accept_addr, &pid); 62 | if (paddr) { 63 | bpf_map_delete_elem(&accept_addr, &pid); 64 | struct conn_event *event; 65 | event = bpf_ringbuf_reserve(&conns, sizeof(*event), 0); 66 | if (!event) return 0; 67 | event->pid = -pid; 68 | long r=bpf_probe_read_user(&(event->addr), sizeof(event->addr), *paddr); 69 | if (r) { 70 | // bpf_printk("fail to read user space value %lld\n", r); 71 | bpf_ringbuf_discard(event, 0); 72 | } else bpf_ringbuf_submit(event, 0); 73 | } 74 | return 0; 75 | } 76 | 77 | SEC("tp/syscalls/sys_enter_accept4") 78 | int trace_enter_accept4(struct syscalls_enter_accept_args *ctx) 79 | { 80 | int pid = bpf_get_current_pid_tgid() >> 32; 81 | void *addr = (void*)(ctx->addr); 82 | bpf_map_update_elem(&accept_addr, &pid, &addr, BPF_ANY); 83 | return 0; 84 | } 85 | 86 | SEC("tp/syscalls/sys_exit_accept4") 87 | int trace_exit_accept4(void *ctx) 88 | { 89 | int pid = bpf_get_current_pid_tgid() >> 32; 90 | void **paddr = (void**)bpf_map_lookup_elem(&accept_addr, &pid); 91 | if (paddr) { 92 | bpf_map_delete_elem(&accept_addr, &pid); 93 | struct conn_event *event; 94 | event = bpf_ringbuf_reserve(&conns, sizeof(*event), 0); 95 | if (!event) return 0; 96 | event->pid = -pid; 97 | long r=bpf_probe_read_user(&(event->addr), sizeof(event->addr), *paddr); 98 | if (r) { 99 | // bpf_printk("fail to read user space value %lld\n", r); 100 | bpf_ringbuf_discard(event, 0); 101 | } else bpf_ringbuf_submit(event, 0); 102 | } 103 | return 0; 104 | } 105 | 106 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /tracepoint/sys_enter_execve/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_execve/main.go: -------------------------------------------------------------------------------- 1 | // This program demonstrates attaching an eBPF program to a kernel tracepoint. 2 | // The eBPF program will be attached to the page allocation tracepoint and 3 | // prints out the number of times it has been reached. The tracepoint fields 4 | // are printed into /sys/kernel/tracing/trace_pipe. 5 | // https://mozillazg.com/2022/05/ebpf-libbpf-tracepoint-common-questions.html 6 | package main 7 | 8 | import ( 9 | "bufio" 10 | "bytes" 11 | "encoding/binary" 12 | "errors" 13 | "fmt" 14 | "github.com/cilium/ebpf/link" 15 | "github.com/cilium/ebpf/ringbuf" 16 | "github.com/cilium/ebpf/rlimit" 17 | "log" 18 | "os" 19 | "os/signal" 20 | ) 21 | 22 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 23 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf sys_enter_execve.c -- -I../../headers 24 | 25 | func main() { 26 | // Allow the current process to lock memory for eBPF resources. 27 | if err := rlimit.RemoveMemlock(); err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // Load pre-compiled programs and maps into the kernel. 32 | objs := bpfObjects{} 33 | if err := loadBpfObjects(&objs, nil); err != nil { 34 | log.Fatalf("loading objects: %v", err) 35 | } 36 | defer objs.Close() 37 | 38 | // Open a tracepoint and attach the pre-compiled program. Each time 39 | // the kernel function enters, the program will increment the execution 40 | // counter by 1. The read loop below polls this map value once per 41 | // second. 42 | // The first two arguments are taken from the following pathname: 43 | // /sys/kernel/tracing/events/syscalls/sys_enter_fchmodat 44 | kp, err := link.Tracepoint("syscalls", "sys_enter_fchmodat", objs.TracepointSyscallsSysEnterExecve, nil) 45 | if err != nil { 46 | log.Fatalf("opening tracepoint: %s", err) 47 | } 48 | defer kp.Close() 49 | 50 | log.Printf("Press Ctrl-C to exit and remove the program") 51 | log.Println("Waiting for events..") 52 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs\n") 53 | 54 | stopper := make(chan os.Signal, 1) 55 | signal.Notify(stopper, os.Interrupt) 56 | 57 | // Open a ringbuf reader from userspace RINGBUF map described in the eBPF C program. 58 | rd, err := ringbuf.NewReader(objs.Events) 59 | if err != nil { 60 | log.Fatalf("opening ringbuf reader: %s", err) 61 | } 62 | defer rd.Close() 63 | 64 | // Close the reader when the process receives a signal, which will exit 65 | // the read loop. 66 | go func() { 67 | <-stopper 68 | 69 | if err := rd.Close(); err != nil { 70 | log.Fatalf("closing ringbuf reader: %s", err) 71 | } 72 | }() 73 | 74 | log.Println("Waiting for events..") 75 | 76 | // bpfEvent is generated by bpf2go. 77 | var event Event 78 | for { 79 | record, err := rd.Read() 80 | if err != nil { 81 | if errors.Is(err, ringbuf.ErrClosed) { 82 | log.Println("Received signal, exiting..") 83 | return 84 | } 85 | log.Printf("reading from reader: %s", err) 86 | continue 87 | } 88 | 89 | // Parse the ringbuf event entry into a bpfEvent structure. 90 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 91 | log.Printf("parsing ringbuf event: %s", err) 92 | continue 93 | } 94 | 95 | log.Printf("Comm: %s ContainerId: %s\n", event.commName(), getContainerId(event.HostPpid)) 96 | } 97 | } 98 | 99 | type Event struct { 100 | HostPid uint32 101 | HostPpid uint32 102 | 103 | Comm [16]byte 104 | } 105 | 106 | func (c Event) commName() string { 107 | return string(bytes.Split(c.Comm[:], []byte("\x00"))[0]) 108 | } 109 | 110 | func getContainerId(pid uint32) string { 111 | if pid == 0 { 112 | return "" 113 | } 114 | path := fmt.Sprintf("/proc/%d/cgroup", pid) 115 | file, err := os.Open(path) 116 | if err != nil { 117 | if os.IsNotExist(err) { 118 | return "" 119 | } 120 | log.Printf("open file %s failed: %+v", path, err) 121 | return "" 122 | } 123 | defer file.Close() 124 | scanner := bufio.NewScanner(file) 125 | for scanner.Scan() { 126 | line := scanner.Text() 127 | if len(line) > 0 { 128 | return line 129 | } 130 | } 131 | return "" 132 | } 133 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_execve/sys_enter_execve.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define TASK_COMM_LEN 16 8 | 9 | struct event_t { 10 | u32 host_pid; // pid in host pid namespace 11 | u32 host_ppid; // ppid in host pid namespace 12 | 13 | char comm[TASK_COMM_LEN]; // the name of the executable (excluding the path) 14 | }; 15 | 16 | /* BPF ringbuf map */ 17 | struct { 18 | __uint(type, BPF_MAP_TYPE_RINGBUF); 19 | __uint(max_entries, 16 * 1024 /* 16 KB */); 20 | } events SEC(".maps"); 21 | 22 | SEC("tracepoint/syscalls/sys_enter_execve") 23 | int tracepoint__syscalls__sys_enter_execve( 24 | struct trace_event_raw_sys_enter *ctx) { 25 | char name[TASK_COMM_LEN]; 26 | bpf_get_current_comm(&name, sizeof(name)); 27 | 28 | struct event_t *event; 29 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 30 | if (!event) { 31 | return 0; 32 | } 33 | 34 | struct task_struct *task = (struct task_struct *)bpf_get_current_task(); 35 | 36 | event->host_pid = bpf_get_current_pid_tgid() >> 32; 37 | event->host_ppid = BPF_CORE_READ(task, real_parent, tgid); 38 | 39 | bpf_get_current_comm(&event->comm, sizeof(event->comm)); 40 | 41 | bpf_ringbuf_submit(event, 0); 42 | 43 | return 0; 44 | } 45 | 46 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /tracepoint/sys_enter_fchmodat/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_fchmodat/main.go: -------------------------------------------------------------------------------- 1 | // This program demonstrates attaching an eBPF program to a kernel tracepoint. 2 | // The eBPF program will be attached to the page allocation tracepoint and 3 | // prints out the number of times it has been reached. The tracepoint fields 4 | // are printed into /sys/kernel/tracing/trace_pipe. 5 | // https://mozillazg.com/2022/05/ebpf-libbpf-tracepoint-common-questions.html 6 | package main 7 | 8 | import ( 9 | "bytes" 10 | "encoding/binary" 11 | "errors" 12 | "log" 13 | "os" 14 | "os/signal" 15 | "strconv" 16 | 17 | "github.com/cilium/ebpf/link" 18 | "github.com/cilium/ebpf/ringbuf" 19 | "github.com/cilium/ebpf/rlimit" 20 | ) 21 | 22 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 23 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf sys_enter_fchmodat.c -- -I../../headers 24 | 25 | func main() { 26 | // Allow the current process to lock memory for eBPF resources. 27 | if err := rlimit.RemoveMemlock(); err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | // Load pre-compiled programs and maps into the kernel. 32 | objs := bpfObjects{} 33 | if err := loadBpfObjects(&objs, nil); err != nil { 34 | log.Fatalf("loading objects: %v", err) 35 | } 36 | defer objs.Close() 37 | 38 | // Open a tracepoint and attach the pre-compiled program. Each time 39 | // the kernel function enters, the program will increment the execution 40 | // counter by 1. The read loop below polls this map value once per 41 | // second. 42 | // The first two arguments are taken from the following pathname: 43 | // /sys/kernel/tracing/events/syscalls/sys_enter_fchmodat 44 | kp, err := link.Tracepoint("syscalls", "sys_enter_fchmodat", objs.TracepointSyscallsSysEnterFchmodat, nil) 45 | if err != nil { 46 | log.Fatalf("opening tracepoint: %s", err) 47 | } 48 | defer kp.Close() 49 | 50 | log.Printf("Press Ctrl-C to exit and remove the program") 51 | log.Println("Waiting for events..") 52 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs\n") 53 | 54 | stopper := make(chan os.Signal, 1) 55 | signal.Notify(stopper, os.Interrupt) 56 | 57 | // Open a ringbuf reader from userspace RINGBUF map described in the eBPF C program. 58 | rd, err := ringbuf.NewReader(objs.Events) 59 | if err != nil { 60 | log.Fatalf("opening ringbuf reader: %s", err) 61 | } 62 | defer rd.Close() 63 | 64 | // Close the reader when the process receives a signal, which will exit 65 | // the read loop. 66 | go func() { 67 | <-stopper 68 | 69 | if err := rd.Close(); err != nil { 70 | log.Fatalf("closing ringbuf reader: %s", err) 71 | } 72 | }() 73 | 74 | log.Println("Waiting for events..") 75 | 76 | // bpfEvent is generated by bpf2go. 77 | var event Event 78 | for { 79 | record, err := rd.Read() 80 | if err != nil { 81 | if errors.Is(err, ringbuf.ErrClosed) { 82 | log.Println("Received signal, exiting..") 83 | return 84 | } 85 | log.Printf("reading from reader: %s", err) 86 | continue 87 | } 88 | 89 | // Parse the ringbuf event entry into a bpfEvent structure. 90 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 91 | log.Printf("parsing ringbuf event: %s", err) 92 | continue 93 | } 94 | 95 | log.Printf("HostPid: %d HostPpid: %d Mod: %s Comm: %s FileName: %s\n", event.HostPid, event.HostPpid, event.modStr(), event.commName(), event.fileName()) 96 | } 97 | } 98 | 99 | type Event struct { 100 | HostPid uint32 101 | HostPpid uint32 102 | 103 | Mod uint32 104 | 105 | Comm [16]byte 106 | 107 | FileName [256]byte 108 | } 109 | 110 | func (c Event) commName() string { 111 | return string(bytes.Split(c.Comm[:], []byte("\x00"))[0]) 112 | } 113 | func (c Event) fileName() string { 114 | return string(bytes.Split(c.FileName[:], []byte("\x00"))[0]) 115 | } 116 | 117 | func (c Event) modStr() string { 118 | return strconv.FormatUint(uint64(c.Mod), 8) 119 | } 120 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_fchmodat/sys_enter_fchmodat.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define TASK_COMM_LEN 16 8 | #define FILE_NAME_LEN 256 9 | 10 | struct event_t { 11 | u32 host_pid; // pid in host pid namespace 12 | u32 host_ppid; // ppid in host pid namespace 13 | u32 mode; 14 | 15 | char comm[TASK_COMM_LEN]; // the name of the executable (excluding the path) 16 | char filename[FILE_NAME_LEN]; 17 | }; 18 | 19 | /* BPF ringbuf map */ 20 | struct { 21 | __uint(type, BPF_MAP_TYPE_RINGBUF); 22 | __uint(max_entries, 16 * 1024 /* 16 KB */); 23 | } events SEC(".maps"); 24 | 25 | SEC("tracepoint/syscalls/sys_enter_fchmodat") 26 | int tracepoint__syscalls__sys_enter_fchmodat(struct trace_event_raw_sys_enter *ctx) { 27 | struct event_t *event; 28 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 29 | if (!event) { 30 | return 0; 31 | } 32 | 33 | struct task_struct *task = (struct task_struct *)bpf_get_current_task(); 34 | 35 | event->host_pid = bpf_get_current_pid_tgid() >> 32; 36 | event->host_ppid = BPF_CORE_READ(task, real_parent, tgid); 37 | bpf_get_current_comm(&event->comm, sizeof(event->comm)); 38 | 39 | char *filename_ptr = (char *)BPF_CORE_READ(ctx, args[1]); 40 | bpf_core_read_user_str(&event->filename, sizeof(event->filename), filename_ptr); 41 | event->mode = BPF_CORE_READ(ctx, args[2]); 42 | 43 | bpf_ringbuf_submit(event, 0); 44 | return 0; 45 | } 46 | 47 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /tracepoint/sys_enter_openat/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_openat/main.go: -------------------------------------------------------------------------------- 1 | // 来源: https://github.com/zq-david-wang/linux-tools/blob/main/ebpf/libbpf-bootstrap/openat.bpf.c 2 | package main 3 | 4 | import ( 5 | "bytes" 6 | "encoding/binary" 7 | "errors" 8 | "log" 9 | "os" 10 | "os/signal" 11 | 12 | "github.com/cilium/ebpf/link" 13 | "github.com/cilium/ebpf/ringbuf" 14 | "github.com/cilium/ebpf/rlimit" 15 | ) 16 | 17 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 18 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf sys_enter_openat.c -- -I../../headers 19 | 20 | func main() { 21 | // Allow the current process to lock memory for eBPF resources. 22 | if err := rlimit.RemoveMemlock(); err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | // Load pre-compiled programs and maps into the kernel. 27 | objs := bpfObjects{} 28 | if err := loadBpfObjects(&objs, nil); err != nil { 29 | log.Fatalf("loading objects: %v", err) 30 | } 31 | defer objs.Close() 32 | 33 | // Open a tracepoint and attach the pre-compiled program. Each time 34 | // the kernel function enters, the program will increment the execution 35 | // counter by 1. 36 | // The first two arguments are taken from the following pathname: 37 | // /sys/kernel/tracing/events/syscalls/sys_enter_openat 38 | kp, err := link.Tracepoint("syscalls", "sys_enter_openat", objs.TracepointOpenat, nil) 39 | if err != nil { 40 | log.Fatalf("opening tracepoint: %s", err) 41 | } 42 | defer kp.Close() 43 | 44 | log.Printf("Press Ctrl-C to exit and remove the program") 45 | log.Println("Waiting for events..") 46 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs\n") 47 | 48 | stopper := make(chan os.Signal, 1) 49 | signal.Notify(stopper, os.Interrupt) 50 | 51 | // Open a ringbuf reader from userspace RINGBUF map described in the eBPF C program. 52 | rd, err := ringbuf.NewReader(objs.Events) 53 | if err != nil { 54 | log.Fatalf("opening ringbuf reader: %s", err) 55 | } 56 | defer rd.Close() 57 | 58 | // Close the reader when the process receives a signal, which will exit 59 | // the read loop. 60 | go func() { 61 | <-stopper 62 | 63 | if err := rd.Close(); err != nil { 64 | log.Fatalf("closing ringbuf reader: %s", err) 65 | } 66 | }() 67 | 68 | log.Println("Waiting for events..") 69 | 70 | // bpfEvent is generated by bpf2go. 71 | var cd cdata 72 | for { 73 | record, err := rd.Read() 74 | if err != nil { 75 | if errors.Is(err, ringbuf.ErrClosed) { 76 | log.Println("Received signal, exiting..") 77 | return 78 | } 79 | log.Printf("reading from reader: %s", err) 80 | continue 81 | } 82 | 83 | // Parse the ringbuf event entry into a bpfEvent structure. 84 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &cd); err != nil { 85 | log.Printf("parsing ringbuf event: %s", err) 86 | continue 87 | } 88 | 89 | log.Printf(` 90 | CgroupId: %d HostTid: %d HostPid: %d HostPpid: %d 91 | Tid: %d Pid: %d Ppid: %d Uid: %d Gid: %d 92 | CgroupNsId: %d IpcNsId: %d NetNsId: %d MountNsId: %d PidNsId: %d TimeNsId: %d UserNsId: %d UtsNsId: %d 93 | Comm: %s 94 | 95 | `, 96 | cd.CgroupId, cd.HostTid, cd.HostPid, cd.HostPpid, 97 | cd.Tid, cd.Pid, cd.Ppid, cd.Uid, cd.Gid, 98 | cd.CgroupNsId, cd.IpcNsId, cd.NetNsId, cd.NetNsId, cd.MountNsId, cd.TimeNsId, cd.UserNsId, cd.UtsNsId, 99 | cd.commName()) 100 | } 101 | } 102 | 103 | type cdata struct { 104 | CgroupId uint64 105 | HostTid uint32 106 | HostPid uint32 107 | HostPpid uint32 108 | 109 | Tid uint32 110 | Pid uint32 111 | Ppid uint32 112 | Uid uint32 113 | Gid uint32 114 | 115 | CgroupNsId uint32 116 | IpcNsId uint32 117 | NetNsId uint32 118 | MountNsId uint32 119 | PidNsId uint32 120 | TimeNsId uint32 121 | UserNsId uint32 122 | UtsNsId uint32 123 | 124 | Comm [16]byte 125 | } 126 | 127 | func (c cdata) commName() string { 128 | return string(bytes.TrimRight(c.Comm[:], "\x00")) 129 | } 130 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_openat/sys_enter_openat.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define TASK_COMM_LEN 16 8 | 9 | struct event_t { 10 | u64 cgroup_id; // cgroup id 11 | u32 host_tid; // tid in host pid namespace 12 | u32 host_pid; // pid in host pid namespace 13 | u32 host_ppid; // ppid in host pid namespace 14 | 15 | u32 tid; // thread id in userspace 16 | u32 pid; // process id in userspace 17 | u32 ppid; // parent process id in userspace 18 | u32 uid; 19 | u32 gid; 20 | 21 | u32 cgroup_ns_id; 22 | u32 ipc_ns_id; 23 | u32 net_ns_id; 24 | u32 mount_ns_id; 25 | u32 pid_ns_id; 26 | u32 time_ns_id; 27 | u32 user_ns_id; 28 | u32 uts_ns_id; 29 | 30 | char comm[TASK_COMM_LEN]; // the name of the executable (excluding the path) 31 | }; 32 | 33 | /* BPF ringbuf map */ 34 | struct { 35 | __uint(type, BPF_MAP_TYPE_RINGBUF); 36 | __uint(max_entries, 16 * 1024 /* 16 KB */); 37 | } events SEC(".maps"); 38 | 39 | SEC("tracepoint/syscalls/sys_enter_openat") 40 | int tracepoint_openat(struct trace_event_raw_sys_enter *ctx) { 41 | struct event_t *event; 42 | event = bpf_ringbuf_reserve(&events, sizeof(*event), 0); 43 | if (!event) { 44 | return 0; 45 | } 46 | 47 | struct task_struct *task = (struct task_struct *)bpf_get_current_task(); 48 | struct task_struct *parent_task = BPF_CORE_READ(task, real_parent); 49 | u64 tgid = bpf_get_current_pid_tgid(); 50 | u64 ugid = bpf_get_current_uid_gid(); 51 | 52 | event->cgroup_id = bpf_get_current_cgroup_id(); 53 | event->host_tid = tgid; 54 | event->host_pid = tgid >> 32; 55 | event->host_ppid = BPF_CORE_READ(parent_task, tgid); 56 | 57 | struct nsproxy *namespaceproxy = BPF_CORE_READ(task, nsproxy); 58 | struct pid_namespace *pid_ns_children = 59 | BPF_CORE_READ(namespaceproxy, pid_ns_for_children); 60 | unsigned int level = BPF_CORE_READ(pid_ns_children, level); 61 | event->tid = BPF_CORE_READ(task, thread_pid, numbers[level].nr); 62 | event->pid = 63 | BPF_CORE_READ(task, group_leader, thread_pid, numbers[level].nr); 64 | 65 | struct nsproxy *parent_namespaceproxy = BPF_CORE_READ(parent_task, nsproxy); 66 | struct pid_namespace *parent_pid_ns_children = 67 | BPF_CORE_READ(parent_namespaceproxy, pid_ns_for_children); 68 | unsigned int parent_level = BPF_CORE_READ(parent_pid_ns_children, level); 69 | event->ppid = BPF_CORE_READ(parent_task, group_leader, thread_pid, 70 | numbers[parent_level].nr); 71 | 72 | event->uid = ugid; 73 | event->gid = ugid >> 32; 74 | 75 | event->cgroup_ns_id = BPF_CORE_READ(namespaceproxy, cgroup_ns, ns.inum); 76 | event->ipc_ns_id = BPF_CORE_READ(namespaceproxy, ipc_ns, ns.inum); 77 | event->net_ns_id = BPF_CORE_READ(namespaceproxy, net_ns, ns.inum); 78 | event->mount_ns_id = BPF_CORE_READ(namespaceproxy, mnt_ns, ns.inum); 79 | event->pid_ns_id = 80 | BPF_CORE_READ(namespaceproxy, pid_ns_for_children, ns.inum); 81 | event->time_ns_id = BPF_CORE_READ(namespaceproxy, time_ns, ns.inum); 82 | event->user_ns_id = BPF_CORE_READ(namespaceproxy, cgroup_ns, ns.inum); 83 | event->uts_ns_id = BPF_CORE_READ(namespaceproxy, cgroup_ns, ns.inum); 84 | 85 | bpf_get_current_comm(&event->comm, sizeof(event->comm)); 86 | 87 | bpf_ringbuf_submit(event, 0); 88 | 89 | return 0; 90 | } 91 | 92 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /tracepoint/sys_enter_openat_asm/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_openat_asm/gen.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 4 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf sys_enter_openat.c -- -I../../headers 5 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_openat_asm/main.go: -------------------------------------------------------------------------------- 1 | // This program demonstrates how to attach an eBPF program to a tracepoint. 2 | // The program is attached to the syscall/sys_enter_openat tracepoint and 3 | // prints out the integer 123 every time the syscall is entered. 4 | package main 5 | 6 | import ( 7 | "errors" 8 | "log" 9 | "os" 10 | "os/signal" 11 | "syscall" 12 | 13 | "github.com/cilium/ebpf" 14 | "github.com/cilium/ebpf/asm" 15 | "github.com/cilium/ebpf/link" 16 | "github.com/cilium/ebpf/perf" 17 | "github.com/cilium/ebpf/rlimit" 18 | ) 19 | 20 | // Metadata for the eBPF program used in this example. 21 | var progSpec = &ebpf.ProgramSpec{ 22 | Name: "my_trace_prog", // non-unique name, will appear in `bpftool prog list` while attached 23 | Type: ebpf.TracePoint, // only TracePoint programs can be attached to trace events created by link.Tracepoint() 24 | License: "GPL", // license must be GPL for calling kernel helpers like perf_event_output 25 | } 26 | 27 | func main() { 28 | 29 | // Subscribe to signals for terminating the program. 30 | stopper := make(chan os.Signal, 1) 31 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 32 | 33 | // Allow the current process to lock memory for eBPF resources. 34 | if err := rlimit.RemoveMemlock(); err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | // Create a perf event array for the kernel to write perf records to. 39 | // These records will be read by userspace below. 40 | events, err := ebpf.NewMap(&ebpf.MapSpec{ 41 | Type: ebpf.PerfEventArray, 42 | Name: "my_perf_array", 43 | }) 44 | if err != nil { 45 | log.Fatalf("creating perf event array: %s", err) 46 | } 47 | defer events.Close() 48 | 49 | // Open a perf reader from userspace into the perf event array 50 | // created earlier. 51 | rd, err := perf.NewReader(events, os.Getpagesize()) 52 | if err != nil { 53 | log.Fatalf("creating event reader: %s", err) 54 | } 55 | defer rd.Close() 56 | 57 | // Close the reader when the process receives a signal, which will exit 58 | // the read loop. 59 | go func() { 60 | <-stopper 61 | rd.Close() 62 | }() 63 | 64 | /* 65 | root@zcw:/home/work/ebpf_labs/tracepoint/sys_enter_openat_asm# clang \ 66 | -target bpf \ 67 | -I../../headers \ 68 | -g \ 69 | -O2 -c sys_enter_openat.c 70 | root@zcw:/home/work/ebpf_labs/tracepoint/sys_enter_openat_asm# llvm-objdump -S sys_enter_openat.o 71 | 72 | sys_enter_openat.o: file format elf64-bpf 73 | 74 | Disassembly of section tracepoint/syscalls/sys_enter_openat: 75 | 76 | 0000000000000000 : 77 | ; int tracepoint_openat(struct trace_event_raw_sys_enter *ctx) { 78 | 0: b7 02 00 00 7b 00 00 00 r2 = 123 79 | ; u64 msg = 123; 80 | 1: 7b 2a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r2 81 | 2: bf a4 00 00 00 00 00 00 r4 = r10 82 | 3: 07 04 00 00 f8 ff ff ff r4 += -8 83 | ; bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &msg, sizeof(int)); 84 | 4: 18 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r2 = 0 ll 85 | 6: 18 03 00 00 ff ff ff ff 00 00 00 00 00 00 00 00 r3 = 4294967295 ll 86 | 8: b7 05 00 00 04 00 00 00 r5 = 4 87 | 9: 85 00 00 00 19 00 00 00 call 25 88 | ; return 0; 89 | 10: b7 00 00 00 00 00 00 00 r0 = 0 90 | 11: 95 00 00 00 00 00 00 00 exit 91 | root@zcw:/home/work/ebpf_labs/tracepoint/sys_enter_openat_asm# 92 | */ 93 | 94 | // Minimal program that writes the static value '123' to the perf ring on 95 | // each event. Note that this program refers to the file descriptor of 96 | // the perf event array created above, which needs to be created prior to the 97 | // program being verified by and inserted into the kernel. 98 | progSpec.Instructions = asm.Instructions{ 99 | // store the integer 123 at FP[-8] 100 | asm.Mov.Imm(asm.R2, 123), 101 | asm.StoreMem(asm.RFP, -8, asm.R2, asm.Word), 102 | 103 | // load registers with arguments for call of FnPerfEventOutput 104 | asm.LoadMapPtr(asm.R2, events.FD()), // file descriptor of the perf event array 105 | asm.LoadImm(asm.R3, 0xffffffff, asm.DWord), 106 | asm.Mov.Reg(asm.R4, asm.RFP), 107 | asm.Add.Imm(asm.R4, -8), 108 | asm.Mov.Imm(asm.R5, 4), 109 | 110 | // call FnPerfEventOutput, an eBPF kernel helper 111 | asm.FnPerfEventOutput.Call(), 112 | 113 | // set exit code to 0 114 | asm.Mov.Imm(asm.R0, 0), 115 | asm.Return(), 116 | } 117 | 118 | // Instantiate and insert the program into the kernel. 119 | prog, err := ebpf.NewProgram(progSpec) 120 | if err != nil { 121 | log.Fatalf("creating ebpf program: %s", err) 122 | } 123 | defer prog.Close() 124 | 125 | // Open a trace event based on a pre-existing kernel hook (tracepoint). 126 | // Each time a userspace program uses the 'openat()' syscall, the eBPF 127 | // program specified above will be executed and a '123' value will appear 128 | // in the perf ring. 129 | tp, err := link.Tracepoint("syscalls", "sys_enter_openat", prog, nil) 130 | if err != nil { 131 | log.Fatalf("opening tracepoint: %s", err) 132 | } 133 | defer tp.Close() 134 | 135 | log.Println("Waiting for events..") 136 | 137 | for { 138 | record, err := rd.Read() 139 | if err != nil { 140 | if errors.Is(err, perf.ErrClosed) { 141 | log.Println("Received signal, exiting..") 142 | return 143 | } 144 | log.Printf("reading from reader: %s", err) 145 | continue 146 | } 147 | 148 | log.Println("Record:", record) 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_openat_asm/main_bak.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/cilium/ebpf/rlimit" 6 | "log" 7 | "os" 8 | "os/signal" 9 | ) 10 | 11 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 12 | // 13 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf sys_enter_openat.c -- -I../../headers 14 | func main() { 15 | // Allow the current process to lock memory for eBPF resources. 16 | if err := rlimit.RemoveMemlock(); err != nil { 17 | log.Fatal(err) 18 | } 19 | 20 | spec, err := loadBpf() 21 | if err != nil { 22 | log.Fatalf("load bpf: %v", err) 23 | } 24 | fmt.Printf("spec:%+v", spec.Programs["tracepoint_openat"]) 25 | 26 | stopper := make(chan os.Signal, 1) 27 | signal.Notify(stopper, os.Interrupt) 28 | 29 | <-stopper 30 | 31 | } 32 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_openat_asm/main_bak2.go: -------------------------------------------------------------------------------- 1 | // This program demonstrates how to attach an eBPF program to a tracepoint. 2 | // The program is attached to the syscall/sys_enter_openat tracepoint and 3 | // prints out the integer 123 every time the syscall is entered. 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "log" 9 | "os" 10 | "os/signal" 11 | "syscall" 12 | 13 | "github.com/cilium/ebpf" 14 | "github.com/cilium/ebpf/asm" 15 | "github.com/cilium/ebpf/perf" 16 | "github.com/cilium/ebpf/rlimit" 17 | ) 18 | 19 | // Metadata for the eBPF program used in this example. 20 | var progSpec = &ebpf.ProgramSpec{ 21 | Name: "my_trace_prog", // non-unique name, will appear in `bpftool prog list` while attached 22 | Type: ebpf.TracePoint, // only TracePoint programs can be attached to trace events created by link.Tracepoint() 23 | License: "GPL", // license must be GPL for calling kernel helpers like perf_event_output 24 | } 25 | 26 | func main() { 27 | 28 | // Subscribe to signals for terminating the program. 29 | stopper := make(chan os.Signal, 1) 30 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 31 | 32 | // Allow the current process to lock memory for eBPF resources. 33 | if err := rlimit.RemoveMemlock(); err != nil { 34 | log.Fatal(err) 35 | } 36 | 37 | // Create a perf event array for the kernel to write perf records to. 38 | // These records will be read by userspace below. 39 | events, err := ebpf.NewMap(&ebpf.MapSpec{ 40 | Type: ebpf.PerfEventArray, 41 | Name: "my_perf_array", 42 | }) 43 | if err != nil { 44 | log.Fatalf("creating perf event array: %s", err) 45 | } 46 | defer events.Close() 47 | 48 | // Open a perf reader from userspace into the perf event array 49 | // created earlier. 50 | rd, err := perf.NewReader(events, os.Getpagesize()) 51 | if err != nil { 52 | log.Fatalf("creating event reader: %s", err) 53 | } 54 | defer rd.Close() 55 | 56 | // Close the reader when the process receives a signal, which will exit 57 | // the read loop. 58 | go func() { 59 | <-stopper 60 | rd.Close() 61 | }() 62 | 63 | // Minimal program that writes the static value '123' to the perf ring on 64 | // each event. Note that this program refers to the file descriptor of 65 | // the perf event array created above, which needs to be created prior to the 66 | // program being verified by and inserted into the kernel. 67 | progSpec.Instructions = asm.Instructions{ 68 | // store the integer 123 at FP[-8] 69 | asm.Mov.Imm(asm.R2, 123), 70 | asm.StoreMem(asm.RFP, -8, asm.R2, asm.Word), 71 | 72 | // load registers with arguments for call of FnPerfEventOutput 73 | asm.LoadMapPtr(asm.R2, events.FD()), // file descriptor of the perf event array 74 | asm.LoadImm(asm.R3, 0xffffffff, asm.DWord), 75 | asm.Mov.Reg(asm.R4, asm.RFP), 76 | asm.Add.Imm(asm.R4, -8), 77 | asm.Mov.Imm(asm.R5, 4), 78 | 79 | // call FnPerfEventOutput, an eBPF kernel helper 80 | asm.FnPerfEventOutput.Call(), 81 | 82 | // set exit code to 0 83 | asm.Mov.Imm(asm.R0, 0), 84 | asm.Return(), 85 | } 86 | 87 | fmt.Printf("spec:%+v\n", progSpec.Instructions) 88 | // Instantiate and insert the program into the kernel. 89 | prog, err := ebpf.NewProgram(progSpec) 90 | if err != nil { 91 | log.Fatalf("creating ebpf program: %s", err) 92 | } 93 | defer prog.Close() 94 | fmt.Printf("spec:%+v", progSpec.Instructions) 95 | <-stopper 96 | } 97 | 98 | /* 99 | 汇编和加载ebpf字节码的对比: 100 | 101 | 汇编 102 | Instructions: 103 | 0: MovImm dst: r2 imm: 123 104 | 1: StXMemW dst: rfp src: r2 off: -8 imm: 0 105 | 2: LoadMapPtr dst: r2 fd: 3 106 | 4: LdImmDW dst: r3 imm: 4294967295 107 | 6: MovReg dst: r4 src: rfp 108 | 7: AddImm dst: r4 imm: -8 109 | 8: MovImm dst: r5 imm: 4 110 | 9: Call FnPerfEventOutput 111 | 10: MovImm dst: r0 imm: 0 112 | 11: Exit 113 | Flags:0 License:GPL KernelVersion:0 ByteOrder:} 114 | 115 | ebpf字节码 116 | Instructions: 117 | ; int tracepoint_openat(struct trace_event_raw_sys_enter *ctx) { 118 | 0: MovImm dst: r2 imm: 123 119 | ; u64 msg = 123; 120 | 1: StXMemDW dst: rfp src: r2 off: -8 imm: 0 121 | 2: MovReg dst: r4 src: rfp 122 | 3: AddImm dst: r4 imm: -8 123 | ; bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &msg, 4); 124 | 4: LoadMapPtr dst: r2 fd: 0 125 | 6: LdImmDW dst: r3 imm: 4294967295 126 | 8: MovImm dst: r5 imm: 4 127 | 9: Call FnPerfEventOutput 128 | ; return 0; 129 | 10: MovImm dst: r0 imm: 0 130 | 11: Exit 131 | Flags:0 License:GPL KernelVersion:0 ByteOrder:LittleEndian} 132 | */ 133 | -------------------------------------------------------------------------------- /tracepoint/sys_enter_openat_asm/sys_enter_openat.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct { 8 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 9 | } events SEC(".maps"); 10 | 11 | SEC("tracepoint/syscalls/sys_enter_openat") 12 | int tracepoint_openat(struct trace_event_raw_sys_enter *ctx) { 13 | u64 msg = 123; 14 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &msg, sizeof(u64)); 15 | return 0; 16 | } 17 | 18 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /uprobe/README.md: -------------------------------------------------------------------------------- 1 | # Uprobe 2 | 3 | > 用户态跟踪 4 | 5 | 用户态空间跟踪使用 **uprobe** 和 **用户空间定义的静态跟踪点**(User Statically Defined Tracing,简称 USDT)。 6 | 7 | 和内核一个问题,要想进行跟踪需要先找到跟踪点,一般来说,我们找跟踪点是从二进制文件中查找。在有调试信息的情况下,就可以通过 [readdlf](https://man7.org/linux/man-pages/man1/readelf.1.html) 、[objdump](https://man7.org/linux/man-pages/man1/objdump.1.html) 、[nm](https://man7.org/linux/man-pages/man1/nm.1.html) 等工具查询可用于跟踪的函数、变量等符号列表。比如查询加解密的 [openssl 动态库](https://cppget.org/libssl) 8 | 9 | ```bash 10 | # 查询符号表(RHEL8系统中请把动态库路径替换为/usr/lib64/libssl.so) 11 | readelf -Ws /usr/lib/x86_64-linux-gnu/libssl.so 12 | 13 | # 查询USDT信息(USDT信息位于ELF文件的notes段) 14 | readelf -n /usr/lib/x86_64-linux-gnu/libssl.so 15 | ``` 16 | 17 | 或者使用 bpftrace 工具。 18 | 19 | ```bash 20 | # 查询uprobe(RHEL8系统中请把动态库路径替换为/usr/lib64/libssl.so) 21 | bpftrace -l 'uprobe:/usr/lib/x86_64-linux-gnu/libssl.so:*' 22 | 23 | # 查询USDT 24 | bpftrace -l 'usdt:/usr/lib/x86_64-linux-gnu/libssl.so:*' 25 | ``` 26 | 27 | ## 编程语言对追踪的影响 28 | 29 | 常用的编程语言按照运行原理,大致可以分为三类; 30 | 31 | - 第一类是 C、C++、Golang 等编译为机器码后再执行的**编译型语言**。这类语言通常会被编译成 ELF 格式的二进制文件,包含了保存在寄存器或栈中的函数参数和返回值,因此可以直接通过二进制文件中的符号进行跟踪。 32 | - 第二类是 Python、Bash、Ruby 等通过解释器语法分析之后的**解释型语言**。这类编程语言开发的程序,无法直接从语言运行时的二进制文件中获取应用程序的调试信息,通常需要跟踪解释器的函数,再从其参数中获取应用程序的运行细节。 33 | - 第三类是 Java、.Net、JavaScript 等先编译为字节码,再有即时编译器(JIT)编译为机器码执行的**即时编译型语言**。同解释型语言类似,这类编程语言无法直接从语言运行的二进制文件中获取应用程序的调试信息,跟踪 JIT 编程语言开发的程序最为困难,因为 JIT 编译的状态只存在于内存中。通常需要一个 [map-agent](https://github.com/jvm-profiling-tools/perf-map-agent) 的东西 34 | 35 | 36 | 37 | ## Reference 38 | 39 | https://kiosk007.top/post/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8ebpf%E8%BF%9B%E8%A1%8C%E8%BF%BD%E8%B8%AA/#%E4%BD%BF%E7%94%A8-libbpf-%E6%96%B9%E6%B3%95%E8%BF%9B%E8%A1%8C%E8%B7%9F%E8%B8%AA 40 | 41 | https://www.hi-roy.com/posts/ebpf%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B03/ 42 | 43 | https://www.zhihu.com/column/c_1477442442325012480 -------------------------------------------------------------------------------- /uprobe/bash_retval/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /uprobe/bash_retval/bash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define TASK_COMM_LEN 16 6 | #define MAX_DATA_SIZE_BASH 256 7 | 8 | struct event { 9 | u32 pid; 10 | u32 uid; 11 | u8 line[MAX_DATA_SIZE_BASH]; 12 | u32 retval; 13 | char comm[TASK_COMM_LEN]; 14 | }; 15 | 16 | struct { 17 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 18 | __uint(key_size, sizeof(u32)); 19 | __uint(value_size, sizeof(u32)); 20 | __uint(max_entries, 1024); 21 | } events SEC(".maps"); 22 | 23 | struct { 24 | __uint(type, BPF_MAP_TYPE_HASH); 25 | __type(key, u32); 26 | __type(value, struct event); 27 | __uint(max_entries, 1024); 28 | } events_t SEC(".maps"); 29 | 30 | // Force emitting struct event into the ELF. 31 | const struct event *unused __attribute__((unused)); 32 | 33 | SEC("uretprobe/bash_readline") 34 | int uretprobe_bash_readline(struct pt_regs *ctx) { 35 | u64 pid_tgid = bpf_get_current_pid_tgid(); 36 | u32 pid = pid_tgid >> 32; 37 | u64 current_uid_gid = bpf_get_current_uid_gid(); 38 | u32 uid = current_uid_gid; 39 | 40 | struct event event = {}; 41 | event.pid = pid; 42 | event.uid = uid; 43 | bpf_probe_read_user(&event.line, sizeof(event.line), (void *)PT_REGS_RC(ctx)); 44 | bpf_get_current_comm(&event.comm, sizeof(event.comm)); 45 | bpf_map_update_elem(&events_t, &pid, &event, BPF_ANY); 46 | 47 | return 0; 48 | } 49 | SEC("uretprobe/bash_retval") 50 | int uretprobe_bash_retval(struct pt_regs *ctx) { 51 | u64 pid_tgid = bpf_get_current_pid_tgid(); 52 | u32 pid = pid_tgid >> 32; 53 | u64 current_uid_gid = bpf_get_current_uid_gid(); 54 | u32 uid = current_uid_gid; 55 | int retval = (int)PT_REGS_RC(ctx); 56 | 57 | struct event *event_p = bpf_map_lookup_elem(&events_t, &pid); 58 | 59 | if (event_p) { 60 | event_p->retval = retval; 61 | bpf_map_delete_elem(&events_t, &pid); 62 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, event_p, sizeof(struct event)); 63 | } 64 | return 0; 65 | } 66 | 67 | char LICENSE[] SEC("license") = "GPL"; 68 | -------------------------------------------------------------------------------- /uprobe/bash_retval/main.go: -------------------------------------------------------------------------------- 1 | // This program demonstrates how to attach an eBPF program to a uretprobe. 2 | // The program will be attached to the 'readline' symbol in the binary '/bin/bash' and print out 3 | // the line which 'readline' functions returns to the caller. 4 | 5 | //go:build amd64 6 | 7 | package main 8 | 9 | import ( 10 | "bytes" 11 | "encoding/binary" 12 | "errors" 13 | "log" 14 | "os" 15 | "os/signal" 16 | "syscall" 17 | 18 | "github.com/cilium/ebpf/link" 19 | "github.com/cilium/ebpf/perf" 20 | "github.com/cilium/ebpf/rlimit" 21 | "golang.org/x/sys/unix" 22 | ) 23 | 24 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target native -type event bpf bash.c -- -I../../headers 25 | 26 | const ( 27 | // The path to the ELF binary containing the function to trace. 28 | // On some distributions, the 'readline' function is provided by a 29 | // dynamically-linked library, so the path of the library will need 30 | // to be specified instead, e.g. /usr/lib/libreadline.so.8. 31 | // Use `ldd /bin/bash` to find these paths. 32 | binPath = "/bin/bash" 33 | symbol = "readline" 34 | ) 35 | 36 | func main() { 37 | stopper := make(chan os.Signal, 1) 38 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 39 | 40 | // Allow the current process to lock memory for eBPF resources. 41 | if err := rlimit.RemoveMemlock(); err != nil { 42 | log.Fatal(err) 43 | } 44 | 45 | // Load pre-compiled programs and maps into the kernel. 46 | objs := bpfObjects{} 47 | if err := loadBpfObjects(&objs, nil); err != nil { 48 | log.Fatalf("loading objects: %s", err) 49 | } 50 | defer objs.Close() 51 | 52 | // Open an ELF binary and read its symbols. 53 | ex, err := link.OpenExecutable(binPath) 54 | if err != nil { 55 | log.Fatalf("opening executable: %s", err) 56 | } 57 | 58 | // Open a Uretprobe at the exit point of the symbol and attach 59 | // the pre-compiled eBPF program to it. 60 | up, err := ex.Uretprobe(symbol, objs.UretprobeBashReadline, nil) 61 | if err != nil { 62 | log.Fatalf("creating uretprobe: %s", err) 63 | } 64 | defer up.Close() 65 | 66 | up2, err := ex.Uretprobe(symbol, objs.UretprobeBashRetval, nil) 67 | if err != nil { 68 | log.Fatalf("creating uretprobe: %s", err) 69 | } 70 | defer up2.Close() 71 | 72 | // Open a perf event reader from userspace on the PERF_EVENT_ARRAY map 73 | // described in the eBPF C program. 74 | rd, err := perf.NewReader(objs.Events, os.Getpagesize()) 75 | if err != nil { 76 | log.Fatalf("creating perf event reader: %s", err) 77 | } 78 | defer rd.Close() 79 | 80 | go func() { 81 | // Wait for a signal and close the perf reader, 82 | // which will interrupt rd.Read() and make the program exit. 83 | <-stopper 84 | log.Println("Received signal, exiting program..") 85 | 86 | if err := rd.Close(); err != nil { 87 | log.Fatalf("closing perf event reader: %s", err) 88 | } 89 | }() 90 | 91 | log.Printf("Listening for events..") 92 | 93 | // bpfEvent is generated by bpf2go. 94 | var event bpfEvent 95 | for { 96 | record, err := rd.Read() 97 | if err != nil { 98 | if errors.Is(err, perf.ErrClosed) { 99 | return 100 | } 101 | log.Printf("reading from perf event reader: %s", err) 102 | continue 103 | } 104 | 105 | if record.LostSamples != 0 { 106 | log.Printf("perf event ring buffer full, dropped %d samples", record.LostSamples) 107 | continue 108 | } 109 | 110 | // Parse the perf event entry into a bpfEvent structure. 111 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 112 | log.Printf("parsing perf event: %s", err) 113 | continue 114 | } 115 | 116 | log.Printf("PID:%d, UID:%d, \tComm:%s, \tRetvalue:%d, \tLine:\n%s", event.Pid, event.Uid, event.Comm, event.Retval, unix.ByteSliceToString((event.Line[:]))) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /uprobe/postgres_query/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /uprobe/postgres_query/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "flag" 8 | "log" 9 | "os" 10 | "os/signal" 11 | "syscall" 12 | 13 | "github.com/cilium/ebpf/link" 14 | "github.com/cilium/ebpf/perf" 15 | "github.com/cilium/ebpf/rlimit" 16 | "golang.org/x/sys/unix" 17 | ) 18 | 19 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target native -type data_t bpf postgres_query.c -- -I../../headers 20 | 21 | var ( 22 | binPath = "/usr/lib/postgresql/12/bin/postgres" 23 | symbol = "pg_parse_query" 24 | ) 25 | 26 | func init() { 27 | flag.StringVar(&binPath, "p", "/usr/lib/postgresql/12/bin/postgres", "a network interface name") 28 | } 29 | 30 | func main() { 31 | stopper := make(chan os.Signal, 1) 32 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 33 | 34 | // Allow the current process to lock memory for eBPF resources. 35 | if err := rlimit.RemoveMemlock(); err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | // Load pre-compiled programs and maps into the kernel. 40 | objs := bpfObjects{} 41 | if err := loadBpfObjects(&objs, nil); err != nil { 42 | log.Fatalf("loading objects: %s", err) 43 | } 44 | defer objs.Close() 45 | 46 | // Open an ELF binary and read its symbols. 47 | ex, err := link.OpenExecutable(binPath) 48 | if err != nil { 49 | log.Fatalf("opening executable: %s", err) 50 | } 51 | 52 | // Open a Uprobe at the exit point of the symbol and attach 53 | // the pre-compiled eBPF program to it. 54 | up, err := ex.Uprobe(symbol, objs.PostgresQuery, nil) 55 | if err != nil { 56 | log.Fatalf("creating Uprobe: %s", err) 57 | } 58 | defer up.Close() 59 | 60 | // Open a perf event reader from userspace on the PERF_EVENT_ARRAY map 61 | // described in the eBPF C program. 62 | rd, err := perf.NewReader(objs.Events, os.Getpagesize()) 63 | if err != nil { 64 | log.Fatalf("creating perf event reader: %s", err) 65 | } 66 | defer rd.Close() 67 | 68 | go func() { 69 | // Wait for a signal and close the perf reader, 70 | // which will interrupt rd.Read() and make the program exit. 71 | <-stopper 72 | log.Println("Received signal, exiting program..") 73 | 74 | if err := rd.Close(); err != nil { 75 | log.Fatalf("closing perf event reader: %s", err) 76 | } 77 | }() 78 | 79 | log.Printf("Listening for events..") 80 | 81 | log.Printf("%-7s %s\n", "PID", "Query") 82 | 83 | // bpfEvent is generated by bpf2go. 84 | var event bpfDataT 85 | for { 86 | record, err := rd.Read() 87 | if err != nil { 88 | if errors.Is(err, perf.ErrClosed) { 89 | return 90 | } 91 | log.Printf("reading from perf event reader: %s", err) 92 | continue 93 | } 94 | 95 | if record.LostSamples != 0 { 96 | log.Printf("perf event ring buffer full, dropped %d samples", record.LostSamples) 97 | continue 98 | } 99 | 100 | // Parse the perf event entry into a bpfEvent structure. 101 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 102 | log.Printf("parsing perf event: %s", err) 103 | continue 104 | } 105 | 106 | log.Printf("%-7d %s\n", event.Pid, unix.ByteSliceToString(event.Query[:])) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /uprobe/postgres_query/postgres_query.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define TASK_COMM_LEN 16 8 | #define MAX_DATA_SIZE_POSTGRES 256 9 | 10 | struct data_t { 11 | u64 pid; 12 | u64 timestamp; 13 | char query[MAX_DATA_SIZE_POSTGRES]; 14 | char comm[TASK_COMM_LEN]; 15 | }; 16 | 17 | struct { 18 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 19 | __uint(key_size, sizeof(u32)); 20 | __uint(value_size, sizeof(u32)); 21 | __uint(max_entries, 1024); 22 | } events SEC(".maps"); 23 | 24 | // Force emitting struct event into the ELF. 25 | const struct data_t *unused __attribute__((unused)); 26 | 27 | // https://github.com/postgres/postgres/blob/7b7ed046cb2ad9f6efac90380757d5977f0f563f/src/backend/tcop/postgres.c#L987-L992 28 | // hook function exec_simple_query 29 | // versions 10 - now 30 | // static void exec_simple_query(const char *query_string) 31 | SEC("uprobe/pg_parse_query") 32 | int postgres_query(struct pt_regs *ctx) { 33 | u64 current_pid_tgid = bpf_get_current_pid_tgid(); 34 | u32 pid = current_pid_tgid >> 32; 35 | 36 | struct data_t data = {}; 37 | data.pid = pid; // only process id 38 | data.timestamp = bpf_ktime_get_ns(); 39 | 40 | char *sql_string = (char *)PT_REGS_PARM1(ctx); 41 | bpf_get_current_comm(&data.comm, sizeof(data.comm)); 42 | bpf_probe_read_user(&data.query, sizeof(data.query), sql_string); 43 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(data)); 44 | return 0; 45 | } 46 | 47 | char LICENSE[] SEC("license") = "GPL"; 48 | -------------------------------------------------------------------------------- /uprobe/readline/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go 20 | -------------------------------------------------------------------------------- /uprobe/readline/main.go: -------------------------------------------------------------------------------- 1 | // This program demonstrates how to attach an eBPF program to a uretprobe. 2 | // The program will be attached to the 'readline' symbol in the binary '/bin/bash' and print out 3 | // the line which 'readline' functions returns to the caller. 4 | // https://zhuanlan.zhihu.com/p/572680301 5 | // https://github.com/mozillazg/libbpfgo-tools/blob/master/tools/bashreadline/main.go 6 | 7 | //go:build amd64 8 | 9 | package main 10 | 11 | import ( 12 | "bytes" 13 | "encoding/binary" 14 | "errors" 15 | "log" 16 | "os" 17 | "os/signal" 18 | "syscall" 19 | 20 | "github.com/cilium/ebpf/link" 21 | "github.com/cilium/ebpf/perf" 22 | "github.com/cilium/ebpf/rlimit" 23 | "golang.org/x/sys/unix" 24 | ) 25 | 26 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target native -type str_t bpf readline.c -- -I../../headers 27 | 28 | const ( 29 | // The path to the ELF binary containing the function to trace. 30 | // On some distributions, the 'readline' function is provided by a 31 | // dynamically-linked library, so the path of the library will need 32 | // to be specified instead, e.g. /usr/lib/libreadline.so.8. 33 | // Use `ldd /bin/bash` to find these paths. 34 | binPath = "/bin/bash" 35 | symbol = "readline" 36 | ) 37 | 38 | func main() { 39 | stopper := make(chan os.Signal, 1) 40 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 41 | 42 | // Allow the current process to lock memory for eBPF resources. 43 | if err := rlimit.RemoveMemlock(); err != nil { 44 | log.Fatal(err) 45 | } 46 | 47 | // Load pre-compiled programs and maps into the kernel. 48 | objs := bpfObjects{} 49 | if err := loadBpfObjects(&objs, nil); err != nil { 50 | log.Fatalf("loading objects: %s", err) 51 | } 52 | defer objs.Close() 53 | 54 | // Open an ELF binary and read its symbols. 55 | ex, err := link.OpenExecutable(binPath) 56 | if err != nil { 57 | log.Fatalf("opening executable: %s", err) 58 | } 59 | 60 | // Open a Uretprobe at the exit point of the symbol and attach 61 | // the pre-compiled eBPF program to it. 62 | up, err := ex.Uretprobe(symbol, objs.Printret, nil) 63 | if err != nil { 64 | log.Fatalf("creating uretprobe: %s", err) 65 | } 66 | defer up.Close() 67 | 68 | // Open a perf event reader from userspace on the PERF_EVENT_ARRAY map 69 | // described in the eBPF C program. 70 | rd, err := perf.NewReader(objs.Events, os.Getpagesize()) 71 | if err != nil { 72 | log.Fatalf("creating perf event reader: %s", err) 73 | } 74 | defer rd.Close() 75 | 76 | go func() { 77 | // Wait for a signal and close the perf reader, 78 | // which will interrupt rd.Read() and make the program exit. 79 | <-stopper 80 | log.Println("Received signal, exiting program..") 81 | 82 | if err := rd.Close(); err != nil { 83 | log.Fatalf("closing perf event reader: %s", err) 84 | } 85 | }() 86 | 87 | log.Printf("Listening for events..") 88 | 89 | log.Printf("%-7s %s\n", "PID", "COMMAND") 90 | 91 | // bpfEvent is generated by bpf2go. 92 | var event bpfStrT 93 | for { 94 | record, err := rd.Read() 95 | if err != nil { 96 | if errors.Is(err, perf.ErrClosed) { 97 | return 98 | } 99 | log.Printf("reading from perf event reader: %s", err) 100 | continue 101 | } 102 | 103 | if record.LostSamples != 0 { 104 | log.Printf("perf event ring buffer full, dropped %d samples", record.LostSamples) 105 | continue 106 | } 107 | 108 | // Parse the perf event entry into a bpfEvent structure. 109 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 110 | log.Printf("parsing perf event: %s", err) 111 | continue 112 | } 113 | 114 | log.Printf("%-7d %s\n", event.Pid, unix.ByteSliceToString(event.Str[:])) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /uprobe/readline/readline.c: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* Copyright (c) 2021 Facebook */ 3 | #include 4 | #include 5 | #include 6 | 7 | #define TASK_COMM_LEN 16 8 | #define MAX_LINE_SIZE 80 9 | 10 | struct str_t { 11 | __u32 pid; 12 | char str[MAX_LINE_SIZE]; 13 | }; 14 | 15 | // Force emitting struct event into the ELF. 16 | const struct str_t *unused __attribute__((unused)); 17 | 18 | struct { 19 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 20 | __uint(key_size, sizeof(__u32)); 21 | __uint(value_size, sizeof(__u32)); 22 | } events SEC(".maps"); 23 | 24 | SEC("uretprobe/readline") 25 | int BPF_KRETPROBE(printret, const void *ret) { 26 | struct str_t data; 27 | char comm[TASK_COMM_LEN]; 28 | u32 pid; 29 | 30 | if (!ret) 31 | return 0; 32 | 33 | bpf_get_current_comm(&comm, sizeof(comm)); 34 | if (comm[0] != 'b' || comm[1] != 'a' || comm[2] != 's' || comm[3] != 'h' || comm[4] != 0 ) 35 | return 0; 36 | 37 | pid = bpf_get_current_pid_tgid() >> 32; 38 | data.pid = pid; 39 | bpf_probe_read_user_str(&data.str, sizeof(data.str), ret); 40 | 41 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(data)); 42 | 43 | return 0; 44 | }; 45 | 46 | char LICENSE[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /xdp/dns_cache_server/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror -D DEBUG $(CFLAGS) 8 | 9 | # The network card name 10 | NET=lo 11 | 12 | format: 13 | find . -type f -name "*.c" | xargs clang-format -i 14 | 15 | # $BPF_CLANG is used in go:generate invocations. 16 | gen: export BPF_CLANG := $(CLANG) 17 | gen: export BPF_CFLAGS := $(CFLAGS) 18 | gen: 19 | go generate ./... 20 | 21 | run: 22 | go run -exec sudo main.go bpf_bpfel.go -n $(NET) 23 | -------------------------------------------------------------------------------- /xdp/ipv4_firewall_v1/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go -n lo 20 | -------------------------------------------------------------------------------- /xdp/ipv4_firewall_v1/ipv4_firewall.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | #include "bpf_endian.h" 3 | #include "common.h" 4 | #include "protocol_hdr.h" 5 | 6 | struct { 7 | __uint(type, BPF_MAP_TYPE_HASH); 8 | __uint(max_entries, 1024); 9 | __type(key, __u32); 10 | __type(value, __u8); 11 | } rules SEC(".maps"); 12 | 13 | SEC("xdp_ipv4_firewall") 14 | int ipv4_firewall_func(struct xdp_md *ctx) { 15 | void *data_end = (void *)(unsigned long)ctx->data_end; 16 | void *data = (void *)(unsigned long)ctx->data; 17 | __u32 sip = 0; 18 | __u8 *value = NULL; 19 | 20 | // 边界检查:检查数据包是否大于完整的以太网 + ip 标头 21 | if (data + sizeof(struct eth_hdr) + sizeof(struct ip_hdr) > data_end) { 22 | return XDP_PASS; 23 | } 24 | 25 | struct eth_hdr *eth = data; 26 | 27 | // 如果以太网协议不是基于 IP 的,则忽略数据包 28 | if (eth->h_proto != bpf_htons(ETH_P_IP)) { 29 | return XDP_PASS; 30 | } 31 | struct ip_hdr *ip = data + sizeof(*eth); 32 | 33 | sip = ip->s_addr; 34 | 35 | value = bpf_map_lookup_elem(&rules, &sip); // 判断攻击源是否 在黑名单中 36 | if (value) { 37 | if (*value) { 38 | // drop 39 | bpf_printk("intercept source ip %x\n", sip); 40 | return XDP_DROP; 41 | } 42 | 43 | return XDP_PASS; 44 | } 45 | 46 | return XDP_PASS; 47 | } 48 | 49 | char __license[] SEC("license") = "Dual MIT/GPL"; 50 | -------------------------------------------------------------------------------- /xdp/ipv4_firewall_v1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/binary" 6 | "encoding/json" 7 | "flag" 8 | "fmt" 9 | "github.com/cilium/ebpf/link" 10 | "log" 11 | "net" 12 | "net/http" 13 | "os" 14 | "os/signal" 15 | "strconv" 16 | "time" 17 | ) 18 | 19 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 20 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf ipv4_firewall.c -- -I../../headers 21 | 22 | var ( 23 | InterfaceName string 24 | Ip string 25 | Port int 26 | ) 27 | 28 | func init() { 29 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 30 | flag.StringVar(&Ip, "i", "0.0.0.0", "server ip") 31 | flag.IntVar(&Port, "p", 8080, "server port") 32 | } 33 | 34 | func main() { 35 | flag.Parse() 36 | 37 | if len(InterfaceName) == 0 { 38 | log.Fatalf("Please specify a network interface") 39 | } 40 | // Look up the network interface by name. 41 | iface, err := net.InterfaceByName(InterfaceName) 42 | if err != nil { 43 | log.Fatalf("lookup network iface %s: %s", InterfaceName, err) 44 | } 45 | 46 | // Load pre-compiled programs into the kernel. 47 | objs := bpfObjects{} 48 | if err := loadBpfObjects(&objs, nil); err != nil { 49 | log.Fatalf("loading objects: %s", err) 50 | } 51 | defer objs.Close() 52 | 53 | // Attach the program. 54 | l, err := link.AttachXDP(link.XDPOptions{ 55 | Program: objs.Ipv4FirewallFunc, 56 | Interface: iface.Index, 57 | }) 58 | if err != nil { 59 | log.Fatalf("could not attach XDP program: %s", err) 60 | } 61 | defer l.Close() 62 | 63 | mu := http.NewServeMux() 64 | mu.Handle("/add", AddIPV4Handler(objs)) 65 | 66 | server := http.Server{ 67 | Addr: fmt.Sprintf("%s:%d", Ip, Port), 68 | Handler: mu, 69 | } 70 | log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index) 71 | log.Printf("Listening and serving HTTP on %s:%d", Ip, Port) 72 | log.Printf("Press Ctrl-C to exit and remove the program") 73 | 74 | go func() { 75 | if err := server.ListenAndServe(); err != nil { 76 | log.Println("server start failed") 77 | } 78 | }() 79 | 80 | stopper := make(chan os.Signal, 1) 81 | signal.Notify(stopper, os.Interrupt) 82 | 83 | <-stopper 84 | 85 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 86 | defer cancel() 87 | 88 | if err := server.Shutdown(ctx); err != nil { 89 | log.Println("server shutdown failed") 90 | } 91 | } 92 | 93 | func AddIPV4Handler(objs bpfObjects) http.Handler { 94 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 95 | ipAddr := req.URL.Query().Get("ip") 96 | interceptStr := req.URL.Query().Get("inter") 97 | 98 | intercept, err := strconv.ParseInt(interceptStr, 10, 10) 99 | if err != nil { 100 | http.Error(w, err.Error(), http.StatusInternalServerError) 101 | return 102 | } 103 | 104 | data := map[string]string{ 105 | "add_ip": ipAddr, 106 | "msg": "success", 107 | } 108 | 109 | jsonBytes, err := json.Marshal(data) 110 | if err != nil { 111 | http.Error(w, err.Error(), http.StatusInternalServerError) 112 | return 113 | } 114 | 115 | w.Header().Set("Content-Type", "application/json") 116 | 117 | ip := net.ParseIP(ipAddr).To4() 118 | if ip == nil { 119 | log.Printf("ip addrs %s format error \n", ipAddr) 120 | jsonBytes, _ := json.Marshal(map[string]string{ 121 | "msg": ipAddr + " ip format error", 122 | }) 123 | w.Write(jsonBytes) 124 | return 125 | } 126 | 127 | // []byte 类型转换为uint32 128 | ipUint32 := binary.LittleEndian.Uint32(ip) 129 | 130 | if intercept == 0 { 131 | err = objs.Rules.Delete(ipUint32) 132 | if err != nil { 133 | log.Printf("delete ip address map rules error %s \n", err) 134 | jsonBytes, _ := json.Marshal(map[string]string{ 135 | "msg": fmt.Sprintf("add ip address %s to map rules error %s", ipAddr, err), 136 | }) 137 | w.Write(jsonBytes) 138 | return 139 | } 140 | } else { 141 | err = objs.Rules.Put(ipUint32, uint8(intercept)) 142 | if err != nil { 143 | log.Printf("add ip address map rules error %s \n", err) 144 | jsonBytes, _ := json.Marshal(map[string]string{ 145 | "msg": fmt.Sprintf("add ip address %s to map rules error %s", ipAddr, err), 146 | }) 147 | w.Write(jsonBytes) 148 | return 149 | } 150 | } 151 | 152 | var res uint8 153 | objs.Rules.Lookup(ipUint32, &res) 154 | log.Printf("add ip address %s, ip to uint32 value %d,is intercept %d", ipAddr, ipUint32, res) 155 | 156 | w.Write(jsonBytes) 157 | return 158 | }) 159 | } 160 | -------------------------------------------------------------------------------- /xdp/ipv4_firewall_v2/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go ip_protocol.go -n lo 20 | -------------------------------------------------------------------------------- /xdp/ipv4_firewall_v2/ip_protocol.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type IPProtocol uint8 4 | 5 | const ( 6 | IPProtocolICMP IPProtocol = 1 7 | IPProtocolTCP IPProtocol = 6 8 | IPProtocolUDP IPProtocol = 17 9 | IPProtocolIPv6 IPProtocol = 41 10 | IPProtocolGRE IPProtocol = 47 11 | ) 12 | 13 | var IPProtocolMap = map[IPProtocol]string{ 14 | IPProtocolICMP: "icmp", 15 | IPProtocolTCP: "tcp", 16 | IPProtocolUDP: "udp", 17 | IPProtocolIPv6: "ipv6", 18 | IPProtocolGRE: "gre", 19 | } 20 | -------------------------------------------------------------------------------- /xdp/ipv4_firewall_v2/ipv4_firewall.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | #include "bpf_endian.h" 3 | #include "common.h" 4 | #include "protocol_hdr.h" 5 | 6 | struct { 7 | __uint(type, BPF_MAP_TYPE_HASH); 8 | __uint(max_entries, 1024); 9 | __type(key, __u32); 10 | __type(value, __u8); 11 | } rules SEC(".maps"); 12 | 13 | struct { 14 | __uint(type, BPF_MAP_TYPE_RINGBUF); 15 | __uint(max_entries, 256 * 1024 /* 256 KB */); 16 | } rb SEC(".maps"); 17 | 18 | struct event { 19 | __u8 protocol; 20 | __u8 flag; // 流量是否拦截 0未拦截 1 已拦截 21 | __u32 s_addr; 22 | __u32 d_addr; 23 | __u32 ingress_ifindex; /* rxq->dev->ifindex */ 24 | }; 25 | 26 | // 上传流量 27 | static int send_data(struct ip_hdr *iph, __u8 flag, 28 | __u32 ingress_ifindex /* rxq->dev->ifindex */) { 29 | 30 | struct event *e; 31 | // 必需步骤 判断是否有足够空间 32 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 33 | if (!e) { 34 | return 0; 35 | } 36 | 37 | e->ingress_ifindex = ingress_ifindex; 38 | e->protocol = iph->protocol; 39 | e->s_addr = iph->s_addr; 40 | e->d_addr = iph->d_addr; 41 | e->flag = flag; 42 | 43 | // 写入数据 44 | bpf_ringbuf_submit(e, 0); 45 | 46 | return 0; 47 | } 48 | 49 | SEC("xdp_ipv4_firewall") 50 | int ipv4_firewall_func(struct xdp_md *ctx) { 51 | void *data_end = (void *)(unsigned long)ctx->data_end; 52 | void *data = (void *)(unsigned long)ctx->data; 53 | __u32 sip = 0; 54 | __u8 *value = NULL; 55 | 56 | // 边界检查:检查数据包是否大于完整的以太网 + ip 标头 57 | if (data + sizeof(struct eth_hdr) + sizeof(struct ip_hdr) > data_end) { 58 | return XDP_PASS; 59 | } 60 | 61 | struct eth_hdr *eth = data; 62 | 63 | // 如果以太网协议不是基于 IP 的,则忽略数据包 64 | if (eth->h_proto != bpf_htons(ETH_P_IP)) { 65 | return XDP_PASS; 66 | } 67 | struct ip_hdr *iph = data + sizeof(*eth); 68 | 69 | sip = iph->s_addr; 70 | 71 | value = bpf_map_lookup_elem(&rules, &sip); // 判断攻击源是否 在黑名单中 72 | if (value) { 73 | if (*value) { 74 | // drop 75 | bpf_printk("intercept source ip %d\n", sip); 76 | send_data(iph, 1, ctx->ingress_ifindex); 77 | return XDP_DROP; 78 | } 79 | return XDP_PASS; 80 | } 81 | 82 | return XDP_PASS; 83 | } 84 | 85 | char __license[] SEC("license") = "Dual MIT/GPL"; 86 | -------------------------------------------------------------------------------- /xdp/lb4/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | NIC=lo 10 | IMAGE=ghcr.io/cilium/ebpf-builder:1694533004 11 | CURRENT_DIR := $(shell pwd) 12 | PARENT_DIR := $(abspath $(CURRENT_DIR)/..) 13 | GRANDPARENT_DIR := $(abspath $(PARENT_DIR)/..) 14 | 15 | format: 16 | find . -type f -name "*.c" | xargs clang-format -i 17 | 18 | env: 19 | go env -w GOPROXY=https://goproxy.cn,direct 20 | 21 | # $BPF_CLANG is used in go:generate invocations. 22 | gen: export BPF_CLANG := $(CLANG) 23 | gen: export BPF_CFLAGS := $(CFLAGS) 24 | gen: env 25 | go generate ./... 26 | 27 | run: 28 | go run -exec sudo main.go bpf_bpfel.go -n $(NIC) 29 | 30 | build: 31 | docker run --rm -v $(GRANDPARENT_DIR):/root/ebpf_labs $(IMAGE) bash -c "cd /root/ebpf_labs/xdp/lb4 && make gen" -------------------------------------------------------------------------------- /xdp/lb4/README.md: -------------------------------------------------------------------------------- 1 | # Lb4 2 | 通过xdp实现L4负载均衡 3 | 4 | ## generate 5 | 执行`make gen`,通过bpf2go编译xdp程序,生成golang相关问文件。 6 | ```bash 7 | $ cd ebpf/xdp/lb4 && make build 8 | ``` 9 | 10 | ## run 11 | 12 | > 保证docker当前没有运行任何容器,因为lb4.c中的所有IP地址为硬编码(数字为IP地址最后一位) 13 | > 14 | > ```c 15 | > #define BACKEND_A 2 16 | > #define BACKEND_B 3 17 | > #define CLIENT 4 18 | > #define LB 5 19 | > ``` 20 | 21 | Terminal1:backend-A 22 | 23 | ```bash 24 | $ docker run -d --rm --name backend-A -h backend-A --env TERM=xterm-color nginxdemos/hello:plain-text 25 | ``` 26 | 27 | Terminal1:backend-B 28 | 29 | ```bash 30 | $ docker run -d --rm --name backend-B -h backend-B --env TERM=xterm-color nginxdemos/hello:plain-text 31 | ``` 32 | 33 | Terminal2:client 34 | 35 | ```bash 36 | $ docker run -itd --name client -h client --env TERM=xterm-color ubuntu:22.04 sh 37 | ``` 38 | 39 | Terminal3:lb4 40 | 41 | ```bash 42 | $ docker run -itd --name lb4 -h lb4 --privileged --env TERM=xterm-color ubuntu:22.04 sh 43 | ``` 44 | 45 | Attach xdp 46 | 47 | ```bash 48 | $ nsenter -t $(docker inspect -f {{.State.Pid}} lb4) -n 49 | 50 | $ make run NIC=eth0 51 | ``` 52 | 53 | curl 54 | 55 | ```bash 56 | $ nsenter -t $(docker inspect -f {{.State.Pid}} client) -n 57 | 58 | $ curl 172.17.0.5 59 | ``` 60 | 61 | ## reference 62 | 63 | https://github.com/lizrice/lb-from-scratch 64 | -------------------------------------------------------------------------------- /xdp/lb4/lb4.c: -------------------------------------------------------------------------------- 1 | #include "lb4.h" 2 | 3 | #define IP_ADDRESS(x) (unsigned int)(172 + (17 << 8) + (0 << 16) + (x << 24)) 4 | 5 | #define BACKEND_A 2 6 | #define BACKEND_B 3 7 | #define CLIENT 4 8 | #define LB 5 9 | 10 | SEC("xdp_lb") 11 | int xdp_load_balancer(struct xdp_md *ctx) 12 | { 13 | void *data = (void *)(long)ctx->data; 14 | void *data_end = (void *)(long)ctx->data_end; 15 | 16 | bpf_printk("got something"); 17 | 18 | struct ethhdr *eth = data; 19 | if (data + sizeof(struct ethhdr) > data_end) 20 | return XDP_ABORTED; 21 | 22 | if (bpf_ntohs(eth->h_proto) != ETH_P_IP) 23 | return XDP_PASS; 24 | 25 | struct iphdr *iph = data + sizeof(struct ethhdr); 26 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) 27 | return XDP_ABORTED; 28 | 29 | if (iph->protocol != IPPROTO_TCP) 30 | return XDP_PASS; 31 | 32 | bpf_printk("Got TCP packet from %x", iph->saddr); 33 | 34 | if (iph->saddr == IP_ADDRESS(CLIENT)) 35 | { 36 | char be = BACKEND_A; 37 | if (bpf_ktime_get_ns() % 2) 38 | be = BACKEND_B; 39 | 40 | iph->daddr = IP_ADDRESS(be); 41 | eth->h_dest[5] = be; 42 | } 43 | else 44 | { 45 | iph->daddr = IP_ADDRESS(CLIENT); 46 | eth->h_dest[5] = CLIENT; 47 | } 48 | iph->saddr = IP_ADDRESS(LB); 49 | eth->h_source[5] = LB; 50 | 51 | iph->check = iph_csum(iph); 52 | 53 | return XDP_TX; 54 | } 55 | 56 | char _license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /xdp/lb4/lb4.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static __always_inline __u16 10 | csum_fold_helper(__u64 csum) 11 | { 12 | int i; 13 | #pragma unroll 14 | for (i = 0; i < 4; i++) 15 | { 16 | if (csum >> 16) 17 | csum = (csum & 0xffff) + (csum >> 16); 18 | } 19 | return ~csum; 20 | } 21 | 22 | static __always_inline __u16 23 | iph_csum(struct iphdr *iph) 24 | { 25 | iph->check = 0; 26 | unsigned long long csum = bpf_csum_diff(0, 0, (unsigned int *)iph, sizeof(struct iphdr), 0); 27 | return csum_fold_helper(csum); 28 | } -------------------------------------------------------------------------------- /xdp/lb4/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "flag" 7 | "github.com/cilium/ebpf/link" 8 | "io" 9 | "log" 10 | "net" 11 | "os" 12 | "os/signal" 13 | "syscall" 14 | ) 15 | 16 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 17 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf printk_pass.c -- -I../../headers -Ilb4.h 18 | 19 | var ( 20 | InterfaceName string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 25 | } 26 | 27 | func main() { 28 | flag.Parse() 29 | 30 | if len(InterfaceName) == 0 { 31 | log.Fatalf("Please specify a network interface") 32 | } 33 | // Look up the network interface by name. 34 | iface, err := net.InterfaceByName(InterfaceName) 35 | if err != nil { 36 | log.Fatalf("lookup network iface %s: %s", InterfaceName, err) 37 | } 38 | 39 | // Load pre-compiled programs into the kernel. 40 | objs := bpfObjects{} 41 | if err := loadBpfObjects(&objs, nil); err != nil { 42 | log.Fatalf("loading objects: %s", err) 43 | } 44 | defer objs.Close() 45 | 46 | // Attach the program. 47 | l, err := link.AttachXDP(link.XDPOptions{ 48 | Program: objs.XdpLoadBalancer, 49 | Interface: iface.Index, 50 | }) 51 | if err != nil { 52 | log.Fatalf("could not attach XDP program: %s", err) 53 | } 54 | defer l.Close() 55 | 56 | log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index) 57 | log.Printf("Press Ctrl-C to exit and remove the program") 58 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs\n") 59 | 60 | // Wait for a signal and close the XDP program, 61 | stopper := make(chan os.Signal, 1) 62 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 63 | 64 | cxt, cancel := context.WithCancel(context.Background()) 65 | go func(cxt context.Context) { 66 | f, err := os.Open("/sys/kernel/debug/tracing/trace_pipe") 67 | if err != nil { 68 | log.Panicf("open file failed, %v", err) 69 | } 70 | defer f.Close() 71 | 72 | r := bufio.NewReader(f) 73 | for { 74 | select { 75 | case <-cxt.Done(): 76 | return 77 | default: 78 | // ReadLine is a low-level line-reading primitive. 79 | // Most callers should use ReadBytes('\n') or ReadString('\n') instead or use a Scanner. 80 | bytes, _, err := r.ReadLine() 81 | if err == io.EOF { 82 | break 83 | } 84 | if err != nil { 85 | panic(err) 86 | } 87 | log.Println(string(bytes)) 88 | } 89 | } 90 | }(cxt) 91 | 92 | <-stopper 93 | cancel() 94 | log.Println("Received signal, exiting XDP program..") 95 | } 96 | -------------------------------------------------------------------------------- /xdp/packets_record/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go -n lo -o xdp_pass 20 | -------------------------------------------------------------------------------- /xdp/packets_record/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/cilium/ebpf" 6 | "github.com/cilium/ebpf/link" 7 | "log" 8 | "net" 9 | "time" 10 | ) 11 | 12 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 13 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf packets_record.c -- -I../../headers 14 | 15 | var ( 16 | InterfaceName string 17 | Option string 18 | ) 19 | 20 | func init() { 21 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 22 | flag.StringVar(&Option, "o", "xdp_pass", "xdp option xdp_pass|xdp_drop|xdp_abort") 23 | } 24 | 25 | func main() { 26 | flag.Parse() 27 | 28 | if len(InterfaceName) == 0 || len(Option) == 0 { 29 | log.Fatalf("Please specify a network interface and xdp option") 30 | } 31 | // Look up the network interface by name. 32 | iface, err := net.InterfaceByName(InterfaceName) 33 | if err != nil { 34 | log.Fatalf("lookup network iface %s: %s", InterfaceName, err) 35 | } 36 | 37 | // Load pre-compiled programs into the kernel. 38 | objs := bpfObjects{} 39 | if err := loadBpfObjects(&objs, nil); err != nil { 40 | log.Fatalf("loading objects: %s", err) 41 | } 42 | defer objs.Close() 43 | 44 | // Choose differently xdp option 45 | var xdpFunc *ebpf.Program 46 | var OptionKey uint32 47 | 48 | switch Option { 49 | case "xdp_pass": 50 | xdpFunc = objs.XdpPassFunc 51 | OptionKey = 2 52 | case "xdp_drop": 53 | xdpFunc = objs.XdpDropFunc 54 | OptionKey = 1 55 | case "xdp_abort": 56 | xdpFunc = objs.XdpAbortFunc 57 | OptionKey = 0 58 | default: 59 | log.Fatalf("xdp option error, only support xdp_pass|xdp_drop|xdp_abort") 60 | } 61 | 62 | // Attach the program. 63 | l, err := link.AttachXDP(link.XDPOptions{ 64 | Program: xdpFunc, 65 | Interface: iface.Index, 66 | }) 67 | if err != nil { 68 | log.Fatalf("could not attach XDP program: %s", err) 69 | } 70 | defer l.Close() 71 | 72 | log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index) 73 | log.Printf("Press Ctrl-C to exit and remove the program") 74 | 75 | ticker := time.NewTicker(1 * time.Second) 76 | defer ticker.Stop() 77 | log.Println("Waiting for events..") 78 | for range ticker.C { 79 | var dataRecs []bpfDataRec 80 | if err := objs.XdpStatsMap.Lookup(OptionKey, &dataRecs); err != nil { 81 | log.Fatalf("reading map: %v", err) 82 | } 83 | 84 | var rxPackets uint64 85 | var rxBytes uint64 86 | 87 | for _, v := range dataRecs { 88 | rxPackets += v.RxPackets 89 | rxBytes += v.RxBytes 90 | } 91 | 92 | log.Printf("Option %s CUP CORE %d RxPackets %d RxBytes %d\n", Option, len(dataRecs), rxPackets, rxBytes) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /xdp/packets_record/packets_record.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | #include "bpf_endian.h" 3 | #include "common.h" 4 | #include "protocol_hdr.h" 5 | 6 | #ifndef XDP_ACTION_MAX 7 | #define XDP_ACTION_MAX (XDP_REDIRECT + 1) 8 | #endif 9 | 10 | /* This is the data record stored in the map */ 11 | struct data_rec { 12 | __u64 rx_packets; 13 | __u64 rx_bytes; 14 | }; 15 | 16 | struct { 17 | __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 18 | __uint(max_entries, XDP_ACTION_MAX); 19 | __type(key, __u32); // source IPv4 address 20 | __type(value, struct data_rec); // packet count 21 | } xdp_stats_map SEC(".maps"); 22 | 23 | /* LLVM maps __sync_fetch_and_add() as a built-in function to the BPF atomic add 24 | * instruction (that is BPF_STX | BPF_XADD | BPF_W for word sizes) 25 | */ 26 | #ifndef lock_xadd 27 | #define lock_xadd(ptr, val) ((void)__sync_fetch_and_add(ptr, val)) 28 | #endif 29 | 30 | static __always_inline __u32 xdp_stats_record_action(struct xdp_md *ctx, 31 | __u32 action) { 32 | void *data_end = (void *)(long)ctx->data_end; 33 | void *data = (void *)(long)ctx->data; 34 | 35 | if (action >= XDP_ACTION_MAX) 36 | return XDP_ABORTED; 37 | 38 | /* Lookup in kernel BPF-side return pointer to actual data record */ 39 | struct data_rec *rec = bpf_map_lookup_elem(&xdp_stats_map, &action); 40 | if (!rec) 41 | return XDP_ABORTED; 42 | 43 | /* Calculate packet length */ 44 | __u64 bytes = data_end - data; 45 | 46 | /* BPF_MAP_TYPE_PERCPU_ARRAY returns a data record specific to current 47 | * CPU and XDP hooks runs under Softirq, which makes it safe to update 48 | * without atomic operations. 49 | */ 50 | rec->rx_packets++; 51 | rec->rx_bytes += bytes; 52 | 53 | return action; 54 | } 55 | 56 | SEC("xdp_pass") 57 | int xdp_pass_func(struct xdp_md *ctx) { 58 | __u32 action = XDP_PASS; /* XDP_PASS = 2 */ 59 | 60 | return xdp_stats_record_action(ctx, action); 61 | } 62 | 63 | SEC("xdp_drop") 64 | int xdp_drop_func(struct xdp_md *ctx) { 65 | __u32 action = XDP_DROP; 66 | 67 | return xdp_stats_record_action(ctx, action); 68 | } 69 | 70 | SEC("xdp_abort") 71 | int xdp_abort_func(struct xdp_md *ctx) { 72 | __u32 action = XDP_ABORTED; 73 | 74 | return xdp_stats_record_action(ctx, action); 75 | } 76 | 77 | char __license[] SEC("license") = "Dual MIT/GPL"; 78 | 79 | /* Copied from: $KERNEL/include/uapi/linux/bpf.h 80 | * 81 | * User return codes for XDP prog type. 82 | * A valid XDP program must return one of these defined values. All other 83 | * return codes are reserved for future use. Unknown return codes will 84 | * result in packet drops and a warning via bpf_warn_invalid_xdp_action(). 85 | * 86 | enum xdp_action { 87 | XDP_ABORTED = 0, 88 | XDP_DROP, 89 | XDP_PASS, 90 | XDP_TX, 91 | XDP_REDIRECT, 92 | }; 93 | 94 | * user accessible metadata for XDP packet hook 95 | * new fields must be added to the end of this structure 96 | * 97 | struct xdp_md { 98 | // (Note: type __u32 is NOT the real-type) 99 | __u32 data; 100 | __u32 data_end; 101 | __u32 data_meta; 102 | // Below access go through struct xdp_rxq_info 103 | __u32 ingress_ifindex; // rxq->dev->ifindex 104 | __u32 rx_queue_index; // rxq->queue_index 105 | }; 106 | */ 107 | -------------------------------------------------------------------------------- /xdp/parse_eth/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go -n lo 20 | -------------------------------------------------------------------------------- /xdp/parse_eth/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/cilium/ebpf/link" 6 | "log" 7 | "net" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | ) 12 | 13 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 14 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf parse_eth.c -- -I../../headers 15 | 16 | var ( 17 | InterfaceName string 18 | ) 19 | 20 | func init() { 21 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 22 | } 23 | 24 | func main() { 25 | flag.Parse() 26 | 27 | if len(InterfaceName) == 0 { 28 | log.Fatalf("Please specify a network interface") 29 | } 30 | // Look up the network interface by name. 31 | iface, err := net.InterfaceByName(InterfaceName) 32 | if err != nil { 33 | log.Fatalf("lookup network iface %q: %s", InterfaceName, err) 34 | } 35 | 36 | // Load pre-compiled programs into the kernel. 37 | objs := bpfObjects{} 38 | if err := loadBpfObjects(&objs, nil); err != nil { 39 | log.Fatalf("loading objects: %s", err) 40 | } 41 | defer objs.Close() 42 | 43 | // Attach the program. 44 | l, err := link.AttachXDP(link.XDPOptions{ 45 | Program: objs.ParseEthFunc, 46 | Interface: iface.Index, 47 | }) 48 | if err != nil { 49 | log.Fatalf("could not attach XDP program: %s", err) 50 | } 51 | defer l.Close() 52 | 53 | log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index) 54 | log.Printf("Press Ctrl-C to exit and remove the program") 55 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs\n") 56 | 57 | // Wait for a signal and close the XDP program, 58 | stopper := make(chan os.Signal, 1) 59 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 60 | 61 | <-stopper 62 | log.Println("Received signal, exiting XDP program..") 63 | } 64 | -------------------------------------------------------------------------------- /xdp/parse_eth/parse_eth.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "common.h" 9 | #include "bpf_endian.h" 10 | 11 | // VLAN 最大深度 12 | #ifndef VLAN_MAX_DEPTH 13 | #define VLAN_MAX_DEPTH 2 14 | #endif 15 | 16 | // 虚拟局域网标识符 17 | #define VLAN_VID_MASK 0x0fff 18 | 19 | // 通过parse_ethhdr_vlan解析后收集 VLAN 的结构 20 | struct collect_vlans { 21 | __u16 id[VLAN_MAX_DEPTH]; 22 | }; 23 | 24 | // 用于跟踪当前解析位置 25 | struct hdr_cursor { 26 | void *pos; 27 | }; 28 | 29 | /* 30 | * struct vlan_hdr - vlan header 31 | * @h_vlan_TCI: priority and VLAN ID 32 | * @h_vlan_encapsulated_proto: packet type ID or len 33 | */ 34 | struct vlan_hdr { 35 | __be16 h_vlan_TCI; 36 | __be16 h_vlan_encapsulated_proto; 37 | }; 38 | 39 | static __always_inline int proto_is_vlan(__u16 h_proto) { 40 | return !!(h_proto == bpf_htons(ETH_P_8021Q) || h_proto == bpf_htons(ETH_P_8021AD)); 41 | } 42 | 43 | // 请注意,parse_ethhdr() 将通过推进 nh->pos 并返回下一个标头 EtherType 来跳过 VLAN 标记,但提供的 ethhdr 指针仍然指向以太网标头。 44 | // 因此,呼叫者可以查看 eth->h_proto,以查看这是否是 VLAN 标记的数据包。 45 | static __always_inline int parse_ethhdr_vlan(struct hdr_cursor *nh, // 当前解析位置 46 | void *data_end, // 数据末尾 47 | struct ethhdr **eth, 48 | struct collect_vlans *vlans) { 49 | 50 | struct ethhdr *eth_tmp = nh->pos; // 赋值 51 | int hdr_size = sizeof(*eth_tmp); // ethhdr 占用大小 52 | struct vlan_hdr *vlh; 53 | __u16 h_proto; 54 | int i; 55 | 56 | // 字节计数边界检查;检查当前指针 + 标题大小是否在data_end之后 57 | if (nh->pos + hdr_size > data_end) 58 | return -1; 59 | 60 | nh->pos += hdr_size; // 更新解析位置 61 | *eth = eth_tmp; 62 | vlh = nh->pos; 63 | h_proto = eth_tmp->h_proto; 64 | 65 | // 使用循环展开来避免对循环的验证程序限制;支持多达 VLAN_MAX_DEPTH 层的 VLAN 封装。 66 | #pragma unroll 67 | for (i = 0; i < VLAN_MAX_DEPTH; i++) { 68 | if (!proto_is_vlan(h_proto)) 69 | break; 70 | 71 | if ((void *)(vlh + 1) > data_end) 72 | break; 73 | 74 | h_proto = vlh->h_vlan_encapsulated_proto; 75 | if (vlans) /* collect VLAN ids */ 76 | vlans->id[i] = (bpf_ntohs(vlh->h_vlan_TCI) & VLAN_VID_MASK); 77 | 78 | vlh++; 79 | } 80 | 81 | nh->pos = vlh; // 更新解析位置 82 | return h_proto; // 网络字节顺序 协议ID 83 | } 84 | 85 | static __always_inline int parse_ethhdr(struct hdr_cursor *nh, 86 | void *data_end, 87 | struct ethhdr **ethhdr) { 88 | 89 | // 期望编译器删除收集 VLAN ID 的代码 90 | return parse_ethhdr_vlan(nh, data_end, ethhdr, NULL); 91 | } 92 | 93 | SEC("xdp_parse_eth") 94 | int parse_eth_func(struct xdp_md *ctx) { 95 | void *data_end = (void *)(long)ctx->data_end; 96 | void *data = (void *)(long)ctx->data; 97 | 98 | int eth_type; 99 | struct ethhdr *eth; 100 | struct hdr_cursor nh = { .pos = data }; 101 | 102 | eth_type = parse_ethhdr(&nh, data_end, ð); 103 | if (eth_type < 0) { 104 | goto out; 105 | } 106 | bpf_printk("------------ETH---------------"); 107 | bpf_printk("[ETH] h_dest %s", eth->h_dest); 108 | bpf_printk("[ETH] h_source %s", eth->h_source); 109 | bpf_printk("[ETH] h_proto %d", bpf_htons(eth->h_proto)); 110 | 111 | if (eth_type == bpf_htons(ETH_P_IP)) { 112 | bpf_printk("------------IP---------------"); 113 | } else if (eth_type == bpf_htons(ETH_P_IPV6)) { 114 | bpf_printk("------------IP6---------------"); 115 | } 116 | 117 | out: 118 | return XDP_PASS; 119 | } 120 | 121 | char __license[] SEC("license") = "Dual MIT/GPL"; 122 | 123 | -------------------------------------------------------------------------------- /xdp/parse_icmp/parse_icmp.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | #include "common.h" 3 | #include "bpf_endian.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #ifndef XDP_ACTION_MAX 16 | #define XDP_ACTION_MAX (XDP_REDIRECT + 1) 17 | #endif 18 | 19 | /* Header cursor to keep track of current parsing position */ 20 | struct hdr_cursor { 21 | void *pos; 22 | }; 23 | 24 | static __always_inline int parse_icmp6hdr(struct hdr_cursor *nh, 25 | void *data_end, 26 | struct icmp6hdr **icmp6hdr) 27 | { 28 | struct icmp6hdr *icmp6h = nh->pos; 29 | 30 | if ((void *)(icmp6h + 1) > data_end) 31 | return -1; 32 | 33 | nh->pos = icmp6h + 1; 34 | *icmp6hdr = icmp6h; 35 | 36 | return icmp6h->icmp6_type; 37 | } 38 | 39 | static __always_inline int parse_icmphdr(struct hdr_cursor *nh, 40 | void *data_end, 41 | struct icmphdr **icmphdr) 42 | { 43 | struct icmphdr *icmph = nh->pos; 44 | 45 | if ((void *)(icmph + 1) > data_end) 46 | return -1; 47 | 48 | nh->pos = icmph + 1; 49 | *icmphdr = icmph; 50 | 51 | return icmph->type; 52 | } 53 | 54 | static __always_inline int parse_icmphdr_common(struct hdr_cursor *nh, 55 | void *data_end, 56 | struct icmphdr_common **icmphdr) 57 | { 58 | struct icmphdr_common *h = nh->pos; 59 | 60 | if ((void *)(h + 1) > data_end) 61 | return -1; 62 | 63 | nh->pos = h + 1; 64 | *icmphdr = h; 65 | 66 | return h->type; 67 | } 68 | -------------------------------------------------------------------------------- /xdp/parse_ipv4/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go lo 20 | -------------------------------------------------------------------------------- /xdp/parse_ipv4/main.go: -------------------------------------------------------------------------------- 1 | // This program demonstrates attaching an eBPF program to a network interface 2 | // with XDP (eXpress Data Path). The program parses the IPv4 source address 3 | // from packets and writes the packet count by IP to an LRU hash map. 4 | // The userspace program (Go code in this file) prints the contents 5 | // of the map to stdout every second. 6 | // It is possible to modify the XDP program to drop or redirect packets 7 | // as well -- give it a try! 8 | // This example depends on bpf_link, available in Linux kernel version 5.7 or newer. 9 | package main 10 | 11 | import ( 12 | "flag" 13 | "fmt" 14 | "log" 15 | "net" 16 | "strings" 17 | "time" 18 | 19 | "github.com/cilium/ebpf" 20 | "github.com/cilium/ebpf/link" 21 | ) 22 | 23 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 24 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf parse_ipv4.c -- -I../../headers 25 | 26 | var ( 27 | InterfaceName string 28 | ) 29 | 30 | func init() { 31 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 32 | } 33 | 34 | func main() { 35 | flag.Parse() 36 | 37 | if len(InterfaceName) == 0 { 38 | log.Fatalf("Please specify a network interface") 39 | } 40 | // Look up the network interface by name. 41 | iface, err := net.InterfaceByName(InterfaceName) 42 | if err != nil { 43 | log.Fatalf("lookup network iface %q: %s", InterfaceName, err) 44 | } 45 | 46 | // Load pre-compiled programs into the kernel. 47 | objs := bpfObjects{} 48 | if err := loadBpfObjects(&objs, nil); err != nil { 49 | log.Fatalf("loading objects: %s", err) 50 | } 51 | defer objs.Close() 52 | 53 | // Attach the program. 54 | l, err := link.AttachXDP(link.XDPOptions{ 55 | Program: objs.ParseIpv4Func, 56 | Interface: iface.Index, 57 | }) 58 | if err != nil { 59 | log.Fatalf("could not attach XDP program: %s", err) 60 | } 61 | defer l.Close() 62 | 63 | log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index) 64 | log.Printf("Press Ctrl-C to exit and remove the program") 65 | 66 | // Print the contents of the BPF hash map (source IP address -> packet count). 67 | ticker := time.NewTicker(1 * time.Second) 68 | defer ticker.Stop() 69 | for range ticker.C { 70 | s, err := formatMapContents(objs.IpStatsMap) 71 | if err != nil { 72 | log.Printf("Error reading map: %s", err) 73 | continue 74 | } 75 | log.Printf("Map contents:\n%s", s) 76 | } 77 | } 78 | 79 | func formatMapContents(m *ebpf.Map) (string, error) { 80 | var ( 81 | sb strings.Builder 82 | key []byte 83 | val uint32 84 | ) 85 | iter := m.Iterate() 86 | for iter.Next(&key, &val) { 87 | sourceIP := net.IP(key) // IPv4 source address in network byte order. 88 | packetCount := val 89 | sb.WriteString(fmt.Sprintf("\t%s => %d\n", sourceIP, packetCount)) 90 | } 91 | return sb.String(), iter.Err() 92 | } 93 | -------------------------------------------------------------------------------- /xdp/parse_ipv4/parse_ipv4.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | #include 3 | #include 4 | 5 | #include "bpf_endian.h" 6 | #include "common.h" 7 | 8 | #define MAX_MAP_ENTRIES 16 9 | 10 | /* Define an LRU hash map for storing packet count by source IPv4 address */ 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_LRU_HASH); 13 | __uint(max_entries, MAX_MAP_ENTRIES); 14 | __type(key, __u32); // source IPv4 address 15 | __type(value, __u32); // packet count 16 | } ip_stats_map SEC(".maps"); 17 | 18 | /* 19 | Attempt to parse the IPv4 source address from the packet. 20 | Returns 0 if there is no IPv4 header field; otherwise returns non-zero. 21 | */ 22 | static __always_inline int parse_ip_src_addr(struct xdp_md *ctx, 23 | __u32 *ip_src_addr) { 24 | void *data_end = (void *)(long)ctx->data_end; 25 | void *data = (void *)(long)ctx->data; 26 | 27 | // First, parse the ethernet header. 28 | struct ethhdr *eth = data; 29 | if ((void *)(eth + 1) > data_end) { 30 | return 0; 31 | } 32 | 33 | if (eth->h_proto != bpf_htons(ETH_P_IP)) { 34 | // The protocol is not IPv4, so we can't parse an IPv4 source address. 35 | return 0; 36 | } 37 | 38 | // Then parse the IP header. 39 | struct iphdr *ip = (void *)(eth + 1); 40 | if ((void *)(ip + 1) > data_end) { 41 | return 0; 42 | } 43 | 44 | // Return the source IP address in network byte order. 45 | *ip_src_addr = (__u32)(ip->saddr); 46 | return 1; 47 | } 48 | 49 | SEC("xdp_parse_ipv4") 50 | int parse_ipv4_func(struct xdp_md *ctx) { 51 | __u32 ip; 52 | if (!parse_ip_src_addr(ctx, &ip)) { 53 | // Not an IPv4 packet, so don't count it. 54 | goto done; 55 | } 56 | 57 | __u32 *pkt_count = bpf_map_lookup_elem(&ip_stats_map, &ip); 58 | if (!pkt_count) { 59 | // No entry in the map for this IP address yet, so set the initial value to 1. 60 | __u32 init_pkt_count = 1; 61 | bpf_map_update_elem(&ip_stats_map, &ip, &init_pkt_count, BPF_ANY); 62 | } else { 63 | // Entry already exists for this IP address, so increment it atomically using an LLVM built-in. 64 | __sync_fetch_and_add(pkt_count, 1); 65 | } 66 | 67 | done: 68 | // Try changing this to XDP_DROP and see what happens! 69 | return XDP_PASS; 70 | } 71 | 72 | char __license[] SEC("license") = "Dual MIT/GPL"; 73 | -------------------------------------------------------------------------------- /xdp/parse_udp/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go ip_protocol.go -n lo 20 | -------------------------------------------------------------------------------- /xdp/parse_udp/ip_protocol.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type IPProtocol uint8 4 | 5 | const ( 6 | IPProtocolICMP IPProtocol = 1 7 | IPProtocolTCP IPProtocol = 6 8 | IPProtocolUDP IPProtocol = 17 9 | IPProtocolIPv6 IPProtocol = 41 10 | IPProtocolGRE IPProtocol = 47 11 | ) 12 | 13 | var IPProtocolMap = map[IPProtocol]string{ 14 | IPProtocolICMP: "icmp", 15 | IPProtocolTCP: "tcp", 16 | IPProtocolUDP: "udp", 17 | IPProtocolIPv6: "ipv6", 18 | IPProtocolGRE: "gre", 19 | } 20 | -------------------------------------------------------------------------------- /xdp/parse_udp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "flag" 8 | "github.com/cilium/ebpf/link" 9 | "github.com/cilium/ebpf/ringbuf" 10 | "log" 11 | "net" 12 | "os" 13 | "os/signal" 14 | ) 15 | 16 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 17 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf parse_udp.c -- -I../../headers 18 | 19 | var ( 20 | InterfaceName string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 25 | } 26 | 27 | func main() { 28 | flag.Parse() 29 | 30 | if len(InterfaceName) == 0 { 31 | log.Fatalf("Please specify a network interface") 32 | } 33 | // Look up the network interface by name. 34 | iface, err := net.InterfaceByName(InterfaceName) 35 | if err != nil { 36 | log.Fatalf("lookup network iface %s: %s", InterfaceName, err) 37 | } 38 | 39 | // Load pre-compiled programs into the kernel. 40 | objs := bpfObjects{} 41 | if err := loadBpfObjects(&objs, nil); err != nil { 42 | log.Fatalf("loading objects: %s", err) 43 | } 44 | defer objs.Close() 45 | 46 | // Attach the program. 47 | l, err := link.AttachXDP(link.XDPOptions{ 48 | Program: objs.ParseUdpFunc, 49 | Interface: iface.Index, 50 | }) 51 | if err != nil { 52 | log.Fatalf("could not attach XDP program: %s", err) 53 | } 54 | defer l.Close() 55 | 56 | stopper := make(chan os.Signal, 1) 57 | signal.Notify(stopper, os.Interrupt) 58 | 59 | // Open a ringbuf reader from userspace RINGBUF map described in the 60 | // eBPF C program. 61 | rd, err := ringbuf.NewReader(objs.Rb) 62 | if err != nil { 63 | log.Fatalf("opening ringbuf reader: %s", err) 64 | } 65 | defer rd.Close() 66 | 67 | // Close the reader when the process receives a signal, which will exit 68 | // the read loop. 69 | go func() { 70 | <-stopper 71 | 72 | if err := rd.Close(); err != nil { 73 | log.Fatalf("closing ringbuf reader: %s", err) 74 | } 75 | }() 76 | 77 | log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index) 78 | log.Printf("Press Ctrl-C to exit and remove the program") 79 | log.Println("Waiting for events..") 80 | 81 | type bpfEvent struct { 82 | // ipv4——firewall.c event.protocol定义为u8但是由于golang内存对齐导致读取错位, 所以必须要补齐位数 uint32 83 | Protocol uint8 `json:"protocol"` 84 | _ [3]uint8 85 | SAddr uint32 `json:"s_addr"` 86 | DAddr uint32 `json:"d_addr"` 87 | Source uint16 `json:"source"` 88 | Dest uint16 `json:"dest"` 89 | Len uint16 `json:"len"` 90 | Check uint16 `json:"check"` 91 | } 92 | 93 | var event bpfEvent 94 | for { 95 | record, err := rd.Read() 96 | if err != nil { 97 | if errors.Is(err, ringbuf.ErrClosed) { 98 | log.Println("Received signal, exiting..") 99 | return 100 | } 101 | log.Printf("reading from reader: %s\n", err) 102 | continue 103 | } 104 | 105 | // Parse the ringbuf event entry into a bpfEvent structure. 106 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 107 | log.Printf("parsing ringbuf event: %s", err) 108 | continue 109 | } 110 | 111 | log.Printf("protocol %s s_addr %s:%d d_addr %s:%d data_len %d chcek %d\n", 112 | IPProtocolMap[IPProtocol(event.Protocol)], 113 | uint32ToIp(event.SAddr), event.Source, 114 | uint32ToIp(event.DAddr), event.Dest, 115 | event.Len, event.Check) 116 | } 117 | 118 | <-stopper 119 | log.Println("Received signal, exiting XDP program..") 120 | } 121 | 122 | func uint32ToIp(ipValue uint32) string { 123 | ipValue = (ipValue&0xff)<<24 | (ipValue&0xff00)<<8 | 124 | (ipValue&0xff0000)>>8 | (ipValue&0xff000000)>>24 125 | 126 | // 将 uint32 值按照大端字节序拆分成 4 个字节 127 | ipBytes := make([]byte, 4) 128 | ipBytes[0] = byte(ipValue >> 24) 129 | ipBytes[1] = byte(ipValue >> 16) 130 | ipBytes[2] = byte(ipValue >> 8) 131 | ipBytes[3] = byte(ipValue) 132 | 133 | // 将字节序列转换为 IP 地址 134 | ip := net.IP(ipBytes) 135 | return ip.String() 136 | } 137 | -------------------------------------------------------------------------------- /xdp/parse_udp/parse_udp.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bpf_endian.h" 9 | #include "common.h" 10 | 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_RINGBUF); 13 | __uint(max_entries, 256 * 1024 /* 256 KB */); 14 | } rb SEC(".maps"); 15 | 16 | struct event { 17 | __u8 protocol; 18 | __u32 s_addr; 19 | __u32 d_addr; 20 | __u16 source; // 源端口号(16 位),网络字节序; 21 | __u16 dest; // 目的端口号(16 位),网络字节序; 22 | __u16 len; // UDP 数据包的长度(16 位),包括 UDP 23 | // 头部和数据部分的长度,网络字节序; 24 | __u16 check; // 校验和(16 位),网络字节序。 25 | }; 26 | 27 | // 上传流量 28 | static int send_data(struct iphdr *ip, struct udphdr *udp) { 29 | struct event *e; 30 | // 必需步骤 判断是否有足够空间 31 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 32 | if (!e) { 33 | return 0; 34 | } 35 | 36 | e->protocol = ip->protocol; 37 | e->s_addr = ip->saddr; 38 | e->d_addr = ip->daddr; 39 | e->source = bpf_ntohs(udp->source); 40 | e->dest = bpf_ntohs(udp->dest); 41 | e->len = bpf_ntohs(udp->len); 42 | e->check = bpf_ntohs(udp->check); 43 | 44 | // 写入数据 45 | bpf_ringbuf_submit(e, 0); 46 | 47 | return 0; 48 | } 49 | 50 | SEC("xdp_parse_udp") 51 | int parse_udp_func(struct xdp_md *ctx) { 52 | void *data_end = (void *)(unsigned long)ctx->data_end; 53 | void *data = (void *)(unsigned long)ctx->data; 54 | 55 | // 边界检查:检查数据包是否大于完整的以太网 + ip 标头 56 | if (data + sizeof(struct ethhdr) + sizeof(struct iphdr) > data_end) { 57 | return XDP_PASS; 58 | } 59 | 60 | struct ethhdr *eth = data; 61 | 62 | // 如果以太网协议不是基于 IP 的,则忽略数据包 63 | if (eth->h_proto != bpf_htons(ETH_P_IP)) { 64 | return XDP_PASS; 65 | } 66 | 67 | struct iphdr *ip = data + sizeof(*eth); 68 | // 判断是否为 UDP 协议 69 | if (ip->protocol != IPPROTO_UDP) { 70 | return XDP_PASS; 71 | } 72 | 73 | // 边界检查:检查数据包是否大于完整的以太网 + ip 标头 + udp 74 | struct udphdr *udp = data + sizeof(struct ethhdr) + sizeof(struct iphdr); 75 | if ((void *)(udp + 1) > data_end) { 76 | return XDP_PASS; 77 | } 78 | 79 | bpf_printk("-------------UDP--------------"); 80 | bpf_printk("src_host %d", ip->saddr); 81 | bpf_printk("src_port_host %d", udp->source); 82 | bpf_printk("dst_port_host %d %d", bpf_ntohs(udp->dest), bpf_htons(53)); 83 | bpf_printk("udp_len_host %d", udp->len); 84 | bpf_printk("udp_csum_host %d", udp->check); 85 | 86 | send_data(ip, udp); 87 | 88 | return XDP_PASS; 89 | } 90 | 91 | char __license[] SEC("license") = "Dual MIT/GPL"; 92 | -------------------------------------------------------------------------------- /xdp/parse_udp_dns/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go -n lo 20 | 21 | -------------------------------------------------------------------------------- /xdp/parse_udp_dns/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "flag" 8 | "github.com/cilium/ebpf/link" 9 | "github.com/cilium/ebpf/ringbuf" 10 | "log" 11 | "net" 12 | "os" 13 | "os/signal" 14 | ) 15 | 16 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 17 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf parse_udp_dns.c --type event -- -I../../headers 18 | 19 | var ( 20 | InterfaceName string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 25 | } 26 | 27 | func main() { 28 | flag.Parse() 29 | 30 | if len(InterfaceName) == 0 { 31 | log.Fatalf("Please specify a network interface") 32 | } 33 | // Look up the network interface by name. 34 | iface, err := net.InterfaceByName(InterfaceName) 35 | if err != nil { 36 | log.Fatalf("lookup network iface %s: %s", InterfaceName, err) 37 | } 38 | 39 | // Load pre-compiled programs into the kernel. 40 | objs := bpfObjects{} 41 | if err := loadBpfObjects(&objs, nil); err != nil { 42 | log.Fatalf("loading objects: %s", err) 43 | } 44 | defer objs.Close() 45 | 46 | // Attach the program. 47 | l, err := link.AttachXDP(link.XDPOptions{ 48 | Program: objs.ParseDnsFunc, 49 | Interface: iface.Index, 50 | }) 51 | if err != nil { 52 | log.Fatalf("could not attach XDP program: %s", err) 53 | } 54 | defer l.Close() 55 | 56 | stopper := make(chan os.Signal, 1) 57 | signal.Notify(stopper, os.Interrupt) 58 | 59 | // Open a ringbuf reader from userspace RINGBUF map described in the 60 | // eBPF C program. 61 | rd, err := ringbuf.NewReader(objs.Rb) 62 | if err != nil { 63 | log.Fatalf("opening ringbuf reader: %s", err) 64 | } 65 | defer rd.Close() 66 | 67 | // Close the reader when the process receives a signal, which will exit 68 | // the read loop. 69 | go func() { 70 | <-stopper 71 | 72 | if err := rd.Close(); err != nil { 73 | log.Fatalf("closing ringbuf reader: %s", err) 74 | } 75 | }() 76 | 77 | log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index) 78 | log.Printf("Press Ctrl-C to exit and remove the program") 79 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs") 80 | log.Printf("Waiting for events..") 81 | 82 | type bpfEvent struct { 83 | Protocol uint8 `json:"protocol"` 84 | Rd uint8 `json:"rd"` 85 | Tc uint8 `json:"tc"` 86 | Aa uint8 `json:"aa"` 87 | Opcode uint8 `json:"opcode"` 88 | Qr uint8 `json:"qr"` 89 | RCode uint8 `json:"r_code"` 90 | Cd uint8 `json:"cd"` 91 | Ad uint8 `json:"ad"` 92 | Z uint8 `json:"z"` 93 | Ra uint8 `json:"ra"` 94 | TransactionId uint16 `json:"transaction_id"` 95 | QCount uint16 `json:"q_count"` 96 | AddCount uint16 `json:"add_count"` 97 | QType uint16 `json:"q_type"` 98 | QClass uint16 `json:"q_class"` 99 | Source uint16 `json:"source"` 100 | Dest uint16 `json:"dest"` 101 | SAddr uint32 `json:"s_addr"` 102 | DAddr uint32 `json:"d_addr"` 103 | Name [256]uint8 `json:"name"` 104 | } 105 | 106 | var event bpfEvent 107 | for { 108 | record, err := rd.Read() 109 | if err != nil { 110 | if errors.Is(err, ringbuf.ErrClosed) { 111 | log.Println("Received signal, exiting..") 112 | return 113 | } 114 | log.Printf("reading from reader: %s\n", err) 115 | continue 116 | } 117 | 118 | // Parse the ringbuf event entry into a bpfEvent structure. 119 | if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { 120 | log.Printf("parsing ringbuf event: %s", err) 121 | continue 122 | } 123 | 124 | log.Printf("protocol %d s_addr %s:%d d_addr %s:%d dns_id %d dns_query %s "+ 125 | "dns_flags %d-%d-%d-%d-%d-%d-%d-%d-%d-%d "+ 126 | "dns_qdcount %d dns_qtype %d dns_qclass %d\n", 127 | event.Protocol, uint32ToIp(event.SAddr), event.Source, uint32ToIp(event.DAddr), event.Dest, 128 | event.TransactionId, uint8ToDomain(event.Name), 129 | event.Rd, event.Tc, event.Aa, event.Opcode, event.Qr, event.RCode, event.Cd, event.Ad, event.Z, event.Ra, 130 | event.QCount, event.QType, event.QClass) 131 | } 132 | 133 | <-stopper 134 | log.Println("Received signal, exiting XDP program..") 135 | } 136 | 137 | func uint32ToIp(ipValue uint32) string { 138 | ipValue = (ipValue&0xff)<<24 | (ipValue&0xff00)<<8 | 139 | (ipValue&0xff0000)>>8 | (ipValue&0xff000000)>>24 140 | 141 | // 将 uint32 值按照大端字节序拆分成 4 个字节 142 | ipBytes := make([]byte, 4) 143 | ipBytes[0] = byte(ipValue >> 24) 144 | ipBytes[1] = byte(ipValue >> 16) 145 | ipBytes[2] = byte(ipValue >> 8) 146 | ipBytes[3] = byte(ipValue) 147 | 148 | // 将字节序列转换为 IP 地址 149 | ip := net.IP(ipBytes) 150 | return ip.String() 151 | } 152 | 153 | func uint8ToDomain(query [256]uint8) string { 154 | var domain bytes.Buffer 155 | for i, v := range query { 156 | if (v > 47 && v < 58) || 157 | (v > 64 && v < 91) || 158 | (v > 69 && v < 123) { 159 | domain.Write([]byte{v}) 160 | } else if v == 0 && i != 0 { // 快速退出 161 | break 162 | } else { 163 | if i != 0 { 164 | domain.Write([]byte{'.'}) 165 | } 166 | } 167 | } 168 | return domain.String() 169 | } 170 | -------------------------------------------------------------------------------- /xdp/parse_udp_dns_af_xdp/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | format: 10 | find . -type f -name "*.c" | xargs clang-format -i 11 | 12 | # $BPF_CLANG is used in go:generate invocations. 13 | gen: export BPF_CLANG := $(CLANG) 14 | gen: export BPF_CFLAGS := $(CFLAGS) 15 | gen: 16 | go generate ./... 17 | 18 | run: 19 | go run -exec sudo main.go bpf_bpfel.go -n lo 20 | 21 | -------------------------------------------------------------------------------- /xdp/parse_udp_dns_af_xdp/parse_udp_dns_af_xdp.c: -------------------------------------------------------------------------------- 1 | // +build ignore 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bpf_endian.h" 9 | #include "common.h" 10 | #include "protocol_hdr.h" 11 | 12 | #define MAX_SOCKS 8 13 | 14 | struct { 15 | __uint(type, BPF_MAP_TYPE_XSKMAP); 16 | __uint(max_entries, MAX_SOCKS); 17 | __type(key, sizeof(int)); 18 | __type(value, sizeof(int)); 19 | } xsks_map SEC(".maps"); 20 | 21 | struct { 22 | __uint(type, BPF_MAP_TYPE_ARRAY); 23 | __uint(max_entries, MAX_SOCKS); 24 | __type(key, sizeof(int)); 25 | __type(value, sizeof(int)); 26 | } qidconf_map SEC(".maps"); 27 | 28 | SEC("xdp_sock") 29 | int xdp_sock_prog(struct xdp_md *ctx) 30 | { 31 | int index = ctx->rx_queue_index; 32 | 33 | // A set entry here means that the correspnding queue_id has an active AF_XDP socket bound to it. 34 | if (bpf_map_lookup_elem(&qidconf_map, &index)) 35 | { 36 | // redirect packets to an xdp socket that match the given IPv4 or IPv6 protocol; pass all other packets to the kernel 37 | void *data = (void *)(long)ctx->data; 38 | void *data_end = (void *)(long)ctx->data_end; 39 | 40 | struct ethhdr *eth = data; 41 | __u16 h_proto = eth->h_proto; 42 | if ((void *)eth + sizeof(*eth) > data_end) 43 | goto out; 44 | 45 | if (bpf_htons(h_proto) != ETH_P_IP) 46 | goto out; 47 | 48 | struct iphdr *ip = data + sizeof(*eth); 49 | if ((void *)ip + sizeof(*ip) > data_end) 50 | goto out; 51 | 52 | // Only UDP 53 | if (ip->protocol != IPPROTO_UDP) 54 | goto out; 55 | 56 | struct udphdr *udp = (void *)ip + sizeof(*ip); 57 | if ((void *)udp + sizeof(*udp) > data_end) 58 | goto out; 59 | 60 | if (udp->dest != bpf_htons(53)) 61 | goto out; 62 | 63 | // 解析 UDP 报头 64 | struct dnshdr *dns = (void *)(udp + 1); 65 | if ((void *)(dns + 1) > data_end) 66 | goto out; 67 | 68 | bpf_printk("------------udp---------------"); 69 | bpf_printk("[udp] src_port %d", bpf_htons(udp->source)); 70 | bpf_printk("[udp] dst_port %d", bpf_htons(udp->dest)); 71 | bpf_printk("[udp] udp_len %d", bpf_htons(udp->len)); 72 | bpf_printk("[udp] udp_csum %d", bpf_htons(udp->check)); 73 | bpf_printk("------------dns---------------"); 74 | bpf_printk("[dns] transaction_id %d", bpf_htons(dns->transaction_id)); 75 | bpf_printk("[dns] rd %d", dns->rd); 76 | bpf_printk("[dns] tc %d", dns->tc); 77 | bpf_printk("[dns] aa %d", dns->aa); 78 | bpf_printk("[dns] opcode %d", dns->opcode); 79 | bpf_printk("[dns] qr %d", dns->qr); 80 | bpf_printk("[dns] r_code %d", dns->r_code); 81 | bpf_printk("[dns] cd %d", dns->cd); 82 | bpf_printk("[dns] ad %d", dns->ad); 83 | bpf_printk("[dns] z %d", dns->z); 84 | bpf_printk("[dns] ra %d", dns->ra); 85 | bpf_printk("[dns] q_count %d", bpf_htons(dns->q_count)); 86 | bpf_printk("[dns] ans_count %d", bpf_htons(dns->ans_count)); 87 | bpf_printk("[dns] auth_count %d", bpf_htons(dns->auth_count)); 88 | bpf_printk("[dns] add_count %d", bpf_htons(dns->add_count)); 89 | return bpf_redirect_map(&xsks_map, index, 0); 90 | } 91 | 92 | out: 93 | return XDP_PASS; 94 | } 95 | 96 | char __license[] SEC("license") = "Dual MIT/GPL"; 97 | -------------------------------------------------------------------------------- /xdp/printk_pass/Makefile: -------------------------------------------------------------------------------- 1 | # The development version of clang is distributed as the 'clang' binary, 2 | # while stable/released versions have a version number attached. 3 | # Pin the default clang to a stable version. 4 | CLANG ?= clang-14 5 | STRIP ?= llvm-strip-14 6 | OBJCOPY ?= llvm-objcopy-14 7 | CFLAGS := -O2 -g -Wall -Werror $(CFLAGS) 8 | 9 | NIC=lo 10 | IMAGE=ghcr.io/cilium/ebpf-builder:1694533004 11 | CURRENT_DIR := $(shell pwd) 12 | PARENT_DIR := $(abspath $(CURRENT_DIR)/..) 13 | GRANDPARENT_DIR := $(abspath $(PARENT_DIR)/..) 14 | 15 | format: 16 | find . -type f -name "*.c" | xargs clang-format -i 17 | 18 | env: 19 | go env -w GOPROXY=https://goproxy.cn,direct 20 | 21 | # $BPF_CLANG is used in go:generate invocations. 22 | gen: export BPF_CLANG := $(CLANG) 23 | gen: export BPF_CFLAGS := $(CFLAGS) 24 | gen: env 25 | go generate ./... 26 | 27 | run: 28 | go run -exec sudo main.go bpf_bpfel.go -n $(NIC) 29 | 30 | build: 31 | docker run --rm -v $(GRANDPARENT_DIR):/root/ebpf_labs $(IMAGE) bash -c "cd /root/ebpf_labs/xdp/printk_pass && make gen" -------------------------------------------------------------------------------- /xdp/printk_pass/README.md: -------------------------------------------------------------------------------- 1 | # printk_pass 2 | 当有流量访问网卡时,通过bpf_printk打印"xdp pass, hello xdp" 3 | 4 | ## generate 5 | 执行`make gen`,通过bpf2go编译xdp程序,生成golang相关问文件。 6 | ```bash 7 | root@imianba:~/xdp_labs/printk_pass# make gen 8 | go generate ./... 9 | Compiled /root/xdp_labs/printk_pass/bpf_bpfeb.o 10 | Stripped /root/xdp_labs/printk_pass/bpf_bpfeb.o 11 | Wrote /root/xdp_labs/printk_pass/bpf_bpfeb.go 12 | Compiled /root/xdp_labs/printk_pass/bpf_bpfel.o 13 | Stripped /root/xdp_labs/printk_pass/bpf_bpfel.o 14 | Wrote /root/xdp_labs/printk_pass/bpf_bpfel.go 15 | root@imianba:~/xdp_labs/printk_pass# 16 | ``` 17 | 18 | ## run 19 | 执行`make run`运行,注意可能需要更改网卡名称,默认为lo 20 | ```bash 21 | root@imianba:~/xdp_labs/printk_pass# make run 22 | go run -exec sudo main.go bpf_bpfel.go -n lo 23 | 2023/03/18 08:46:14 Attached XDP program to iface "lo" (index 1) 24 | 2023/03/18 08:46:14 Press Ctrl-C to exit and remove the program 25 | 2023/03/18 08:46:14 Successfully started! Please run "sudo cat /sys/kernel/debug/tracing/trace_pipe" to see output of the BPF programs 26 | 27 | 28 | ``` 29 | 30 | ## trace_pipe 31 | 新开终端,ping挂载的网卡对应的ip,默认为lo对应ip为127.0.0.1 32 | ```bash 33 | root@imianba:~# ping 127.0.0.1 34 | PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data. 35 | 64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.064 ms 36 | 64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.077 ms 37 | ``` 38 | 新开终端,查看trace_pipe打印的日志信息。 39 | ```bash 40 | root@imianba:~# sudo cat /sys/kernel/debug/tracing/trace_pipe 41 | ping-156842 [001] d.s11 3808758.716803: bpf_trace_printk: xdp pass, hello xdp 42 | ping-156842 [001] d.s11 3808758.716826: bpf_trace_printk: xdp pass, hello xdp 43 | ping-156842 [001] d.s11 3808759.731813: bpf_trace_printk: xdp pass, hello xdp 44 | ping-156842 [001] d.s11 3808759.731835: bpf_trace_printk: xdp pass, hello xdp 45 | ``` 46 | 47 | ## reference 48 | https://tonybai.com/2022/07/19/develop-ebpf-program-in-go/ 49 | https://github.com/xdp-project/xdp-tutorial -------------------------------------------------------------------------------- /xdp/printk_pass/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "context" 6 | "flag" 7 | "github.com/cilium/ebpf/link" 8 | "io" 9 | "log" 10 | "net" 11 | "os" 12 | "os/signal" 13 | "syscall" 14 | ) 15 | 16 | // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile. 17 | //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS bpf printk_pass.c -- -I../../headers 18 | 19 | var ( 20 | InterfaceName string 21 | ) 22 | 23 | func init() { 24 | flag.StringVar(&InterfaceName, "n", "lo", "a network interface name") 25 | } 26 | 27 | func main() { 28 | flag.Parse() 29 | 30 | if len(InterfaceName) == 0 { 31 | log.Fatalf("Please specify a network interface") 32 | } 33 | // Look up the network interface by name. 34 | iface, err := net.InterfaceByName(InterfaceName) 35 | if err != nil { 36 | log.Fatalf("lookup network iface %s: %s", InterfaceName, err) 37 | } 38 | 39 | // Load pre-compiled programs into the kernel. 40 | objs := bpfObjects{} 41 | if err := loadBpfObjects(&objs, nil); err != nil { 42 | log.Fatalf("loading objects: %s", err) 43 | } 44 | defer objs.Close() 45 | 46 | // Attach the program. 47 | l, err := link.AttachXDP(link.XDPOptions{ 48 | Program: objs.XdpSimpleFunc, 49 | Interface: iface.Index, 50 | }) 51 | if err != nil { 52 | log.Fatalf("could not attach XDP program: %s", err) 53 | } 54 | defer l.Close() 55 | 56 | log.Printf("Attached XDP program to iface %q (index %d)", iface.Name, iface.Index) 57 | log.Printf("Press Ctrl-C to exit and remove the program") 58 | log.Printf("Successfully started! Please run \"sudo cat /sys/kernel/debug/tracing/trace_pipe\" to see output of the BPF programs\n") 59 | 60 | // Wait for a signal and close the XDP program, 61 | stopper := make(chan os.Signal, 1) 62 | signal.Notify(stopper, os.Interrupt, syscall.SIGTERM) 63 | 64 | cxt, cancel := context.WithCancel(context.Background()) 65 | go func(cxt context.Context) { 66 | f, err := os.Open("/sys/kernel/debug/tracing/trace_pipe") 67 | if err != nil { 68 | log.Panicf("open file failed, %v", err) 69 | } 70 | defer f.Close() 71 | 72 | r := bufio.NewReader(f) 73 | for { 74 | select { 75 | case <-cxt.Done(): 76 | return 77 | default: 78 | // ReadLine is a low-level line-reading primitive. 79 | // Most callers should use ReadBytes('\n') or ReadString('\n') instead or use a Scanner. 80 | bytes, _, err := r.ReadLine() 81 | if err == io.EOF { 82 | break 83 | } 84 | if err != nil { 85 | panic(err) 86 | } 87 | log.Println(string(bytes)) 88 | } 89 | } 90 | }(cxt) 91 | 92 | <-stopper 93 | cancel() 94 | log.Println("Received signal, exiting XDP program..") 95 | } 96 | -------------------------------------------------------------------------------- /xdp/printk_pass/printk_pass.c: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | #include "bpf_endian.h" 3 | #include "common.h" 4 | 5 | SEC("xdp") 6 | int xdp_simple_func(struct xdp_md *ctx) { 7 | bpf_printk("xdp pass, hello xdp"); 8 | return XDP_PASS; 9 | } 10 | 11 | char __license[] SEC("license") = "Dual MIT/GPL"; 12 | --------------------------------------------------------------------------------