Post content
彻底解决了!我其实想做的只是“在 xfrm error 增加的时候记录此时的 skb 和 xfrm 状态”,但是这件事比我想象中的复杂太多太多了。 一开始我是在 kfree_skbmem 的地方检查 skb->net,因为 xfrm_stat 就在 net 里。但是发现读出来都是 0。 然后我发现这是 percpu_ptr,幸好 bpf 有几个 percpu helper,但是发现他们只能用于全局 ksym 的那种 percpu ptr,中道崩殂。 然后我检查了一下 percpu 的实现,原来就是 %gs 嘛,这有何难,我在之前 bpf for go 里抠 goid 就是从 %fs 里取出来的,照抄一番稍作修改不就行了。但是发现 thread->gsbase 都是 0,原来那个字段根本不用来存储 %gs,和 %fs 不一样。其实仔细想想也是,为啥 percpu 的数据会绑死在线程数据结构。 然后我开始“模糊化”,尝试用 pwru --output-only-if-hit kfree_skbmem 结合 pcap filter esp or (tcp and dst port 8000) 来过滤两个方向的流量,但是发现会漏掉不少 xfrm_stat 变化;而一旦放宽 pcap filter 又会匹配过多的流量导致数据丢失。这条路我一度觉得可以走通,都开始开香槟了,最后在 eks 上测试时被打击坏了。 做了好几个噩梦之后还是没有放弃,决定试试力量大的粗暴方案:kprobe attach address! 想想我在 bpf for go 是怎么绕过 kretprobe 的:搜索 ret 指令,k attach all those addresses。好,那我现在也 全量搜索 /proc/kcore 搜寻符合模式的指令,一共只有 500 多个,全部 kprobe attach 并不过分,在结合一些巧妙的过滤足以准确确定 “这就是 XFRM_INC_STATS” 。连夜糊出代码,在 eks 上运行,结果非常理想,既不错杀也不放过。 又一次实践了 “零知识 debug” 。 这个概念我在北京和奥格黄老师吃饭时说过一次。我说,我希望的 debugging tool 能让人在尽量少地拥有前置知识的情况下也依然可以通过观测到的异常现象来让大家推敲出这里有问题。与其相反的 debug 方式,也就是大家常用的 “富知识 debug”、“经验派 debug”,其实本质和瞎猜没有太大区别,都是通过搜索记忆中曾经遇到过的问题,来试试“这是不是以前遇到的那个问题的变种呢”。如果症状见得多,一下匹配准确了,猜对了,大家感叹“真是老中医,有经验”。这固然好,但是我想面向的是从无到有的,对程序员的前置要求也更低的,回合式战旗游戏般的分析。当然这都是很主观很理想的想法,每个人性格也有不同的解决问题方式,适合自己就好,就当我胡说八道。 还是说回这个 xfrm tracing,我彻底放弃了理解内核 policy + SA 并输出不同错误码的实现细节,转而观测 “出错时的 skb + policy + SA”,和 “期望状态” 一对比就能分析出问题,这完全不需要理解 XfrmInStateMismatch 和 XfrmInStateInvalid 和 XfrmInNoStates 有什么语义区别,我认为对调试 xfrm 报错来说也是降低了门槛,大家不同对着错误码统计数据猜测控制平面行为。