目前,我们在 IPtables 中设置了一些规则,如下所示:
-A PREROUTING -d dstip/32 -p udp -m udp --dport 27035 -m u32 --u32 "0x0>>0x16&0x3c@0x8=0xffffffff&&0x0>>0x16&0x3c@0xc=0x54536f75&&0x0>>0x16&0x3c@0x10=0x72636520&&0x0>>0x16&0x3c@0x14=0x456e6769&&0x0>>0x16&0x3c@0x18=0x6e652051&&0x0>>0x16&0x3c@0x1c=0x75657279" -j REDIRECT --to-ports 21010
这会将包含该有效负载的数据包重定向到我们的缓存程序,这非常有效。但是,只有“新”数据包会命中此 NAT 规则。经过一番查看后,我设法通过设置以下内容解决了这个问题:
sudo sysctl -w net.netfilter.nf_conntrack_udp_timeout=0
sudo sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=0
这引入了另一个问题,所有 UDP 数据包都被设置为此。在小型 UDP 洪水期间,这将导致服务器上的所有 udp 流量停止。
我需要将包含特定负载的所有 UDP 数据包重定向到同一台计算机上的另一个端口。我们有另一个程序正在侦听此端口并回复此有效负载。这是由于查询洪水通常会冻结我们的一个应用程序,因此我们将此查询卸载到另一个程序。
有什么解决办法吗?几个月来一直在寻找,没有任何答案。
答案1
我有两种完全不同的方法来解决这个问题:
iptables 与连线区域
Contrack 区域允许多个 conntrack 实例(每个网络命名空间)。
经过添加 orig-zone 标签对于数据包和流,可以有两个 conntrack 实例将(一半创建的)流分成两部分:正常数据包和匹配数据包,每个部分都有自己的流。因此,通常的 NAT 规则将继续起作用,并且可以发生两次而不是一次:一次针对正常数据包,一次针对匹配数据包。
在对数据包进行一次测试后,将在适当的位置放置一个标记,而不是重复测试。
iptables -t raw -A PREROUTING -d dstip/32 -p udp -m udp --dport 27035 -m u32 --u32 "0x0>>0x16&0x3c@0x8=0xffffffff&&0x0>>0x16&0x3c@0xc=0x54536f75&&0x0>>0x16&0x3c@0x10=0x72636520&&0x0>>0x16&0x3c@0x14=0x456e6769&&0x0>>0x16&0x3c@0x18=0x6e652051&&0x0>>0x16&0x3c@0x1c=0x75657279" -j MARK --set-mark 1
iptables -t raw -A PREROUTING -m mark --mark 1 -j CT --zone-orig 1
iptables -t nat -A PREROUTING -p udp -m mark --mark 1 -j REDIRECT --to-ports 21010
conntrack 结果示例:
# conntrack -E -p udp --orig-port-dst 27035
[NEW] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=52670 dport=27035 [UNREPLIED] src=10.0.3.66 dst=10.0.3.1 sport=27035 dport=52670
[NEW] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=52670 dport=27035 zone-orig=1 [UNREPLIED] src=10.0.3.66 dst=10.0.3.1 sport=21010 dport=52670
[UPDATE] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=52670 dport=27035 src=10.0.3.66 dst=10.0.3.1 sport=27035 dport=52670
[UPDATE] udp 17 30 src=10.0.3.1 dst=10.0.3.66 sport=52670 dport=27035 zone-orig=1 src=10.0.3.66 dst=10.0.3.1 sport=21010 dport=52670
nftables 与不追踪和数据包头字段修改
需要内核 >= 4.10
(当然zone方法可以用nftables实现)。
这里 NAT 是无状态完成的,并且禁用了 conntrack,用于匹配来自重定向端口的传入数据包和所有传出回复数据包。
nft add table ip raw
nft add chain ip raw prerouting '{type filter hook prerouting priority -300;}'
nft add chain ip raw output '{type filter hook output priority -300;}'
nft add rule ip raw prerouting ip daddr dstip/32 udp dport 27035 @th,64,32 == 0xffffffff @th,96,32 == 0x54536f75 @th,128,32 == 0x72636520 @th,160,32 == 0x456e6769 @th,192,32 == 0x6e652051 @th,224,32 == 0x75657279 notrack udp dport set 21010
nft add rule ip raw output udp sport 21010 notrack udp sport set 27035
(请注意,u32
等效的原始有效负载过滤器可以被简化,并且实际上由 nftables 自动简化:看起来 nftablesu128
内部处理,如 所示nft --debug=netlink list ruleset -a
)。