105

The title pretty much sums it up. I would like to send UDP traffic through a SSH tunnel. Specifically, I need to be able to send UDP packets through the tunnel and have the server be able to send them back to me on the other side. I know how to do it for TCP connections. Is this it possible with UDP?

3

9 Answers 9

58

This small guide tells you how to send UDP traffic via SSH using tools that come standard (ssh,nc,mkfifo) with most UNIX-like operating systems.

Performing UDP tunneling through an SSH connection

Step by step

Open a TCP forward port with your SSH connection

On your local machine (local), connect to the distant machine (server) by SSH, with the additional -L option so that SSH will do TCP port-forwarding:

local# ssh -L 6667:localhost:6667 server.foo.com

This will allow TCP connections on the port number 6667 of your local machine to be forwarded to the port number 6667 on server.foo.com through the secure channel.

Setup the TCP to UDP forward on the server

On the server, we open a listener on the TCP port 6667 which will forward data to UDP port 53 of a specified IP. If you want to do DNS forwarding like me, you can take the first nameserver's IP you will find in /etc/resolv.conf.

But first, we need to create a fifo. The fifo is necessary to have two-way communications between the two channels. A simple shell pipe would only communicate left process' standard output to right process' standard input.

server# mkfifo /tmp/fifo
server# nc -l -p 6667 < /tmp/fifo | nc -u 192.168.1.1 53 > /tmp/fifo

This will allow TCP traffic on server's port 6667 to be forwarded to UDP traffic on 192.168.1.1's port 53, and responses to come back.

Setup the UDP to TCP forward on your machine

Now, we need to do the opposite of what was done above on the local machine. You need priviledged access to bind the UDP port 53.

local# mkfifo /tmp/fifo
local# sudo nc -l -u -p 53 < /tmp/fifo | nc localhost 6667 > /tmp/fifo

This will allow UDP traffic on local machine's port 53 to be forwarded to TCP traffic on local machine's port 6667. Enjoy your local DNS server :)

As you've probably guessed, when a DNS query will be performed on the local machine, e.g. on local UDP port 53, it will be forwarded to local TCP port 6667, then to server's TCP port 6667, then to server's DNS server, UDP port 53 of 192.168.1.1. To enjoy DNS services on your local machine, put the following line as first nameserver in your /etc/resolv.conf:

nameserver 127.0.0.1
7
  • 45
    This solution is not safe. TCP streams are not guaranteed to preserve message boundaries, so a single UDP datagram may be split in parts, breaking any protocol. Aug 8, 2011 at 15:50
  • 3
    It could also be nice to use port 1153 instead of 6667 (from man SSH example), which is used by IRC. May 30, 2012 at 14:04
  • 2
    @JuhoÖstman Thanks for pointing out this pitfall. Being aware of the problem... have you run accros a solution? would small enough messages be a way to make it likely to work? Mar 29, 2013 at 7:42
  • 6
    The solution would be to prepend a length to each packet before they are sent through the TCP stream, and reconstruct the original packets from that. It would easy to write a C script to do that, but I am not sure if a readily-available solution exists. In practice, the TCP usually seems to preserve message boundaries in this case, but strange failures can occur at any time. Apr 23, 2013 at 9:33
  • I faced another problem with this: the pipe and fifo are buffered so frame boundaries are lost. So many UDP frames can be concatenated into one TCP frame. Jul 20, 2015 at 14:47
51

