![iptables / nftables:将 UDP 数据转发到多个目标](https://linux22.com/image/1634773/iptables%20%2F%20nftables%EF%BC%9A%E5%B0%86%20UDP%20%E6%95%B0%E6%8D%AE%E8%BD%AC%E5%8F%91%E5%88%B0%E5%A4%9A%E4%B8%AA%E7%9B%AE%E6%A0%87.png)
我需要为以下场景创建 iptables 规则:
- 不同主机向主机发送UDP数据
A
,目标端口为1234
。 - 主机
A
(8.2.3.4)将接收到的 UDP 数据重定向到主机B1
(7.2.3.1)、B2
(22.93.12.3)、……Bn
(12.42.1.3);IP 地址仅用于说明。 - 这与负载平衡无关,因此每个主机
B1
,,B2
...Bn
必须接收全部包。因此,主机A
必须复制包裹。 - 转发的包必须有正确的目标IP(
B1
,B2
, ...Bn
)和源IP(主机A
) - 我无法更改向主机发送数据的初始主机上的任何内容
A
- 我无法在目标主机上更改任何内容
B1
,,B2
...Bn
- 主持人
B1
,,B2
...Bn
不必能够回答
我尝试用PREROUTING
/来解决这个问题mangle
:
HOST_A=8.2.3.4
HOST_B1=7.2.3.1
HOST_B2=22.93.12.3
...
HOST_BN=12.42.1.3
iptables -F -t mangle
iptables -t mangle -A PREROUTING -d $HOST_A -p udp --dport 1234 -j TEE --gateway $HOST_B1
iptables -t mangle -A PREROUTING -d $HOST_A -p udp --dport 1234 -j TEE --gateway $HOST_B2
...
iptables -t mangle -A PREROUTING -d $HOST_A -p udp --dport 1234 -j TEE --gateway $HOST_BN
iptables -L -t mangle
主机B1
、B2
、 ...Bn
似乎没有收到数据。有人知道哪里出了问题吗?调试实际上相当棘手(我发现没有真正的方法可以做到这一点)。
谢谢
答案1
取样器
有一个工具可以满足所有条件,取样器:
UDP 采样器
这个小程序在给定端口上接收 UDP 数据报,并将这些数据报重新发送到指定的一组接收器。此外,可以为每个接收器单独指定一个采样因子 N,这样接收器将只接收 N 个接收到的数据包中的一个。
在主机 A 上运行的示例命令与 OP 针对目的地 B1 B2 和 Bn 的示例相匹配:
samplicate -p 1234 7.2.3.1/1234 22.93.12.3/1234 12.42.1.3/1234
这或许是更合理的做法。
nftables
对于不合理的方法,这可以在内核中完成,或者使用iptables有困难(例如,在我的答案中只有两个重复UL SE 问答)或nftables可以做无状态 NAT避免与以下相关的一些复杂性连接跟踪区域。
DUP 声明
dup 语句用于复制数据包并将副本发送到不同的目的地。
重复 设备
重复 地址 设备 设备
注意:第一个语法不能在系列中使用ip
,而只能在netdev
系列中使用,这会增加多个问题,其中之一就是必须猜测正确的MAC地址(在以太网接口的情况下),这也需要进行修改,因此变得不太实用。
重复其行为与球座:必须立即将重复项“撤离”到接口上的网关(网关意味着 IPv4 地址,如果与接口类型相关,则仅用于使用 ARP 解析目标 MAC 地址,但不在 IP 数据包本身中)以避免被路由堆栈的其余部分异常处理。最终目的地不能是网关地址(除非恰巧在同一个局域网内)。
这里进行了特殊的路由设置,以便将已经无国籍将经过 SNAT 处理(到 A 自己的地址)/ DNAT 处理(到最终目的地)的数据包发送到主机本身,以便主机进一步将这些数据包路由到目的地,就好像这些数据包实际上是由主机自己发出的一样。这需要接受接收自己的本地 IP 地址,并禁用所有反向路径过滤器在接收韦特接口(因此需要在全部设置)。它仍然可以在eth0如果需要的话。
在主机 A 上进行设置,除了其初始运行的网络配置(具有单个接口eth0):
HOST_A=8.2.3.4
ip link add name vethinj up type veth peer name vethgw
ip link set vethgw up
sysctl -w net.ipv4.conf.vethgw.forwarding=1
sysctl -w net.ipv4.conf.vethgw.accept_local=1
sysctl -w net.ipv4.conf.vethgw.rp_filter=0
sysctl -w net.ipv4.conf.all.rp_filter=0
ip route add $HOST_A/32 dev vethinj
没有额外的 IP 地址可供分配。Linux 使用弱主机模型,IP 地址eth0可在韋斯因此可以作为网关通过韦希尼一旦路线被添加。
这nftables文件中的规则multiply.nft
,下面要加载的nft -f multiply.nft
是:
- 匹配数据包以进行乘法
- 做一个无国籍者SNAT(
ip saddr set
)到 A 自己的地址。这不涉及连接跟踪的 NAT。 - 对于每个目的地Bx,对目的地HOST_Bx进行无状态DNAT(
ip daddr set
),并将数据包复制到注入侧韦特对,使用A自己的地址作为网关。 - 删除剩余的已更改的原件,因为它不再有用。
multiply.nft
:
define HOST_A=8.2.3.4
define HOST_B1=7.2.3.1
define HOST_B2=22.93.12.3
define HOST_Bn=12.42.1.3
table ip multiply
delete table ip multiply
table ip multiply {
chain c {
type filter hook prerouting priority -300; policy accept;
iif != vethgw ip daddr $HOST_A udp dport 1234 ip saddr set $HOST_A goto cmultiply
}
chain cmultiply {
jump cdnatdup
drop
}
chain cdnatdup {
ip daddr set $HOST_B1 dup to $HOST_A device vethinj
ip daddr set $HOST_B2 dup to $HOST_A device vethinj
ip daddr set $HOST_Bn dup to $HOST_A device vethinj
}
}
要在此之后添加新的目的地 192.0.2.2,可以手动执行以下操作:
nft add rule ip multiply cdnatdup ip daddr set 192.0.2.2 dup to 8.2.3.4 device vethinj
下面是来自 S2 并复制到 B1 B2 和 Bn 的单个数据包的文本示意图:
S1 →┄┄┄┄┄╮ ╭┄┄┄┄┄┄┄┄→ B1
S2 →┄┄┄╮ ┊ ┊ ╭┄┄┄┄┄┄→ B2
... ┊ ┊ ┊ ┊ ...
Sn →┄╮ ┊ ┊ ┊ ┊ ╭┄┄┄┄→ Bn
┊ ┊ ╰┄┄ ┄ ┌──────┐↗┄┄┄┄╯ ┊ ┊
┊ ╰┄┄┄┄┄┄┄┄┄┄┄┄→│ eth0 │→┄┄┄┄┄┄╯ ┊
╰┄┄┄┄┄┄ ┄ └──────┘↘┄┄┄┄┄┄┄┄╯
↙ ↖↖↖
snat ↯ ↑↑↑ internal
nftables: dnat ⇊ A ↑↑↑ routing
& dup ↓↓↓ ↑↑↑
┌───────┐ ┌───────┐
│vethinj│ │vethgw │
└───────┘ └───────┘
╰─── ⇶ ───╯
virtual wire
注意:由于此 NAT 不是由连接跟踪B1 B2...Bn 不可能回答原始来源,但是 OP 放弃了这一权利。