Welcome to the Black Parade


Гео и язык канала: не указан, не указан
Категория: не указана


Death has many faces, and I'm looking forward to seeing this one.

Связанные каналы  |  Похожие каналы

Гео и язык канала
не указан, не указан
Категория
не указана
Статистика
Фильтр публикаций




为了追查为什么 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


又是被 iptables 日翻股的一天,但是我对着内核代码翻来覆去地看,似乎找到了一种 trace iptables 的纯 bpf 方法,而且似乎和 legacy/nft 无关,ipv4/v6 也统一。对于非匹配的规则,也可以先输出“我们出、入 A 表 B 链啦”,来暗示接下来的空白输出是没有匹配。更重要的是它不修改内存,不添加观测性规则,不使用 ko,还可以很方便地用 pcap filter 过滤,实在是太棒了!

唯一的问题是时间,我的时间😭


bpftrace 小贴士

比方说我想看 nf_log_buf_close 的调用参数,首先看参数类型

$ s bpftrace -lv kfunc:vmlinux:nf_log_buf_close
kfunc:vmlinux:nf_log_buf_close
struct nf_log_buf * m

然后检查检查类型定义

$ pahole -C nf_log_buf
struct nf_log_buf {
unsigned int count; /* 0 4 */
char buf[1020]; /* 4 1020 */

/* size: 1024, cachelines: 16, members: 2 */
};

然后写 bpftrace

bpftrace -e 'kfunc:vmlinux:nf_log_buf_close {printf("%s\n", str(args->m->buf));}'

可惜上面那个会报错,说 str() 要指针但是传入的是 char[]。

这就是大家经常遇到的 bpftrace 类型问题,非常弱智,但如果你不了解他的运作模式会卡住,放弃。

我的解法是直接从结构体取成员退化成手动偏移:

s bpftrace -e 'kfunc:vmlinux:nf_log_buf_close {$m=(uint8*)args->m; printf("%s\n", str($m+4));}'

这一套方式虽然原始,失去了一些便捷性,但是扩展了边界,用熟练了之后也很快。

比如接下来我就可以稍加修改,只在 nat POSTROUTING 的时候打印栈

$ s bpftrace -e 'kfunc:vmlinux:nf_log_buf_close {$m=(uint8*)args->m; $buf=str($m+4); if (strcontains($buf, "nat/POSTROUTING")) {printf("%s\n", kstack)}}'

这不比 bcc 之流方便太多了。


Репост из: Неизвестно


我感觉这类 “bug” 根因是内核这些功能才一开始没考虑到现代 SDN 的玩法。

比如 skb_scrub 在 5.10 还无条件在 veth_xmit 的时候清空 mark,这是没考虑在 bpf 时代有人会把一对 veth 放在同一 netns。在史前做这种事毫无意义。

比如 iptables nat POSTROUTING 只对 skb 执行一次,在古代毫无问题,但是现在可能会出现一个 skb 多次 host forwarding,就坏掉了。

比如 tproxy server 生产的流量如果直接路由走了,万事大吉,但是 Z 世代居然把流量路由给本地 veth 然后再走一次 host forwarding,这在 sysctl_tcp_early_demux=1 的情况下会被设置上 skb->sk 导致通不过 ip_forward() 检查。

还一个比一个难查,吐了。


在床上大叫:iptables 我 CNM!

nat 表 POSTROUTING 链貌似(但有九成把握)只会对一个 skb 执行一次。如果我的数据面是 veth -> vhost -> vnet -> eth0 且在 veth -> vhost 这一次 host forwarding 的时候执行了 nat POSTROUTING,那么在 vnet -> eth0 的时候就不会执行!这个潜规则貌似(但)只有 nat POSTROUTING 才有,像 mangle POSTROUTING 就听话得像一只鸡。

这也是 iptables 的另一个观测难题:我们可以想办法 trace “哪些规则被命中了”,但是不能简单用 trace 来解释“为什么有些规则看起来应该命中却没有”。

实现上可能是通过 skb->nfct 来控制的,走过一次 nat POSTROUTING 可能被认为是 ct 处理过,如果再走一次害怕会 ct conflict,所以直接标记为不再执行。

但是文档呢?你吗的文档呢?


震撼,全公司都以为 cilium L7 proxy 是透明代理,直到我发现不是,然后全公司都陷入了震撼,开始 @L7 proxy 的作者。太棒嘞,符合我对开源软件的预期😆

是的我还在修那个可能会被美国联邦政府起诉的 bug,最近脑海中的比喻是达瓦札天然气燃烧坑,苏联工程师在1971年找到这个天然气坑,认为气坑会释出有毒气体所以一致决定把它烧掉。当时估计气体会在几星期后燃烧殆尽,但最后燃烧了超过四十年。

