Git Product home page Git Product logo

udp-reverse-tunnel's Introduction

UDP reverse tunnel

What kind of problem does this solve?

Background story

I have a wireguard server running at home to be able to enter my home network from the outside. Wireguard is using UDP. Unfortunately I can not just open an IPv4 UDP port in my fritz box like it used to work in the old days because my provider has put me behind CG-NAT, I have to use IPv6 but IPv6 is not yet usable in my workplace.

So at home I have IPv6 and at work I have IPv4 and no way to reach my home network.

I also have a VPS with a public IPv4 to which I could establish reverse ssh tunnels for TCP traffic. Unfortunately the ssh tunnels only support TCP and one should not do VPN connections over TCP anyways, which is the reason wireguard is using only UDP.

Problem

VPN-Server                                 VPN-Client
  (UDP)         |        |                 (IPv4 UDP)
    |           |        | not possible        |
     -----------|--------| <-------------------
                |        |
               NAT     CG-NAT

Solution

VPN-Server                                 VPN-Client
  (UDP)                                    (IPv4 UDP)
    |                                          |
    |                                          |
inside agent                              outside agent
   tunnel                                    tunnel
    ||          |         |                    ||
    ||     punch|    punch|                    ||
    | --------->|-------->|-------------------- |
     -----------|<--------|<--------------------
                |         |
                |         |
               NAT     CG-NAT

The outside agent is running on a VPS with public IPv4

The inside agent will send UDP datagrams to the public IP and port of the outside agent, this will punch holes into both NATs, the outside agent will receive these datagrams and learn from their source address and port how to send datagrams back to the inside agent.

The VPN client can send UDP to the outside agent and these datagrams will be forwarded to the inside agent and from there to the VPN server, the VPN server can reply to the inside agent, these will be forwarded to the outside agent and from there to the VPN client.

The outside agent will appear as the VPN server to the client, and the inside agent will appear as the VPN client to the server.

Multiple client connections are possible because it will use a tunnel and a new socket on the inside agent for every new client connecting on the outside, so to the server it will appear as if multiple clients were running on the inside agent's host.

Installation and Usage

Clone or unzip the code on on both machines and build it. You need at least make and gcc. Enter the source directory and use the command

$ make

After successful build you end up with the binary udp-tunnel in the same folder. Now you can either start it directly from a terminal (with the right options of course) to make a few quick tests, or you can install it with the help of the makefile.

Quick test without installing

The compiled binary udp-tunnel can either act as the inside agent or as the outside agent, depending on what options you pass on the command line.

Assume as an example the VPN server is listening on UDP 1234, running on localhost (same as inside agent) and the outside machine is jump.example.com and we want that to listen on UDP port 9999.

On the inside host we start it with

$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999

On the outside host we start it with

$ ./udp-tunnel -l 9999

Installation with makefile

The makefile contains 3 install targets: install to install only the binary, install-outside and install-inside to also install the systemd service files. The latter two need variables passed to make in order to work properly.

To install the outside agent on on the jump host (assuming you want port 9999) you execute this command:

$ sudo make install-outside listen=9999

This will install the binary into /usr/local/bin/ and install a systemd service file into /etc/systemd/system/ containing the needed command to start it in outside agent mode with port 9999.

To install the inside agent on the inside machine use the following command (assuming as an example your vpn server is localhost:1234 and your jump host is jump.example.com):

$ sudo make install-inside service=localhost:1234 outside=jump.example.com:9999

This will again install the binary into /usr/local/bin/ and a systemd unit file into /etc/systemd/system/

At this point you might want to have a quick look into the systemd unit files to see how the binary is used and to check whether the options are correct. The options should look like described above in the quick test.

After the systemd files are installed and confirmed to be correct they are not yet enabled for autostart, you need to enable and start them. On the Inside machine:

$ sudo systemctl enable udp-tunnel-inside.service
$ sudo systemctl start udp-tunnel-inside.service

and on the outside machine

$ sudo systemctl enable udp-tunnel-outside.service
$ sudo systemctl start udp-tunnel-outside.service

Security

There is no encryption. Packets are forwarded as they are, it is assumed that whatever service you are tunneling knows how to protect or encrypt its data on its own. Usually this is the case for VPN connections.

Additionally an attacker might want to spoof the keepalive packets from the inside agent to confuse the outside agent and divert the tunnel to his own machine, resulting in service disruption. To prevent this very simple attack the keepalive datagrams can be authenticated with a hash based message authentication code. You can use a pre shared password using the -k option on both tunnel ends to activate this feature.

On the inside host you would use it like this

$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999 -k mysecretpassword

On the outside host you would start it with

$ ./udp-tunnel -l 9999 -k mysecretpassword

After you got the installation steps from above successfully working you might want to manually edit your systemd files on both ends and add a -k option, then reload and restart on both ends.

The keepalive message will then contain an SHA-256 over this password and over a strictly increasing nonce that can only be used exactly once to prevent simple replay attacks.

How it works

Both agents maintain a list of connections, each connection stores socket addresses and socket handles assiciated with that particular client conection.

On startup the inside agent will initiate an outgoing tunnel to the outside agent, mark it as unused and send keepalive packets over it. The outside agent will see these packets and add this tunnel with its source address it to its own connection list. The keepalive packets are signed with a nonce and an authentication code, so they cannot be spoofed or replayed.