This example (I think John's answer points the the same thing at a different place), describes how to access another machine's UDP/DNS services over an TCP/SSH connection.

We will forward local UDP/53 traffic to TCP, then TCP traffic with the port-forwarding mechanism of SSH to the other machine, then TCP to UDP/53 on the other end.
Typically, you can do it with openvpn.
But here, we'll do it with simpler tools, only openssh and netcat.

At the end of that page, is another comment with a reference to 'socat',
The same UDP/DNS access is made with,

Server side: socat tcp4-listen:5353,reuseaddr,fork UDP:nameserver:53
Client side: socat udp4-listen:53,reuseaddr,fork tcp:localhost:5353

Refer socat examples for more.

2
  • 5
    This one seems much more useful for me than the accepted answer. I needed a unidirectional redirection of video stream (TS/UDP)... ssh orig_strm_src socat udp4-listen:4003,reuseaddr,fork STDOUT| socat STDIN udp-sendto:localhost:4003
    – nhed
    Jul 24, 2012 at 23:11
  • 5
    FWIW, I've written a more detailed guide on my home page describing how to set up socat over SSH for UDP forwarding. It uses SNMP as an example. Oct 8, 2013 at 5:44
29

SSH (at least OpenSSH) has support for simple VPNs. Using the -w or Tunnel option in the ssh client, you can create a tun device at both ends, which can be used to forward any kind of IP traffic. (See also Tunnel in the manual page of ssh_config(5).) Note that this requires OpenSSH (and probably root privileges) at both ends.

7
  • 1
    This requires root privileges on remote machine even for UDP tunneling of non-priviledged ports and PermitRootLogin set to not 'no'. Too bad. May 30, 2012 at 14:03
  • 7
    @humanityANDpeace: You can precreate the tun/tap devices and make them owned by a specific user, using ip tuntap add. Nov 6, 2013 at 18:08
  • 1
    Hi @grawity. I like your solution but cannot make it work. I precreated the tun device as follows: sudo ip tuntap add mode tun but when ever using the -w option like this: ssh $Server -w $port I get Tunnel device open failed. Could not request tunnel forwarding. What am I doing wrong? Aug 13, 2018 at 14:54
  • 5
    It would be very helpful to expand this answer from "you can do it" to show how exactly you can do it, which commands to execute Apr 2, 2022 at 18:53
  • 1
    "requires ... PermitRootLogin set to not 'no'." — @philpirozhkov no it doesn't, you can use "PermitRootLogin without-password" and SSH keys May 21, 2022 at 5:01
28

Or you could simply use ssf (which was designed to handle this use case), with a simple command:


Client side:

#>./ssfc -U 53:192.168.1.1:53 server.foo.com

This command redirects local port 53 (dns) to 192.168.1.1 port 53, through a secure tunnel between localhost and server.foo.com.


You will need a ssf server (instead of - or next to - your ssh server):

#>./ssfs

By the way, both client and server side of ssf work on Windows / Linux / Mac. This is a userland application, so you don't need tun/tap or VPN.

To redirect port 53, you will need administrative privileges - regardless of the tool you're using.

For more info, details, use case, or download: https://securesocketfunneling.github.io/ssf/

7
  • 12
    At most, it's a shameless plug. Anyway the solution may fit your request. By the way, SSF is OpenSource and non profit. Jun 15, 2015 at 7:53
  • 33
    @heavyd: If he posted a bunch of hacky scripts, it would be acceptable, but because he made a mature open-source tool, it's not? It answers the original question perfectly.
    – Synthead
    Jan 19, 2016 at 3:59
  • 3
    This worked perfectly for my needs. Specifically forwarding UDP ports. Jan 13, 2020 at 15:52
  • 1
    seems like ssfc would be nice, but Windows Defender identifies the binaries as a Trojan (as obtained from this website in answer above). I've looked into compiling it myself but can't really be bothered now.
    – RexBarker
    Jun 2, 2021 at 14:17
  • 2
    Would have loved to try it, but it hasn't been maintained in years. I couldn't get it to compile on recent Ubuntu and Manjaro. Nov 13, 2021 at 7:23
18

I couldn't get nc to work for SNMP, because SNMP clients keep choosing a new source UDP port, and several can be active at once.

Instead, I've written a post describing how to do it with socat in this blog post, using SNMP as an example. Essentially, using two terminals, starting with an overview:

overview

Terminal one:

client$ ssh -L 10000:localhost:10000 server
server$ socat -T10 TCP4-LISTEN:10000,fork UDP4:switch:161

This creates the SSH forwarding of TCP port 10000 and runs socat on the server. Notice how the switch’s IP address is mentioned in the socat command line as “switch”.

Terminal two:

client$ sudo socat UDP4-LISTEN:161,fork TCP4:localhost:10000

That sets up socat on the client. That should do it.

6
  • 2
    this worked for me :) Apr 10, 2018 at 13:42
  • Why don't you need a -R remote tunnel to tunnel the answer from the UDP server ("switch") back the UDP client ("manager")?
    – bers
    Apr 17, 2020 at 16:33
  • @bers: socat does that for me Apr 20, 2020 at 7:37
  • @PeterV.Mørch alright, thanks. It seems socat keeps the existing connection open long enough to tunnel the answer back the same way the request came. Thanks!
    – bers
    Apr 20, 2020 at 7:56
  • Exactly! And this is the main improvement of socat over nc. nc will do it for one single UDP port combination, which means it will work for SNMP for "some time" until the SNMP manager chooses another source port (which it is free to do for every request). Socat handles that. nc doesn't. So with nc, SNMP forwarding will work "for a little while" only. And not at all with parallel requests. Apr 20, 2020 at 9:21