现在正在发生的事情简直就是翻版,一开始以为是几个欠考虑的 PR 不经意间引起的 bug,结果现在发现是自古以来的超级漏洞,这把大火不知还要再燃烧多久才能熄灭。


记下最近高强度 debug 所面临的工具短缺问题

1. iptables-tracing。先别提 nft / legacy / ip6tables,光是 nat 就能让人皱紧眉头。比方说最简单的 masquerade,我们当然可以用 -j LOG/TRACE (甚至搞个 .ko)在这个规则被命中的时候打印 skb 和规则本身,但是 nat 自带的 rev-nat 是没有显式规则的。你 SNAT 之后的包发出去,回来的时候要 DNAT 吧,然而这个 DNAT 是没有 iptables rules。更别说 nat 内部奇奇怪怪的 drop 条件,比如 nat conflict,血条直接动态清零。

2. pwru。我最近每天至少要运行 50 次 pwru,然而还是有太多功能缺失了:--filter-mark 需要接受 mask、--filter-track-skb 需要支持 skb_clone、需要有 kretprobe 去接收 retval、需要以生命期为单位过滤(从而可以反向追溯 skb)、需要观测 sk kfuncs、需要正确打印进程名和 ifname、需要反映函数的调用关系、需要在函数结束的时候同样输出 *skb 来确认“就是你干得好事”。

3. pcap filter。需要一些糖来过滤:vxlan 内层 L3/L4、tcp payload。需要简单的办法来指定特定的 tcp 链接(我说的就是 src port)。需要过滤 **tcp 会话**(我记得是大鹅第一次提出这个想法)。

4. gdb。我问遍了 AI 都不知道怎么让 gdb 同时载入 /proc/kcore 和 /proc/kallsyms。我的根本目的是检查 /proc/kcore 里的指令时 gdb 能帮我把 callq 的目标符号打印出来,而已!难不成要我写个工具把 kallsyms text 转成 ELF,这太扭曲了,不 unix。

5. bpftrace。我大量使用 tid 来串联,目前还没出过问题。本质是这样的:在“锚函数”(比如以 skb 作为参数的函数)处,我们可以通过很多手段过滤,确认目标后记录 tid;然后在内层函数处(甚至不用函数,偏移!)通过 tid 串联出当前 skb,做检查和期望的输出。这种多函数串联 tracing 的功能,内核目前只有一个很不好用的 ftrace graph,基本可视为没有过滤功能。我不需要完整的 ftrace,光是一个单层调用的,自解析参数的,带过滤功能的,the little ftrace,就是我想要的。还有 kstack 的 bug,每次我都手动回溯,石器时代是吧。

6. inside a kernel function。ftrace 的粒度还是太大了,就算定位了罪魁祸首的函数,其内部运行逻辑的观测是十分艰难。就算有 retval,你也难以判断内部是哪个 if 导致 goto drop。由于有 ASLR,就算有 dbgsym 也并不能直接拿来使用,但是我发现 offset 是绝妙的观测手段。比如在 /proc/kcore 上观测到 ip_forward() 对一个特定 skb 的运行路径是 +0, +1, +10, +78,那么直接把偏移拿到 dbgsym 上去解读就行了。不过 inline 依然恶心,我们只能期待 inline 本身就没多少指令来肉眼解读。或者反汇编也是一个思路,比如知道某参数是 skb,那么分析汇编就足以知道 mov 0x0, 0x18(%rbx) 是 skb->sk = null,这种弱智工作,就tm应该自动完成,别浪费工程师的时间。


梁博最新的 Live 专辑, 没有日落大道我不是很认可 有黑夜中,我十分认可!

(专辑封面真好看,像情人在我左右,跳舞在黑夜中)


Using boolean modifiers when searching for jobs on LinkedIn

好用,示例用法 golang NOT (ByteDance OR TikTok)


今天又打开了久违的领英开始搜工作,已经在考虑转行回去干土木了。

或者趁着雅思A还没过期,试试能不能逃出我们亚洲,山是高昂的头!

胡思乱想中又想起了 DoS2 的支线任务“存在性危机”,想起汪精卫的“饮刀成一快,不负少年头”,想起罗翔的“勇气”,像中二少年般想象有一天命运之神把那把名为勇气的宝剑交到我手上,希望我能鼓起勇气挥舞。

想起我从小喜欢灰色,喜欢那些描绘濒死的歌曲,小学对表哥倾述自己的自杀计划。

想起我不止一次在 trello 写下自杀之旅,列清单。想起自己在梦中都自杀过几次,有一次还是在梦里当着妈妈的面。

不不不,这不是由于最近工作不顺导致的。

我只是很少在这里暴露真实的自己。我内向,不想说话,讨厌社交,没有朋友(也不想有),公司团建吃顿晚饭尬聊三小时简直要我命,让我上台做技术分享我能失眠两个月。别人都说工作几年后跳槽都靠人脉内推,我离职一家拉黑一半以上的前同事。

