我有一个在高级端口(16161)上监听的 SNMP 代理,我想从标准 SNMP 代理端口 161 重定向流量。简单的 iptables 规则:
iptables -t nat -A PREROUTING -p udp --dport 161 -j REDIRECT --to-ports 16161
然而响应似乎来自端口 16161 并被 Linux 客户端丢弃。
我想将响应的源端口更改为 161(根据 SNMP 规范这也是正确的。)我添加了:
-t nat -A POSTROUTING -p udp --sport 16161 -j SNAT --to-source :161
这似乎导致响应永远不会离开代理。如果我更改to-source
为任何其他端口,即 162,它就可以正常工作。或者,如果我使用--dport 162
并让客户端将请求发送到端口 162,响应可以使用端口 161。只有当 PREROUTINGdport
与 POSTROUTING 匹配时to-source
,响应才会从代理发送失败。
例如,使用 PREROUTING--dport 161
和 POSTROUTING --to-source 162
:
snmpget -v2c -c public $DEVICE_IP iso.3.6.1.2.1.1.5.0
来自代理的 tcpdump,响应数据包上的源端口更改为 162(预期:)
$ tcpdump -n udp and port 161 or port 162 or port 16161
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
19:47:30.755126 IP 192.168.66.3.55843 > 192.168.9.87.161: GetRequest(28) .1.3.6.1.2.1.1.5.0
19:47:30.787415 IP 192.168.9.87.162 > 192.168.66.3.55843: GetResponse(41) .1.3.6.1.2.1.1.5.0="foo"
我甚至可以有针对同一端口的 PREROUTING 和 POSTROUTING 规则,但请求不能通过响应将使用的同一端口进行:
$ iptables -t nat -L -v
Chain PREROUTING (policy ACCEPT 3 packets, 313 bytes)
pkts bytes target prot opt in out source destination
2 142 REDIRECT udp -- any any anywhere anywhere udp dpt:snmp redir ports 16161
0 0 REDIRECT udp -- any any anywhere anywhere udp dpt:snmp-trap redir ports 16161
Chain INPUT (policy ACCEPT 3 packets, 313 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
2 168 SNAT udp -- any any anywhere anywhere udp spt:16161 to::162
单个 tcpdump 会话:
tcpdump -n udp and port 161 or port 162 or port 16161
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
>>>>>>> made request to port 161, response over 162, OK <<<<<<<<<
19:55:10.709670 IP 192.168.66.3.51621 > 192.168.9.87.161: GetRequest(28) .1.3.6.1.2.1.1.5.0
19:55:10.751607 IP 192.168.9.87.162 > 192.168.66.3.51621: GetResponse(41) .1.3.6.1.2.1.1.5.0="foo"
>>>>>>> made request to port 162, response not sent <<<<<<<<<
19:55:20.372213 IP 192.168.66.3.55108 > 192.168.9.87.162: GetRequest(28) .1.3.6.1.2.1.1.5.0
19:55:21.378445 IP 192.168.66.3.55108 > 192.168.9.87.162: GetRequest(28) .1.3.6.1.2.1.1.5.0
19:55:22.380925 IP 192.168.66.3.55108 > 192.168.9.87.162: GetRequest(28) .1.3.6.1.2.1.1.5.0
19:55:23.390915 IP 192.168.66.3.55108 > 192.168.9.87.162: GetRequest(28) .1.3.6.1.2.1.1.5.0
19:55:24.393482 IP 192.168.66.3.55108 > 192.168.9.87.162: GetRequest(28) .1.3.6.1.2.1.1.5.0
19:55:25.397306 IP 192.168.66.3.55108 > 192.168.9.87.162: GetRequest(28) .1.3.6.1.2.1.1.5.0
如何让我的代理使用 iptables 在同一端口上接受 UDP 数据包并发送回复?
答案1
感谢 Tero 对我的问题的评论,使用-j DNAT
代替REDIRECT
是缺少的调味料。最终解决方案:
在 nat 规则中:
-A PREROUTING -p udp --dport 161 -j DNAT --to-destination :16161
-A POSTROUTING -p udp --sport 16161 -j SNAT --to-source :161
过滤规则:
-A INPUT -p udp --dport 16161 -m conntrack --ctstate DNAT -j ACCEPT
诀窍--ctstate DNAT
来自这个答案因此只有端口 161 接受连接,而不接受 16161。
答案2
这是一适用于 MacOS 和 Linux 的替代 UDP 重定向器;除了通常的源/目标之外,它还支持指定源/目标接口,以及丢弃火星人(来自未知来源的 UDP 数据包)。
您可以这样运行它:
./udp-redirect \
--listen-port 161 \
--connect-address 127.0.0.1 --connect-port 16161
SNMP 客户端会将数据包发送到端口 161,然后端口 161 会将数据包重定向到端口 16161;返回的数据包会从端口 16161 经过重定向器发送出去。
免责声明:我是作者,我现在正在这篇文章中使用它来重定向 Wireguard 流量。