如果数据包包含例如字符串test
,我需要将所有目标端口为 15000 的 UDP 数据包重定向到端口 15001。我有以下两个简单规则:
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 15000 -m string --string 'test' --algo bm -j LOG --log-prefix='[netfilter] '
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 15000 -m string --string 'test' --algo bm -j REDIRECT --to-ports 15001
奇怪的行为:
- 如果第一个数据包包含
test
字符串,则重定向 全部连接的数据包; - 如果连接的第一个数据包不包含
test
,则永远不会进行重定向,即使后续数据包包含test
但是所有符合规则的数据包都被正确记录。
我还尝试将轨道信息添加到规则中:
-m state --state NEW,ESTABLISHED
但行为是一样的。有什么想法吗?
这是完整的iptables
规则集:
过滤表:
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
nat 表:
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
LOG udp -- anywhere anywhere udp dpt:15000 STRING match "test" ALGO name bm TO 65535 LOG level warning prefix "[netfilter] "
REDIRECT udp -- anywhere anywhere udp dpt:15000 STRING match "test" ALGO name bm TO 65535 redir ports 15001
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
混淆表:
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain FORWARD (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
原始表:
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
答案1
nat 表规则始终仅适用于连接中的第一个数据包。同一连接的后续数据包永远不会遍历 nat 规则列表,并且仅由 conntrack 代码支持
由于 UDP 本质上是无连接的,因此这里的“连接”仅由地址、端口和超时定义。因此,如果第二个具有相同源端口和地址以及相同目标端口和地址的 UDP 数据包在超时内到达,Linux 会认为它属于已建立的“连接”,并且根本不会评估 nat 规则表,而是重复使用对前一个数据包发出的判决。
看这里:http://www.netfilter.org/documentation/HOWTO/netfilter-hacking-HOWTO-3.html
答案2
这是因为 iptables 在PREROUTING
链上应用了连接跟踪。每当建立新连接时,iptables 都会查阅 conntrack 缓存。如果找到匹配项,则不会从 nat 表中应用任何规则。
如果您想禁用更改此行为,请查看NOTRACK
原始表中的目标。
请注意,这甚至适用于 UDP(无连接协议)。第一个数据包被视为打开连接NEW
,另一个数据包被视为回复ESTABLISHED
。
我找到了一篇相关文章服务器故障。
答案3
iptables 与连接跟踪区域
这conntrack 区域功能允许将两个相同的 conntrack 5-uples(或其中的一部分)视为不同的区属性。这通常可能与基于复杂策略的路由一起使用,处理相同的 IP,但流经不同的路径(路由),以防止 conntrack 合并来自不同路径的不相关流。
这里可以用它来解决这个问题:考虑UDP数据包没有test
和数据包必须test
是两个不同来源区域的一部分(通过使用CT --zone-orig
):正常区域和重定向区域。每个源区域将允许状态中的 conntrack 条目NEW
不发生冲突并被视为独立。假设钩子中有两个 nat 表PREROUTING
:规则评估不是nat
针对流的第一个数据包进行一次,而是针对第一个正常数据包进行一次,针对第一个test
数据包进行一次。
虽然这不是真正需要的,但为了避免更多的重复规则,我会设置一个标记并在以后重复使用它以简化。
iptables -t raw -A PREROUTING -i eth0 -p udp --dport 15000 -m string --string 'test' --algo bm -j MARK --set-mark 1
iptables -t raw -A PREROUTING -m mark --mark 1 -j CT --zone-orig 1
iptables -t nat -A PREROUTING -m mark --mark 1 -j LOG --log-prefix='[netfilter] '
iptables -t nat -A PREROUTING -p udp -m mark --mark 1 -j REDIRECT --to-ports 15001
测试(服务器 IP 为10.0.3.66
)。按时间顺序,在查询之后输入的答案(来自术语 1 的答案 1 和来自术语 2 的答案 2):
serverterm1$ socat udp4-listen:15000,reuseaddr,fork -
query1normal
query3normal
answer1
serverterm2$ socat udp4-listen:15001,reuseaddr,fork -
query2test
query4test
answer2
client$ socat udp4:10.0.3.66:15000 -
query1normal
query2test
query3normal
query4test
answer1
answer2
serverterm3# conntrack -E -p udp --orig-port-dst 15000
[NEW] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 [UNREPLIED] src=10.0.3.66 dst=10.0.3.1 sport=15000 dport=33150
[NEW] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 zone-orig=1 [UNREPLIED] src=10.0.3.66 dst=10.0.3.1 sport=15001 dport=33150
[UPDATE] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 src=10.0.3.66 dst=10.0.3.1 sport=15000 dport=33150
[UPDATE] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=33150 dport=15000 zone-orig=1 src=10.0.3.66 dst=10.0.3.1 sport=15001 dport=33150