You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ip6tables无法过滤特定目标IPv6地址的TCP分片数据包问题咨询

ip6tables无法过滤特定目标IPv6地址的TCP分片数据包问题咨询

各位大佬好,最近碰到一个ip6tables过滤IPv6分片数据包的棘手问题,试了各种规则都不管用,想请教下大家怎么解决。

我的核心需求

我最终想要实现的是:阻断所有发往特定IPv6地址(比如google.com的2a00:1450:4010:c0e::79)的TCP IPv6分片数据包。这些流量是主机转发来自Docker容器内应用的数据包,最终通过上游接口eth0发往下一跳路由器。

尝试过的无效规则

我先后试了以下几种ip6tables规则,都没法过滤掉目标分片数据包:

# 规则1:在filter表的FORWARD链添加过滤
ip6tables -t filter -A FORWARD -d 2a00:1450:4010:c0e::79/128  -o eth0 -m ipv6header --header ipv6-frag --soft -j DROP

# 规则2:在mangle表的POSTROUTING链添加过滤
ip6tables -t mangle -A POSTROUTING -d 2a00:1450:4010:c0e::79/128  -o eth0 -m ipv6header --header ipv6-frag --soft -j DROP

# 尝试使用frag模块的规则,同样无效

奇怪的现象:tcpdump与ip6tables日志不一致

为了排查问题,我同时用tcpdump抓包和开启ip6tables日志,结果发现两者看到的数据包状态完全不一样:

tcpdump抓包结果(eth0出口方向)

ubuntu@ip-172-31-11-241:/# sudo tcpdump -ni eth0 -Q out ip6 host 2a00:1450:4010:c0e::79
...
15:53:31.088278 IP6 2600:1f16:c97:7a02:f52b:8bb4:1751:e72a.37292 > 2a00:1450:4010:c0e::79.443: Flags [.], ack 2909720068, win 507, options [nop,nop,TS val 751022009 ecr 3180988502], length 0
15:53:31.088597 IP6 2600:1f16:c97:7a02:f52b:8bb4:1751:e72a > 2a00:1450:4010:c0e::79: frag (0|104) 37292 > 443: Flags [P.], seq 1428:1500, ack 1, win 507, options [nop,nop,TS val 751022009 ecr 3180988502], length 72
15:53:31.088781 IP6 2600:1f16:c97:7a02:f52b:8bb4:1751:e72a.37292 > 2a00:1450:4010:c0e::79.443: Flags [.], seq 0:1378, ack 1, win 507, options [nop,nop,TS val 751022009 ecr 3180988502], length 1378
15:53:31.088798 IP6 2600:1f16:c97:7a02:f52b:8bb4:1751:e72a.37292 > 2a00:1450:4010:c0e::79.443: Flags [P.], seq 1378:1500, ack 1, win 507, options [nop,nop,TS val 751022009 ecr 3180988502], length 122
15:53:31.089023 IP6 2600:1f16:c97:7a02:f52b:8bb4:1751:e72a.37292 > 2a00:1450:4010:c0e::79.443: Flags [F.], seq 1500, ack 1, win 507, options [nop,nop,TS val 751022010 ecr 3180988502], length 0

ip6tables日志(开启POSTROUTING链LOG后的dmesg输出)

ubuntu@ip-172-31-11-241:~$ sudo dmesg -T
...
# 包1:显示正常
[Mon Nov 27 15:53:33 2023] IN=docker0 OUT=eth0 PHYSIN=veth9c34169 MAC=02:42:49:39:59:c0:02:42:ac:11:00:02:86:dd SRC=fc00:0000:0000:0000:0000:0242:ac11:0002 DST=2a00:1450:4010:0c0e:0000:0000:0000:0079 LEN=72 TC=0 HOPLIMIT=63 FLOWLBL=191995 PROTO=TCP SPT=37292 DPT=443 WINDOW=507 RES=0x00 ACK URGP=0

# 包2:异常,日志显示长度为1572
[Mon Nov 27 15:53:33 2023] IN=docker0 OUT=eth0 PHYSIN=veth9c34169 MAC=02:42:49:39:59:c0:02:42:ac:11:00:02:86:dd SRC=fc00:0000:0000:0000:0000:0242:ac11:0002 DST=2a00:1450:4010:0c0e:0000:0000:0000:0079 LEN=1572 TC=0 HOPLIMIT=63 FLOWLBL=191995 PROTO=TCP SPT=37292 DPT=443 WINDOW=507 RES=0x00 ACK PSH URGP=0

# 包3:同样异常,长度显示1572
[Mon Nov 27 15:53:33 2023] IN=docker0 OUT=eth0 PHYSIN=veth9c34169 MAC=02:42:49:39:59:c0:02:42:ac:11:00:02:86:dd SRC=fc00:0000:0000:0000:0000:0242:ac11:0002 DST=2a00:1450:4010:0c0e:0000:0000:0000:0079 LEN=1572 TC=0 HOPLIMIT=63 FLOWLBL=191995 PROTO=TCP SPT=37292 DPT=443 WINDOW=507 RES=0x00 ACK PSH URGP=0

# 包4:显示正常
[Mon Nov 27 15:53:33 2023] IN=docker0 OUT=eth0 PHYSIN=veth9c34169 MAC=02:42:49:39:59:c0:02:42:ac11:00:02:86:dd SRC=fc00:0000:0000:0000:0000:0242:ac11:0002 DST=2a00:1450:4010:0c0e:0000:0000:0000:0079 LEN=72 TC=0 HOPLIMIT=63 FLOWLBL=191995 PROTO=TCP SPT=37292 DPT=443 WINDOW=507 RES=0x00 ACK FIN URGP=0

问题细节分析

我仔细对比了两种输出,发现两个关键场景的异常:

  1. 第一次场景:客户端发送两个IPv6包(一个1500字节,一个104字节),因为第一个包超过MTU1450被主机丢弃,第二个包被作为原子IPv6分片转发,但netfilter日志里却把这两个包合并成了一个1572字节的包
  2. 第二次场景:客户端收到路由器的ICMPv6 TOO_BIG后,拆成两个符合MTU的包(1450字节和194字节),tcpdump里能清晰看到这两个独立的包,但netfilter日志里还是显示成一个1572字节的包

我猜测这可能是因为netfilter在处理聚合后的数据包(比如TSO/GSO),导致它根本看不到实际的IPv6分片头?

临时 workaround 但未解决根本问题(UPD1)

我试着在容器内禁用TSO(tcp-segmentation-offload)后,根命名空间的netfilter就能看到正确的两个包,而且内核也不会生成原子IPv6分片了。但这只是临时的解决方案,我还是想搞清楚:

  • 为什么会出现netfilter和tcpdump视角不一致的情况?
  • 有没有办法不用禁用TSO,就能让ip6tables正确过滤主机实际发送的分片数据包?

备注:内容来源于stack exchange,提问作者Kamil Zaripov

火山引擎 最新活动