When a client connects, the outside agent will send the client data down that unused spare tunnel, the inside agent will see this, mark the tunnel as active, create a socket for communicating with the service and forward data in both directions. It will then also immediately create another new outgoing spare tunnel to be ready for the next incoming client.

When the new spare tunnel arrives at the outside agent it will add it to its own connection list to be able to serve the next connecting client immediately.

At this point both agents have 2 tunnels in their list, one is active and one is spare, waiting for the next client connection.

inactivity timeouts will make sure that dead tunnels (those that had been marked active in the past but no client data for some time) will be deleted and their sockets will be closed. The inside agent will detect the lack of forwarded data for prolonged time, remove it from its own list and close its sockets, then some time later the outside agent will detect that there are no keepalives arriving anymore for this conection and remove it from its own list too.

Beware

This code is still highly experimental, so don't base a multi million dollar business on it, at least not yet. It serves the purpuse perfectly well for me, but it might crash and burn and explode your server for you. You have been warned.

udp-reverse-tunnel's People

Contributors

prof7bit avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

udp-reverse-tunnel's Issues

Reconnects and breaks UDP flow

I have two machines (A and B) behind NATs and one public server (S). A and B had constant bidirectional UDP flow (RTP audio) using port forwarding. Now without port forwarding they exchange UDP flow with help of udp-reverse-tunnel as follows:

A --> 5001:S --> 5001:B
A:5002 <-- S:5002 <-- A

OS on the machines:

A: Linux 5.10.103-v7+ armv7l
B: Linux 4.4.202-1237-rockchip-ayufan-gfd4492386213 aarch64
S: Linux 5.10.0-20-amd64 x86_64

The UDP flow from machine A to B works perfectly with no interruptions. The flow from machine B to A interrupts after one-two seconds and may recover after tens of seconds.

The ssh connections are stable. Seems the problem occurs only with UDP tunnel. Changing ports doesn't help.

The logs for the non stable UDP tunnel:

$ udp-tunnel -o 111.111.111.111:5002 -s 127.0.0.1:5002
<6>UDP tunnel inside agent v1.2
<6>building tunnels to outside agent at 111.111.111.111, port 5002
<6>forwarding incomimg UDP to 127.0.0.1, port 5002
<6>creating initial outgoing tunnel
$ udp-tunnel -l 5002
<6>UDP tunnel outside agent v1.2
<6>listening on port 5002
<6>new incoming reverse tunnel from: 222.222.222.222:27110
<6>Total: 1, active: 0, spare: 1
<6>new incoming reverse tunnel from: 222.222.222.222:29156
<6>Total: 2, active: 0, spare: 2
<6>removing connection
<6>Total: 1, active: 0, spare: 1
<6>new incoming reverse tunnel from: 222.222.222.222:11751
<6>Total: 2, active: 0, spare: 2
<6>removing connection
<6>Total: 1, active: 0, spare: 1

Prevent log flooding

The outside agent floods the log when receives UDP packets while there is no any inside agent connected:

new client conection from x.x.x.x:40587
could not find tunnel connection for client, dropping package
new client conection from x.x.x.x:40587
could not find tunnel connection for client, dropping package
new client conection from x.x.x.x:40587
could not find tunnel connection for client, dropping package
...

A better approach would be to show such message just once and wait until at least one tunnel is created, e.g.:

new client conection from x.x.x.x:40587
could not find tunnel connection for client, dropping all following packages
waiting for incoming reverse tunnel

UDP client server

Hello, how should it work?

Server:

./udp-tunnel -l 9092

customer

/udp-tunnel -o 51.79.84.21:9092 -s 127.0.0.1:445 -t 10

127.0.0.1:445

Is the listener for my ovpn server in UDP? true but I can't make a connection on this port

Multiple listen to port?

In my gaming server, i would like to need to open several udp port at once. is it possible to do something like:

udp-tunnel -l 1234 -l 1235

?

Trying to build for openwrt with musl instead of glibc curious how many changes would be necessary

Hey I was hoping to run this on an openwrt router that is built with musl instead of glibc.
I am getting a build error that seems indicative that glibc be used instead of musl.

mipsel-openwrt-linux-musl-gcc -MMD -O3 -flto -Wall -Wextra -DVERSION=1.2 -c main.c -o main.o
mipsel-openwrt-linux-musl-gcc -MMD -O3 -flto -Wall -Wextra -DVERSION=1.2 -c connlist.c -o connlist.o
mipsel-openwrt-linux-musl-gcc -MMD -O3 -flto -Wall -Wextra -DVERSION=1.2 -c args.c -o args.o
args.c:2:10: fatal error: argp.h: No such file or directory
    2 | #include <argp.h>
      |          ^~~~~~~~
compilation terminated.
make: *** [Makefile:44: args.o] Error 1

Before I potentially attempt to work around this error, I'm curious if you know what other dependencies there are on glibc that may make this a difficult. The alternative I'm considering right now is using zerotier with a custom standalone planet server and a custom planet file. I do however like this nationalistic approach as I think there is less room for future potential security issues.

Any suggestions you have would be appreciate.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.