填坑 eBPF Talk: skbtracer-iptables 中的 One more thing.

源代码:GitHub iptables-trace

前置知识:

kprobes in kernel module

因为 eBPF 不支持修改 skb->nf_trace 字段和 struct pt_regs(出于安全考虑),所以就在内核模块里修改它们。

  1. 先运行 ipt_do_table/ip6t_do_table 上的 kprobe bpf prog。
  2. skb->nf_trace = 1;,后面就会运行 nf_log_trace()
  3. 运行 nf_log_trace 上的 kprobe bpf prog。
  4. regs->si = 0; 将第二个参数置 0,劫持 nf_log_trace() 函数,使其跳过 log 处理逻辑。
  5. 运行 ipt_do_table/ip6t_do_table 上的 kretprobe bpf prog。

处理逻辑并不复杂。

但需要在 insmod 内核模块的时候:

  1. 获取 kprobe/kretprobe 三个 bpf prog。
  2. ipt_do_table/ip6t_do_table/nf_log_trace 进行 kprobe,在 kprobe 回调函数里 bpf_prog_run() kprobe bpf prog。
  3. ipt_do_table/ip6t_do_table 进行 kretprobe,在 kretprobe 回调函数里 bpf_prog_run() kretprobe bpf prog。

rmmod 时:

  1. 注销 kprobekretprobe
  2. 释放 kprobe/kretprobe 三个 bpf prog。

Emm,在开发的时候,没正确注销 kprobekretprobe,将我的虚拟机搞崩了 3 次;吐血了都。庆幸有做快照,能快速恢复回来。所以,在开发内核模块前,需要给系统做下快照,以便搞坏了后恢复回来。

Go and bpf prog

没错,用户态的代码逻辑复用了 GitHub skbtracer-iptables

不同的地方是,将 skbtracer-iptables 进行 ebpf.Kprobe() 的逻辑改为 insmod 了,使用内核模块进行 kprobe

将 bpf prog pin 到 /sys/fs/bpf/ 后,将 pin 路径传给内核模块;内核模块通过 bpf_prog_get_type_path() 获取 bpf prog。

这里踩了坑:将 bpf prog 的 FD 传给内核模块是否可以?通过 insmod 的方式是不可以的,因为 exec.Command() 会拉起新的进程;不同进程间不能直接共享 FD。

如果想要通过 FD 的方式传递 bpf prog,可以考虑 netlink:Linux 网络: netlink 是 Go 和内核模块之间优秀的通信兵

效果

 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