那么,以上就是频道主姗姗来迟的自我介绍。欢迎来到我之暗面,welcome to my black parade.


(是的我还在高强度女娲补天)现在在我看来已经走到死路了。加密功能强依赖 skb->mark,然而 L7 proxy 之 tproxy 也在 iptables 里强刷新 mark,根本做不了,系统性金融危机。

这两天我闪现过无数画面,比如你看到裤腰带上有个线头,想把它扯掉,结果越扯越大,越拉越长,你才发现这个根本不是什么无关紧要的“线头”,而是这个裤子本身的结构性矛盾。

太棒了,强度太高了,这就是最受欢迎的 CNCF 毕业项目啊,受教了。(最受欢迎是我乱说的。)

(求别转,这次事故大到连 commit message 都只敢支支吾吾,计划的策略是以掩耳盗铃芝士发版,虽然我现在觉得根本发不了。)


成就:从早十点工作到凌晨一点,manager还对我特不满。太菜了,是时候辞职了,求内推。


从没见过这么惨的 revert,本来以为是两个 PR,结果牵扯出一大堆以此为基础的 PRs。更惨的是我担心 revert 已经不可能了,代码早已日新月异,很多 cleanups 都是基于之前的错误 PR,难道要全部拎出来?
最惨的是这是 release blocker,manager 半夜催我,马上被开除。


即将解锁成就:Back and Forth Prodigy
解锁这个成就需要在 github 合并一个 patch,之后 revert 这个 patch,之后 revert 那个 revert,最后再 revert revert revert。
(捅出这么大篓子不被开除留着 pip 吗)


昨晚散步的随机幻想:

我终于想试试给 bpf 版本的 tcpdump 做一下 benchmark 和 tcpdump 对比一下性能,但是很快发现 bpf 版本(简称 etcpdump(被人吐槽取名太烂))里充满了没必要的指令,比如:

; if (CONFIG.skb_track)
22: movabsq $-118460502762736, %rdi
2c: movzbq (%rdi), %rdi
; if (CONFIG.skb_track)
31: testq %rdi, %rdi
34: je 0x61
36: movq %rbp, %rsi
;
39: addq $-16, %rsi
; if (bpf_map_lookup_elem(&skb_addresses, &skb_addr))
3d: movabsq $-118459148791808, %rdi
47: movq -24(%rbp), %rax
4e: callq 0xffffffffde9b37e4
53: testq %rax, %rax
56: je 0x5c
58: addq $56, %rax

这一分支由 CONFIG.skb_track 这个注入的配置来决定是否跳入,但是从性能考虑,我们连这个 if (CONFIG.skb_track) 条件判断的指令都最好删掉,而这是完全可以做到的,因为相比传统可执行的 ELF,运行 bpf 是要先在用户态读 bpf 字节码,这多的一层就足以让我们在用户态根据运行时配置裁剪 bpf 字节码,删除死分支。

这样一想很多新东西就出现了,比如有些语言喜欢玩“依赖注入”,直接在这个阶段(以下称为 loader 阶段)把指令换了就行;而 linker 做的事情,粗糙来看也是替换一些 ELF 里的字节,只是我们这里把这个概念扩展成更复杂的操作,不仅是修改 plt/got,而是根据用户传入的命令行参数+环境变量动态修改 .text。

这确实有点极端了,也就 bpf 这种极端性能主义才会绞尽脑汁做这些优化。


超级喜欢猴子打气球6 (Bloons TD6)这个游戏!塔防爱好者不要错过鸭!


某些社区实在太睿智了,还 "push users towards using sys.monitoring now that 3.12 has been released",这两 API 完全就不是同一层面的东西,从完全非侵入式且大有潜力的现代 API,开倒车回到完全侵入式且很难扩展的古董 API,某些社区感觉还好极了。

我很少明面上吐槽什么人的“编程品味”,因为这相当主观且不解决问题。但我现在就从“解决问题”的角度来说,这群人太没品味了。


Репост из: Manjusaka 的碎碎念
和 Gray 反复确认后发现,3.12 的确是在一个 commit 的中移除了 function_entry 和 function_return 的 USDT Point
https://github.com/python/cpython/issues/110385
我们应该是第一个发现的。。。然而始作俑 commit 是作为 PEP 669 的一个实现在四个月前引入的 https://github.com/python/cpython/commit/411b1692811b2ecac59cb0df0f920861c7cf179a

只能高呼 Python 遥遥领先,遥遥领先(不知道作者是故意的还是不小心的(要是因为 669 而移除 USDT 的话,那就属于开历史倒车的行为了

Показано 20 последних публикаций.

98

подписчиков
Статистика канала