2

I have an SNMP agent listening on a high-level port (16161) and I want to redirect traffic from the standard SNMP agent port 161. Easy iptables rule:

iptables -t nat -A PREROUTING -p udp --dport 161 -j REDIRECT --to-ports 16161

However responses appear to come from port 16161 and are dropped by linux clients.

I want to change the response's source port to 161 (this is also correct per the SNMP spec.) I added:

-t nat -A POSTROUTING -p udp --sport 16161 -j SNAT --to-source :161

and this appears to cause responses to never leave the agent. If I change to-source to any other port, i.e. 162, it works. Or if I use --dport 162 and have the client send the request to port 162, the response can use port 161. Only when the PREROUTING dport matches the POSTROUTING to-source does the response fail to send from the agent.

Example, using PREROUTING --dport 161 and POSTROUTING --to-source 162:

snmpget -v2c -c public $DEVICE_IP iso.3.6.1.2.1.1.5.0

tcpdump from the agent, source port is changed to 162 on the response packet (expected:)

$ 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"

I can even have a PREROUTING and POSTROUTING rule for the same port, but the request can't come over the same port that the response will use:

$ 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

single tcpdump session:

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

How do I get my agent to accept UDP packets on and send replies on the same port using iptables?

2
  • 1
    Have you tried iptables -t nat -A PREROUTING -p udp --dport 161 -j DNAT --to-destination 127.0.0.1:16161 rule only? Jun 26, 2020 at 7:03
  • Thanks @Tero I will try that. I was wondering if maybe REDIRECT was causing conntrack to attempt some stateful handling on the steam that was causing issues with the response.
    – thom_nic
    Jun 26, 2020 at 10:57

2 Answers 2

1

Thanks to Tero's comment on my question, using -j DNAT instead of REDIRECT was the missing sauce. Final solution:

in nat rules:

-A PREROUTING -p udp --dport 161 -j DNAT --to-destination :16161
-A POSTROUTING -p udp --sport 16161 -j SNAT --to-source :161

filter rules:

-A INPUT -p udp --dport 16161 -m conntrack --ctstate DNAT -j ACCEPT

The --ctstate DNAT trick came from this answer so only port 161 accepts connections and not also 16161.

1

This is an alternative UDP redirector for MacOS and Linux; in addition to the usual source / target, it supports specifying the source / destination interfaces, as well as dropping martians (UDP packets arriving from unknown sources).

You could run it as:

./udp-redirect \
   --listen-port 161 \
   --connect-address 127.0.0.1 --connect-port 16161

The SNMP client would send packets to port 161, which would redirect them to port 16161; returning packets would come from port 16161, via the redirector, and be sent out from port 161.

Disclaimer: I am the author, and I am using it right now for this very post, to redirect Wireguard traffic.

1
  • That certainly seems handy if one were not using iptables. 👍
    – thom_nic
    Oct 11, 2023 at 13:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .