我使用iptables Trace分析一个网络问题,但是ACK包的记录ID始终为零:
[ 3556.086666] TRACE: raw:OUTPUT:policy:2 IN= OUT=lo SRC=10.233.52.186 DST=10.233.52.186 ID=51512 PROTO=TCP SPT=54186 DPT=80 SYN
[ 3556.086772] TRACE: nat:OUTPUT:rule:1 IN= OUT=lo SRC=10.233.52.186 DST=10.233.52.186 ID=51512 PROTO=TCP SPT=54186 DPT=80 SYN
[ 3556.086784] TRACE: nat:KUBE-SERVICES:rule:1 IN= OUT=lo SRC=10.233.52.186 DST=10.233.52.186 ID=51512 PROTO=TCP SPT=54186 DPT=80 SYN
[ 3556.086793] TRACE: nat:KUBE-MARK-MASQ:rule:1 IN= OUT=lo SRC=10.233.52.186 DST=10.233.52.186 ID=51512 PROTO=TCP SPT=54186 DPT=80 SYN
[ 3556.086803] TRACE: nat:KUBE-MARK-MASQ:return:2 IN= OUT=lo SRC=10.233.52.186 DST=10.233.52.186 ID=51512 PROTO=TCP SPT=54186 DPT=80 SYN MARK=0x4000
[ 3556.086814] TRACE: nat:KUBE-SERVICES:rule:2 IN= OUT=lo SRC=10.233.52.186 DST=10.233.52.186 ID=51512 PROTO=TCP SPT=54186 DPT=80 SYN MARK=0x4000
[ 3556.086825] TRACE: nat:KUBE-NODE-PORT:return:2 IN= OUT=lo SRC=10.233.52.186 DST=10.233.52.186 ID=51512 PROTO=TCP SPT=54186 DPT=80 SYN MARK=0x4000
[ 3556.086846] TRACE: nat:KUBE-SERVICES:rule:3 IN= OUT=lo SRC=10.233.52.186 DST=10.233.52.186 ID=51512 PROTO=TCP SPT=54186 DPT=80 SYN MARK=0x4000
[ 3556.086891] TRACE: raw:OUTPUT:rule:1 IN= OUT=eth0 SRC=10.233.52.186 DST=10.22.46.48 ID=51512 PROTO=TCP SPT=54186 DPT=30080 SYN MARK=0x4000
[ 3556.086900] TRACE: raw:OUTPUT:policy:2 IN= OUT=eth0 SRC=10.233.52.186 DST=10.22.46.48 ID=51512 PROTO=TCP SPT=54186 DPT=30080 SYN MARK=0x4000
[ 3556.086918] TRACE: filter:OUTPUT:policy:1 IN= OUT=eth0 SRC=10.233.52.186 DST=10.22.46.48 ID=51512 PROTO=TCP SPT=54186 DPT=30080 SYN MARK=0x4000
[ 3556.086928] TRACE: nat:POSTROUTING:rule:1 IN= OUT=eth0 SRC=10.233.52.186 DST=10.22.46.48 ID=51512 PROTO=TCP SPT=54186 DPT=30080 SYN MARK=0x4000
[ 3556.086939] TRACE: nat:KUBE-POSTROUTING:rule:3 IN= OUT=eth0 SRC=10.233.52.186 DST=10.22.46.48 ID=51512 PROTO=TCP SPT=54186 DPT=30080 SYN MARK=0x4000
[ 3556.086948] TRACE: nat:KUBE-POSTROUTING:rule:4 IN= OUT=eth0 SRC=10.233.52.186 DST=10.22.46.48 ID=51512 PROTO=TCP SPT=54186 DPT=30080 SYN
[ 3556.087562] TRACE: raw:PREROUTING:policy:2 IN=eth0 OUT= SRC=10.22.46.48 DST=10.22.46.11 ID=0 PROTO=TCP SPT=30080 DPT=39830 SYN
[ 3556.087592] TRACE: filter:INPUT:rule:1 IN=eth0 OUT= SRC=10.22.46.48 DST=10.233.52.186 ID=0 PROTO=TCP SPT=30080 DPT=54186 SYN
[ 3556.087606] TRACE: filter:KUBE-NODE-PORT:return:2 IN=eth0 OUT= SRC=10.22.46.48 DST=10.233.52.186 ID=0 PROTO=TCP SPT=30080 DPT=54186 SYN
[ 3556.087617] TRACE: filter:INPUT:policy:2 IN=eth0 OUT= SRC=10.22.46.48 DST=10.233.52.186 ID=0 PROTO=TCP SPT=30080 DPT=54186 SYN
为什么?
答案1
详细信息描述于RFC 6864:更新了 IPv4 ID 字段的规范。以下所有引用均来自此 RFC,不一定按顺序排列。
IPv4 ID字段用于在重组时区分具有相同偏移但属于两个不同数据包的两个片段。当没有碎片时,就没有什么特别需要照顾的。
由于 SYN 和 SYN/ACK 都设置了不分段 (DF) 标志,因此不可能有分段,因此 ID 变得无用:
>> DF=1 的 IPv4 数据报不得分段。
o 原子数据报:(DF==1)&&(MF==0)&&(frag_offset==0)
在原子数据报中,IPv4 ID字段没有意义;因此,它可以设置为任意值,即
原子数据报不再需要源地址/目标地址/协议元组中的非重复 ID:>> 原始源可以将原子数据报的 IPv4 ID 字段设置为任何值。
Linux 仅对所有 SYN/ACK 使用 0,只要它们具有 DF 标志(我不考虑TCP快速打开当 SYN/ACK 也包含实际数据时,这可能会改变这一点)。
这要求当分段可能时(例如,DF=0)或当分段已经发生时(例如,frag_offset>0或MF=1),ID在源地址/目的地地址/协议元组内是唯一的。
因此,本文档定义该字段的值仅用于碎片和重组:
>>IPv4 ID 字段不得用于分段和重组以外的目的。
RFC 6864 明确指出不应依赖 ID 进行流量识别。
>> 原始源可以将原子数据报的 IPv4 ID 字段设置为任何值。
>> 所有检查 IPv4 标头的设备必须忽略原子数据报的 IPv4 ID 字段。
奖金
提供非常有限的方式来识别流量。这需要nftables(iptables不能这样做)。人们无法通过有效负载修改直接操作 IP 字段,或者无法正确重新计算 IP 校验和。因此,它仅限于使用单个 ID 值分配一次,ip id set
从而正确重新计算校验和。由于右值不能被输入(例如:@th,16,16
gets type tcp dport
),我选择重用客户端源端口的 15 个低位(@th,17,15
保持整数),这是 SYN/ACK 中的目标端口:
customsynackid.nft
:
table ip customsynackid # for idempotence
delete table ip customsynackid # for idempotence
table ip customsynackid {
chain prerouting { type filter hook prerouting priority -310; policy accept;
ip frag-off & 0x4000 != 0 tcp flags syn|ack ip id == 0 ip id set @th,17,15
}
}
这旨在在路由器上完成,例如:K8s 节点,并且可以通过选择应应用它的接口来补充,甚至可能在后路由代替预路由。它可以在终端节点上完成(例如:如果 pod 有能力使用nftables它不会)只需替换hook prerouting
为hook output
.
这设置了id字段到 (TCP协议dport 和 0x7fff)。例如,对从端口 37142 到端口 80(因此从端口 80 到端口 37142)的 TCP 连接的回复将给出 id 字段为 4374 的 SYN/ACK(37142 & 0x7fff = 4374)。