Post content
为了追查为什么 nat POSTROUTING 不会处理同一 skb 两次,我今天写了以下 bpftrace 脚本 k:nf_nat_inet_fn { $skb=(struct sk_buff*)reg("si"); $data=$skb->network_header+$skb->head; if (*(uint8[4]*)((uint8*)$data+16)==pton("1.1.1.1") || *(uint8[4]*)((uint8*)$data+16)==pton("1.0.0.1")) { $state=(struct nf_hook_state*)reg("dx"); printf("%d@%s nfct=%llx ", $state->hook, $skb->dev->name, $skb->_nfct); $ct=(struct nf_conn*)($skb->_nfct & ~7); printf("ct->status=%llx nf_nat_initialized=%d do_nf_nat_manip_pkt?=%d\n", $ct->status, $ct->status & (1<<7), $ct->status & (1<<4)); @traced[tid]=true; @tid2ct[tid]=$ct; @cts[$ct]=true; } } kr:nf_nat_inet_fn{ if (@traced[tid]==true) { delete(@traced[tid]); $ct=@tid2ct[tid]; delete(@cts[$ct]); delete(@tid2ct[tid]); printf("retval %d\n", reg("ax")); } } k:nf_nat_manip_pkt { if (@traced[tid]==true) { printf("nf_nat_manip_pkt: skb=%llx\n", reg("di")); } } k:nf_nat_setup_info { $ct=(struct nf_conn*)reg("di"); if (@cts[$ct]==true) { printf("%s\n", ksym(*(uint64*)reg("sp"))); printf("%s\n", kstack); } } 虽然大家缺少上下文不太能看懂我在做什么,但是我还是总结了几点 takeaways: 1. bpftrace 使用熟练之后绝对比 bcc 生产力高得多,更别说 cilium/ebpf。因为在 debugging 场景下,写的脚本需要大量快速修改,看完这个参数的值要看另一个地方 kprobe 并把两处用 tid 关联起来,用 bpftrace 可以十秒钟用上 bpf map,一秒钟做内核符号转换,更别说对 deref 操作的转化,已经是我最爱的 bpftrace 抽象语法。deref 转化是指,*(uint64*)reg("sp") 这句实际上编译成了 bpf_probe_read_kernel 这种繁琐的 helper。我太爱这简化了。 2. 多个 kprobe 的事件关联。这是我非常非常常用的技巧。我们从简单的开始,假如想观测一个内核函数的返回值,输出 "入参->返回”,我们面临的问题是,在 kprobe 处没有返回值,在kretprobe 处没有入参值。所以我最常用的手段是在 kprobe 处把参数用 tid 存到 map 里,在 kretprobe 用 tid 取出来。实际上这甚至可以用在不同函数,我上面的代码里就使用多个不变量在多个 kprobe 之间关联事件。 3. 过滤。这是老大难了,ftrace graph 那么难用,一大原因就是支持的过滤太少。比如我想看 'dst host 1.1.1.1 and dst port 80' 的 skb 在 eth0 上执行这个内核函数时的第三入参 state 的值,没有已有工具能帮你一下子拿到,但是 bpftrace 去做这事并不麻烦。我上面代码里实际过滤的是 'dst host 1.1.1.1 or dst host 1.0.0.1',如果我愿意甚至可以过滤 tcp[tcpflags] = tcp-syn,也不难。 bpftrace 太灵活了,写起来还简单。 4. 畏难心理。就算我自己拥有大量 bpftrace 使用经验,知道各种问题怎么处理,理解底层实现,能读 verifier log 和 bpf opcode,但是逼迫自己用一个没有尝试过的方式去解决一个陌生的问题,依然心生恐惧。这种恐惧阻止人往前走,甚至让人如释重负、放弃思考:这太难了,我解决不了,就这样吧。我有过无数次这样的时候,我也有硬着头皮走下去也依然解决不了的时候,但是,(我就不灌鸡汤了)。 总之都快去用起来 bpftrace!让我们到达神奇的世界创造未知,创造未知的空间,熊熊火焰在胸中凝结~凝结!