IP over UDP tunnel with socat
Published:
Updated:
A simple way to create IP over UDP tunnels using socat
.
This is the protocol stack we are going to implement:
[ IP ] [ UDP ] [ IP ]
Warning: no protection!
This does not provide any protection (encryption, authentication)!
In order to create a tunnel, we must create a TUN device interface. A TUN device is a network device managed by a userspace program:
- when the kernel sends an IP packet to this device, it is sent to the userspace program which can do anything with it (like sending it in a tunnel);
- conversely, the userpace program can send packets to the kernel through this network device.
We would like to have a simple program which manages such a TUN device by encapsulating them over a UDP socket.
Using socat
It turns out socat
can do this already!
On the first host:
sudo socat UDP:192.0.2.2:9000,bind=192.0.2.1:9000 \
TUN:10.0.1.1/24,tun-name=tundudp,iff-no-pi,tun-type=tun,su=$USER,iff-up
On the second one:
sudo socat UDP:192.0.2.1:9000,bind=192.0.2.2:9000 \
TUN:10.0.1.2/24,tun-name=tundudp,iff-no-pi,tun-type=tun,su=$USER,iff-up
Explanations:
- the
UDP
type creates aconnect()
-ed UDP socket which means only the packets coming from this specific remote UDP endpoint will be accepted; iff-no-pi
sets theIFF_NO_PI
flag which disables some encapsulation;tun-type=tun
asks an IP-based devices (as opposed fortun-type=tap
for Ethernet-based device which would implement an Ethernet over UDP tunnel);iff-up
sets the interface up;su=$USER
changes the user of the process (instead of usingroot
).
Now we can ping over the tunnel:
host1:~$ ping 10.0.1.2
PING 10.0.1.2 (10.0.1.2) 56(84) bytes of data.
64 bytes from 10.0.1.2: icmp_seq=1 ttl=64 time=39.3 ms
64 bytes from 10.0.1.2: icmp_seq=2 ttl=64 time=40.1 ms
host1:~$ ip route get 10.0.1.2
10.0.1.2 dev tunudp src 10.0.1.1
cache
You can add IPv6 addresses to the tunnel and it works as expected: both IPv4 and IPv6 packets are sent over the same UDP socket and the version field is used to distinguish them.
Using ip fou
The other solution is to use ip fou
(for foo over UDP):
modprobe fou
ip fou add port 9000 ipproto 4
ip link add name tunudp type ipip \
remote 192.168.2.2 local 192.168.2.1 ttl 225 \
encap fou \
encap-sport auto encap-dport 9000
We can expect better performance as its handled completely in the kernel side. The downside is that you have to create two different tunnels (one for encapsulating IPv4 and the other for IPv6).