# git clone https://github.com/Asphaltt/iptables-trace.git
# cd iptables-trace
# make
# ./iptables-trace -c 20
TIME       SKB                  NETWORK_NS   PID      CPU    INTERFACE          DEST_MAC           IP_LEN PKT_INFO                                               IPTABLES_INFO
[04:53:15] [0xffff8df402e052e8] [4026531840] 6888     3                         00:00:00:00:00:00  264    T_ACK,PSH:192.168.1.138:22->192.168.1.12:53030         ipttrace=[pf=PF_INET in= out=enp0s8 table=filter chain=OUTPUT hook=3 rulenum=1]
[04:53:15] [0xffff8df402e052e8] [4026531840] 6888     3                         00:00:00:00:00:00  264    T_ACK,PSH:192.168.1.138:22->192.168.1.12:53030         iptables=[pf=PF_INET table=filter hook=OUTPUT verdict=ACCEPT cost=77.425µs]
[04:53:15] [0xffff8df50291d200] [4026531840] 8432     1      enp0s8             08:00:27:39:de:94  52     T_PSH:192.168.1.12:53030->192.168.1.138:22             ipttrace=[pf=PF_INET in=enp0s8 out= table=filter chain=INPUT hook=1 rulenum=1]
[04:53:15] [0xffff8df50291d200] [4026531840] 8432     1      enp0s8             08:00:27:39:de:94  52     T_PSH:192.168.1.12:53030->192.168.1.138:22             iptables=[pf=PF_INET table=filter hook=INPUT verdict=ACCEPT cost=36.942µs]
[04:53:15] [0xffff8df402e050e8] [4026531840] 8432     1                         87:ab:0d:ea:d5:19  88     T_ACK,PSH:192.168.1.138:22->192.168.1.12:53030         ipttrace=[pf=PF_INET in= out=enp0s8 table=filter chain=OUTPUT hook=3 rulenum=1]
[04:53:15] [0xffff8df402e050e8] [4026531840] 8432     1                         87:ab:0d:ea:d5:19  88     T_ACK,PSH:192.168.1.138:22->192.168.1.12:53030         iptables=[pf=PF_INET table=filter hook=OUTPUT verdict=ACCEPT cost=40.266µs]
[04:53:15] [0xffff8df402e04ce8] [4026531840] 6888     3                         00:00:00:00:00:00  328    T_ACK,PSH:192.168.1.138:22->192.168.1.12:53030         ipttrace=[pf=PF_INET in= out=enp0s8 table=filter chain=OUTPUT hook=3 rulenum=1]
[04:53:15] [0xffff8df402e04ce8] [4026531840] 6888     3                         00:00:00:00:00:00  328    T_ACK,PSH:192.168.1.138:22->192.168.1.12:53030         iptables=[pf=PF_INET table=filter hook=OUTPUT verdict=ACCEPT cost=84.42µs]
[04:53:15] [0xffff8df50291db00] [4026531840] 8432     1      enp0s8             08:00:27:39:de:94  52     T_PSH:192.168.1.12:53030->192.168.1.138:22             ipttrace=[pf=PF_INET in=enp0s8 out= table=filter chain=INPUT hook=1 rulenum=1]
[04:53:15] [0xffff8df50291db00] [4026531840] 8432     1      enp0s8             08:00:27:39:de:94  52     T_PSH:192.168.1.12:53030->192.168.1.138:22             iptables=[pf=PF_INET table=filter hook=INPUT verdict=ACCEPT cost=38.611µs]
[04:53:15] [0xffff8df50291d000] [4026531840] 8432     1      enp0s8             08:00:27:39:de:94  52     T_PSH:192.168.1.12:53030->192.168.1.138:22             ipttrace=[pf=PF_INET in=enp0s8 out= table=filter chain=INPUT hook=1 rulenum=1]
[04:53:15] [0xffff8df50291d000] [4026531840] 8432     1      enp0s8             08:00:27:39:de:94  52     T_PSH:192.168.1.12:53030->192.168.1.138:22             iptables=[pf=PF_INET table=filter hook=INPUT verdict=ACCEPT cost=40.887µs]
[04:53:15] [0xffff8df50291d900] [4026531840] 8432     1      enp0s8             08:00:27:39:de:94  52     T_PSH:192.168.1.12:53030->192.168.1.138:22             ipttrace=[pf=PF_INET in=enp0s8 out= table=filter chain=INPUT hook=1 rulenum=1]
[04:53:15] [0xffff8df50291d900] [4026531840] 8432     1      enp0s8             08:00:27:39:de:94  52     T_PSH:192.168.1.12:53030->192.168.1.138:22             iptables=[pf=PF_INET table=filter hook=INPUT verdict=ACCEPT cost=48.685µs]
[04:53:15] [0xffff8df402e048e8] [4026531840] 6888     3                         00:00:00:00:00:00  328    T_ACK,PSH:192.168.1.138:22->192.168.1.12:53030         ipttrace=[pf=PF_INET in= out=enp0s8 table=filter chain=OUTPUT hook=3 rulenum=1]
[04:53:15] [0xffff8df402e048e8] [4026531840] 6888     3                         00:00:00:00:00:00  328    T_ACK,PSH:192.168.1.138:22->192.168.1.12:53030         iptables=[pf=PF_INET table=filter hook=OUTPUT verdict=ACCEPT cost=126.368µs]
[04:53:15] [0xffff8df50291df00] [4026531840] 8432     1      enp0s8             08:00:27:39:de:94  52     T_PSH:192.168.1.12:53030->192.168.1.138:22             ipttrace=[pf=PF_INET in=enp0s8 out= table=filter chain=INPUT hook=1 rulenum=1]
[04:53:15] [0xffff8df50291df00] [4026531840] 8432     1      enp0s8             08:00:27:39:de:94  52     T_PSH:192.168.1.12:53030->192.168.1.138:22             iptables=[pf=PF_INET table=filter hook=INPUT verdict=ACCEPT cost=38.087µs]
[04:53:15] [0xffff8df402e050e8] [4026531840] 6888     3                         87:ab:0d:ea:d5:19  1324   T_ACK,PSH:192.168.1.138:22->192.168.1.12:53030         ipttrace=[pf=PF_INET in= out=enp0s8 table=filter chain=OUTPUT hook=3 rulenum=1]
[04:53:15] [0xffff8df402e050e8] [4026531840] 6888     3                         87:ab:0d:ea:d5:19  1324   T_ACK,PSH:192.168.1.138:22->192.168.1.12:53030         iptables=[pf=PF_INET table=filter hook=OUTPUT verdict=ACCEPT cost=40.68µs]

ipttrace 里的 rulenum 对应 iptables -nvL --line-numbers 里的规则编号。

总结

内核模块会搞崩系统,还是 eBPF 安全。