skbtracer 是基于 eBPF 技术的 skb 网络包路径追踪利器,基于 goebpf , libbpf-bootstrap (required Linux Kernel 4.15+ with CONFIG_DEBUG_INFO_BTF=y, Go 1.16+) 实现,参考 Python 版本 github.com/DavadDi/skbtracer。
skbtracer 是一个 Linux 可执行文件,不依赖 libbcc.so 动态链接库,就可以在开启了CONFIG_DEBUG_INFO_BTF 的环境中运行。
开源地址:github.com/Asphaltt/skbtracer
使用
运行效果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
$ sudo ./skbtracer -c 10
TIME NETWORK_NS CPU INTERFACE DEST_MAC IP_LEN PKT_INFO TRACE_INFO
[13:43:45] [0 ] 3 nil 00:00:00:00:00:00 168 T_ACK,PSH:10.0.1.10:22->10.0.4.15:55343 ffff9a271b1b3ae0.0:ip_output
[13:43:45] [0 ] 3 ens18 00:00:00:00:00:00 168 T_ACK,PSH:10.0.1.10:22->10.0.4.15:55343 ffff9a271b1b3ae0.0:ip_finish_output
[13:43:45] [0 ] 3 ens18 00:00:00:00:00:00 168 T_ACK,PSH:10.0.1.10:22->10.0.4.15:55343 ffff9a271b1b3ae0.0:__dev_queue_xmit
[13:43:45] [0 ] 3 nil 00:00:00:00:00:00 248 T_ACK,PSH:10.0.1.10:22->10.0.4.15:55343 ffff9a271b1b36e0.0:ip_output
[13:43:45] [0 ] 3 ens18 00:00:00:00:00:00 248 T_ACK,PSH:10.0.1.10:22->10.0.4.15:55343 ffff9a271b1b36e0.0:ip_finish_output
[13:43:45] [0 ] 3 ens18 00:00:00:00:00:00 248 T_ACK,PSH:10.0.1.10:22->10.0.4.15:55343 ffff9a271b1b36e0.0:__dev_queue_xmit
[13:43:45] [0 ] 3 nil 00:00:00:00:00:00 120 T_ACK,PSH:10.0.1.10:22->10.0.4.15:55343 ffff9a271b1b2ce0.0:ip_output
[13:43:45] [0 ] 3 ens18 00:00:00:00:00:00 120 T_ACK,PSH:10.0.1.10:22->10.0.4.15:55343 ffff9a271b1b2ce0.0:ip_finish_output
[13:43:45] [0 ] 3 ens18 00:00:00:00:00:00 120 T_ACK,PSH:10.0.1.10:22->10.0.4.15:55343 ffff9a271b1b2ce0.0:__dev_queue_xmit
[13:43:45] [0 ] 3 nil 00:00:00:00:00:00 120 T_ACK,PSH:10.0.1.10:22->10.0.4.15:55343 ffff9a271b1b30e0.0:ip_output
15 event(s) received
0 event(s) lost (e.g. small buffer, delays in processing)
|
使用帮助:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
$ ./skbtracer -h
examples:
skbtracer # trace all packets
skbtracer --proto=icmp -H 1.2.3.4 --icmpid 22 # trace icmp packet with addr=1.2.3.4 and icmpid=22
skbtracer --proto=tcp -H 1.2.3.4 -P 22 # trace tcp packet with addr=1.2.3.4:22
skbtracer --proto=udp -H 1.2.3.4 -P 22 # trace udp packet wich addr=1.2.3.4:22
skbtracer -t -T -p 1 -P 80 -H 127.0.0.1 --proto=tcp --callstack --icmpid=100 -N 10000
Usage:
skbtracer [flags]
Flags:
--callstack output kernel stack trace (DEPRECATED: not implemented to print the function stack)
-c, --catch-count uint catch and print count (default 1000)
--dropstack output kernel stack trace when drop packet (DEPRECATED: not supported on Ubuntu 18.04.5 LTS with kernel 5.10.29-051029-generic)
-h, --help help for skbtracer
--icmpid uint trace icmp id
-H, --ipaddr string ip address
--iptable output iptable path
--keep keep trace packet all lifetime (DEPRECATED: not implemented yet)
-N, --netns uint trace this Network Namespace only
--noroute do not output route path
-p, --pid uint trace this PID only
-P, --port uint udp or tcp port
--proto string tcp|udp|icmp|any
-T, --time show HH:MM:SS timestamp (default true)
-t, --timestamp show timestamp in seconds at us resolution
|
编译
编译 skbtracer 需要使用较新版本的 llvm,要求 Go 语言的版本 1.16 及以上,因为需要使用 embed
包将 BPF 的 ELF 文件内嵌到 Go 程序里面,不依赖 Linux 内核头文件。
只需要以下几步即可编译得到可执行文件:
1
2
3
4
|
$ git clone https://github.com/Asphaltt/skbtracer.git
$ cd skbtracer
$ make
$ ./bin/skbtracer -h # 可执行文件在 bin 目录下
|
实现原理
使用 clang -target bpf
编译 BPF 代码,得到对应的 ELF 文件。
然后在 Go 代码里使用
1
2
|
//go:embed skbtracer.elf
var bpfProg []byte
|
将 BPF ELF 文件内嵌进来。接着使用 goebpf 加载 BPF ELF 文件,并将 BPF 程序装载到内核里去运行。
BPF 程序通过 perf event 将网络包路径信息发送到 Go 程序,Go 程序就可以解析 perf event 得到 struct。
BPF 程序里使用以下 kprobe
追踪网络包路径信息:
- netif_rx
- __netif_receive_skb
- tpacket_rcv
- packet_rcv
- napi_gro_receive
- __dev_queue_xmit
- br_handle_frame_finish
- br_nf_pre_routing
- br_nf_pre_routing_finish
- br_pass_frame_up
- br_netif_receive_skb
- br_forward
- __br_forward
- br_forward_finish
- br_nf_forward_ip
- br_nf_forward_finish
- br_nf_post_routing
- br_nf_dev_queue_xmit
- ip_rcv
- ip_rcv_finish
- ip_output
- ip_finish_output
使用 kprobe
、kretprobe
探测 ipt_do_table
去追踪网络包的 iptables 信息。
相关文档