5

A VPN is a better solution if you have access to an UDP port.

If you only have access to the TCP SSH port, then an SSH tunnel is as good as a VPN, at least for ping and packet backtracking.

1
  • 1
    A ssh-only solution won't do it. ssh can only forward tcp traffic. For udp you'll need to tunnel your udp packets over one or more tcp-sockets like they do in the other answers.
    – Garo
    Feb 27, 2021 at 0:44
3

on ssh server:

sudo ip tuntap add dev tun7 mode tun user SSHUSER
sudo ip addr add 192.168.7.1/30 dev tun7
sudo iptables -t nat -A PREROUTING -p udp -i eth0 --dport PUBLIC_PORT -j DNAT --to-destination 192.168.7.2:LOCAL_PORT
sudo iptables -t filter -A FORWARD -i eth0 -p udp -d 192.168.7.2 --dport LOCAL_PORT -j ACCEPT
sudo iptables -t filter -A FORWARD -i tun7 -o eth0 -j ACCEPT
sudo iptables -t nat -A POSTROUTING -o tun7 -j MASQUERADE
# set "PermitTunnel point-to-point" for user SSHUSER in /etc/ssh/sshd_config
sudo sshd -t && sudo systemctl restart sshd

replace eth0 with the interface connected to the internet

on ssh client running udp server:

sudo ip tuntap add dev tun8 mode tun user LOCALUSER
sudo ip addr add 192.168.7.2/30 dev tun8
ssh -v -w 8:7 SSHUSER@REMOTEHOST

you might also need sudo ip link set tun7 up on the remote host and sudo ip link set tun8 up on the local host.

inspired by comment UDP traffic through SSH tunnel

0

An experienced red teamer got this to work and proven by wireshark, but had to change the UDP packets into TCP packets and then forward them into the ssh tunnel and then convert them from TCP packets back into UDP packets to the listener UDP port of the application.

2
  • 1
    Please see How do I write a good answer in the Help -section. I'd suggest editing the answer to include how exactly the issue was resolved. As it is it's not very helpful, and will likely be flagged for length and quality, and will likely be deleted. This would be OK as a comment, but you will need to collect some reputation before you're allowed to comment. Oct 6, 2021 at 14:23
  • @Peregrino69, acctually it is a useful answer. It's very useful to know that UDP packets are not forwardable on their own. Then one can decide what approach to use. For example, I can ask my software that needs tunnel to use TCP and other answers I need to decipher to understand that. I don't need other tools. Oct 27, 2021 at 15:23
0

Use sshuttle to set up a VPN over SSH without the hassle of manually configuring tunnel mode. It supports UDP.

1
  • 1
    we need more info
    – emoxam
    Feb 9, 2023 at 14:06

You must log in to answer this question.

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