Forward UDP Traffic Through SSH with socat

SSH port forwards only carry TCP — the protocol has no UDP channel type. To reach a UDP service (SNMP on 161/udp, DNS, TFTP) through an SSH pivot, the standard workaround is a pair of socat relays that convert UDP↔TCP on each side of the tunnel.

socat (“SOcket CAT”) establishes two bidirectional byte streams and shuttles data between them, and its address types include both UDP/UDP4-LISTEN and TCP/TCP4-LISTEN — so one instance can terminate a UDP datagram flow and re-emit it as a TCP stream, and a mirror instance can reverse the transformation at the far end.

The pattern

Three moving parts: a socat UDP→TCP bridge on the pivot, an ordinary SSH local forward through the pivot, and a socat TCP→UDP bridge on your own machine.

# 1. On the pivot host: accept TCP on 42061 (loopback only) and
#    forward each payload as UDP to the target service.
#    bind=127.0.0.1 keeps the listener off external interfaces —
#    an unexpected 0.0.0.0 listener is exactly what defenders scan for.
socat TCP4-LISTEN:42061,reuseaddr,fork,bind=127.0.0.1 UDP:remote.host:161 &
 
# 2. From your machine: ordinary local forward through the pivot
#    (or add it at runtime via the ~C escape command line).
ssh -L 42061:localhost:42061 user@pivot.host
 
# 3. On your machine: listen for UDP on the service's real port and
#    push datagrams into the TCP tunnel. Privileged ports (<1024)
#    need root for the listener.
sudo socat UDP4-LISTEN:161,reuseaddr,fork,bind=127.0.0.1 TCP:localhost:42061 &

Note the symmetry: the two socat invocations are mirror images, and the SSH forward in the middle is a vanilla -L. Once it’s up, point your UDP tools at localhost:161 as if the service were local.

Options that matter

  • reuseaddr — lets the listener rebind immediately after restart (SO_REUSEADDR); essential when iterating.
  • fork — handle each association in a new child process, so one conversation can’t block the listener. Without it, the relay dies or stalls after the first flow.
  • bind=127.0.0.1 — scope the listener to loopback. On the pivot this keeps the TCP side invisible to anyone but the SSH tunnel; on your machine it keeps the fake UDP service from being offered to your local network.

Caveats

  • UDP semantics don’t survive perfectly: datagram boundaries, ICMP errors, and source-port expectations can confuse protocols that do more than simple request/response. SNMP walks and DNS queries generally work; anything with complex session behavior may not.
  • Each hop adds latency and a process to keep alive. For heavy or long-lived use, a real UDP-capable tunnel (e.g. a VPN-style tool) is less fragile than the socat sandwich.

Sources

  • ssh — the port-forwarding primitives this builds on
  • socat — the anything-to-anything connector used here
  • udp — the connectionless protocol this pattern exists to tunnel
  • netsh-windows-firewall — when the pivot is Windows and forwarded ports must be reachable by other hosts