填坑 eBPF Talk: skbtracer-iptables 中的 One more thing.
源代码:GitHub iptables-trace
前置知识:
kprobes in kernel module
因为 eBPF 不支持修改 skb->nf_trace
字段和 struct pt_regs
(出于安全考虑),所以就在内核模块里修改它们。
- 先运行
ipt_do_table
/ip6t_do_table
上的 kprobe
bpf prog。
skb->nf_trace = 1;
,后面就会运行 nf_log_trace()
。
- 运行
nf_log_trace
上的 kprobe
bpf prog。
regs->si = 0;
将第二个参数置 0,劫持 nf_log_trace()
函数,使其跳过 log 处理逻辑。
- 运行
ipt_do_table
/ip6t_do_table
上的 kretprobe
bpf prog。
处理逻辑并不复杂。
但需要在 insmod
内核模块的时候:
- 获取
kprobe
/kretprobe
三个 bpf prog。
- 对
ipt_do_table
/ip6t_do_table
/nf_log_trace
进行 kprobe
,在 kprobe
回调函数里 bpf_prog_run()
kprobe
bpf prog。
- 对
ipt_do_table
/ip6t_do_table
进行 kretprobe
,在 kretprobe
回调函数里 bpf_prog_run()
kretprobe
bpf prog。
在 rmmod
时:
- 注销
kprobe
和 kretprobe
。
- 释放
kprobe
/kretprobe
三个 bpf prog。
Emm,在开发的时候,没正确注销 kprobe
和 kretprobe
,将我的虚拟机搞崩了 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 安全。