Linux 核心網路包路徑追蹤利器 skbtracer,Go 語言版本

Huayra發表於2021-08-15

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

使用

執行效果:

$ 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)

使用幫助:

$ ./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 核心標頭檔案。

只需要以下幾步即可編譯得到可執行檔案:

$ git clone https://github.com/Asphaltt/skbtracer.git
$ cd skbtracer
$ make
$ ./bin/skbtracer -h  # 可執行檔案在 bin 目錄下

實現原理

使用 clang -target bpf 編譯 BPF 程式碼,得到對應的 ELF 檔案。

然後在 Go 程式碼裡使用

//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 資訊。

相關文件

更多原創文章乾貨分享,請關注公眾號
  • Linux 核心網路包路徑追蹤利器 skbtracer,Go 語言版本
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章