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

使用 kprobekretprobe 探测 ipt_do_table 去追踪网络包的 iptables 信息。

相关文档