Lightweight non-encrypted low-overhead L3 IPv4 tunnel natively supported by Linux.
The name is an acronym of IPIP-over-UDP which is effectively IPIP-over-FOU, where FOU is Foo-over-UDP.
ipipou utility helps to create such tunnels and adds optional remote side authentication using known remote IP:port and/or one of supported auth schemes:
-
plaintext token
-
public/verify key of the remote side (only ed25519 supported so far) and optional shared secret
IPIP-over-UDP in general and this tool in particular has some advantages over pure IPIP tunnel:
-
In the global net tangible part of hardware optimized for 3 most popular protocols (TCP, UDP, ICMP), so other IP based protocols may have worse performance or disallowed at all. Being encapsulated in UDP IPIP tunnel going to be more stable and often faster.
-
It's possible to create such tunnel even if some host is behind NAT (pure IPIP requires public IPs on both sides). Sometimes it's possible to create it when both sides are behind NAT (using stun and other techniques: not supported by this tool yet).
-
Authentication support for remote IP:port connection. It's useful if one of the hosts is behind NAT.
-
Multiple tunnels can be created between the same public IPs pair (e.g. when multiple clients share the same public IP). To support it this implementation relies on local NAT configuration using additional private IP. In the future Linux may start to support creating FOU tunnels to the same destination IP but different port natively without this NAT hack, or may not...
-
Live roaming support. Client side of connection may change its public IP and/or port, after [re]authentication packet tunnel will be reconfigured to use updated IP:port (WireGuard behaves similar way).
In comparison with encrypted tunnels (like WireGuard) IPIP-over-UDP has lower overhead and going to have higher throughput, lower latency and CPU usage, while still supports weak authentication. It can be better choice than WireGuard when tunnel level encryption is not required and better to be avoided, e.g. if inner layer going to be encrypted itself (HTTPS, SSH, VPN, etc.), or you prefer performance over security.
Nevertheless as opposed to WireGuard FOU supported by Linux only AFAIK.
Linux 4.15+ (not tested with lower versions, 5.4+ recommended) built with support of IPIP and FOU, nftables, netfilter queue, python 3.6+ (not tested with lower versions).
Internally ipipou tool relies on the following binaries: ip, nft for server mode, optionally modprobe and conntrack
External python3 libraries will be required:
netfilterqueuefor server modenaclwhen ed25519 cryptography used for auth
Some checks relies on procfs and sysfs but fallback to other ways if does not exist.
ipipou must be run with root privileges OR have the following capabilities:
- CAP_NET_ADMIN - to create and configure network interfaces
- CAP_NET_RAW - if authentication packets sending required
- CAP_SYS_MODULE - if "fou" and "ipip" modules are not loaded yet
Example for Debian/Ubuntu.
Download ipipou script and put to desired directory, e.g. to /usr/local/bin/.
Run all commands in root terminal.
For client mode with IP:port or token auth all requirements should be already satisfied, but in case if not:
apt install iproute2 python3For client mode with AUTH_KEY auth nacl python library is required additionally:
apt install python3-naclFor complete client/server mode:
apt install iproute2 python3 python3-nacl nftables conntrack build-essential libnfnetlink-dev libnetfilter-queue-dev python3-dev python3-pip
# There is no official deb package for NetfilterQueue, so use pip:
pip3 install -U NetfilterQueue -t /usr/local/lib/ipipou
# OR for newer version (you may try it if previous command failed):
pip3 install -U git+https://github.com/kti/python-netfilterqueue -t /usr/local/lib/ipipouIf you run in server mode and installed netfilterqueue to separate directory by pip3 -t (to not soil OS by systemwide pip) you have to change PYTHONPATH first (valid only for current shell session and its descendants):
export PYTHONPATH="/usr/local/lib/ipipou${PYTHONPATH:+:${PYTHONPATH}}"Run ipipou -h for inline help.
Generate ed25519 key pair and share pubkey (2nd line) and shared secret ("topsecret" in this example) with the server
ipipou --auth-keygen -von server side:
PYTHONPATH="/usr/local/lib/ipipou${PYTHONPATH:+:${PYTHONPATH}}" \
ipipou -s -vvv -b @eth0 -n0 --auth-secret topsecret --auth-remote-pubkey-b64 2ndlinepublicbase64keyon client side:
ipipou -c -vvv -b @wlan0 -r 203.0.113.1:10000 --auth-key-b64 1stlineprivatebase64key --auth-secret topsecret --tunl-ip 172.28.0.1 --keepalive 27On separate client terminal verify that the tunnel works
ping -c2 -w3 172.28.0.0 # Server side tunnel IP
When public IP or port changed (e.g. connection expired and NAT mapping changed) send SIGUSR1 to the process by kill -SIGUSR1 PIDofIPIPOU, or run the same command but with additional --reauth-only option.
To prevent connection expiry use --keepalive SEC option.
Full cleanup when all tunnels are done (will delete all ipip interfaces and fou listeners):
modprobe -r fou ipip # Unload kernel modulesIf client process dies (e.g. you kill it by kill -9 PIDofIPIPOU) the tunnel still remains in configured state and should work while connection is not expired, but on graceful exit (by ctrl+c or SIGTERM) the tunnel will be deconfigured first.
-
Put
[email protected]to/etc/systemd/system/(or create symlink) and runsystemctl daemon-reload. -
Create
NAME.conffile(s) in/etc/ipipou/whereNAMEis arbitrary string. For file content syntax seeipipou --helpfor--configoption.Config examples:
/etc/ipipou/server0.conf:server number 0 fou-dev eth0 fou-local-port 10000 tunl-ip 172.28.0.0 auth-remote-pubkey-b64 eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI= auth-secret topsecret auth-lifetime 3600 reply-on-auth-ok verb 3/etc/ipipou/client0.conf:client number 0 fou-local @wlan0 fou-remote ipipou.example.net:10000 tunl-ip 172.28.0.1 # pubkey of auth-key-b64: eQYNhD/Xwl6Zaq+z3QXDzNI77x8CEKqY1n5kt9bKeEI= auth-key-b64 RuBZkT23na2Q4QH1xfmZCfRgSgPt5s362UPAFbecTso= auth-secret topsecret keepalive 27 verb 2Keys, secret, and other options shown here only as an example, for secure setup you have to use your own!
If config has sensitive info (like private keys), it's recommended to create
ipipouuser and group, set them for[email protected], change permissions:adduser --system --no-create-home --group ipipou chown -RH 0:ipipou /etc/ipipou chmod 640 /etc/ipipou/*.conf # In [email protected] uncomment User=/Group= lines, comment DynamicUser=, then systemctl daemon-reload -
Run service as
systemctl start [email protected]
If both sides have public IP or behind one-to-one NAT and you do not need any auth you can create IPIP-over-FOU tunnel directly without ipipou:
on server side:
# Load FOU kernel module
modprobe fou
# Create IPIP tunnel encapsulated to FOU,
# ipip kernel module will be loaded automatically.
ip link add name ipipou0 type ipip \
remote 198.51.100.2 local 203.0.113.1 \
encap fou encap-sport 10000 encap-dport 20001 \
mode ipip dev eth0
# Add FOU listener for this tunnel
ip fou add port 10000 ipproto 4 local 203.0.113.1 dev eth0
# Assign IP address to the tunnel
ip address add 172.28.0.0 peer 172.28.0.1 dev ipipou0
# Up tunnel
ip link set ipipou0 up
on client side:
modprobe fou
ip link add name ipipou1 type ipip \
remote 203.0.113.1 local 192.168.0.2 \
encap fou encap-sport 10001 encap-dport 10000 encap-csum \
mode ipip dev eth0
# Options "local", "peer", "peer_port", "dev" can be not supported by old kernels and can be skipped.
ip fou add port 10001 ipproto 4 local 192.168.0.2 peer 203.0.113.1 peer_port 10000 dev eth0
ip address add 172.28.0.1 peer 172.28.0.0 dev ipipou1
ip link set ipipou1 up
where:
ipipou*— tunnel interface name203.0.113.1— server public IP198.51.100.2— client public IP192.168.0.2— client IP assigned toeth010001— client local FOU port20001— client public FOU port10000— server public FOU portencap-csum— an option to add checksum to inner UDP packets; can be replaced withnoencap-csumto avoid calculation and keep it empty, packets integrity will be controlled by outer UDP layer (while the packet is in the tunnel).eth0— local base interface for the tunnel172.28.0.1— tunnel client private IP address172.28.0.0— tunnel server private IP address
-
If ipipou service in server mode crashed next run may fail on first auth packet receiving (because previous configuration was not cleared). Additional restart may help (previous configuration will be cleared on clean stop).
-
On client side in case if tunnel stops working (e.g. connection is expired somewhere in the middle, or you got new public ip), you can reload service (or send SIGUSR1) to send auth packet again and reestablish connection:
systemctl reload [email protected] -
If there was no traffic in tunnel for some period (so connection was expired) the client behind NAT going to be not accessible from server side. As a workaround you may
- send keepalive packets regularly, e.g. every 27s, to either outer and/or inner tunnel layer (like auth and/or remote tunnel IP ICMP/ping packets correspondingly)
- monitor connection and reauthenticate on failure (by the service reload or SIGUSR1 on client side)
- run client in monitor mode (not supported yet)
-
If you want to cleanup your system from all interfaces created by
ipipou(liketunl0, oripipou*in case if automatic cleanup failed) you may runmodprobe -r fou ipip; nft delete table ip ipipou -
ipipouconfiguration ofnftablesshould be fully compatible with your existingiptablesconfiguration,nftablesandiptablesconfigurations can coexist. But in case if you use separate service fornftablesmanaging, on service restart/reloadip ipipoutable might be flushed, so the script may turn to inconsistent state. Be sure to keepip ipipoutable intact. It affects only server mode.
- Review article in habr.com (Russian): ipipou: больше чем просто нешифрованный туннель
In case if you found a bug or have reasonable feature request feel free to create an issue or PR.