Einfaches UDP-Hole-Punching am Beispiel eines IPIP-Tunnels

Guten Tag!

In diesem Artikel möchte ich erzÀhlen, wie ich (eine weitere) Bash-Skript zur Verbindung zweier Computer hinter NAT mit der Technologie UDP Hole Punching am Beispiel von Ubuntu/Debian umgesetzt habe.

Die Einrichtung der Verbindung besteht aus mehreren Schritten:

  1. Starten des Knotens und Warten auf die Bereitschaft des entfernten Knotens;
  2. Bestimmen der externen IP-Adresse und des UDP-Ports;
  3. Übertragung der externen IP-Adresse und des UDP-Ports an den entfernten Knoten;
  4. Erhalt der externen IP-Adresse und des UDP-Ports vom entfernten Knoten;
  5. Einrichten eines IPIP-Tunnels;
  6. Überwachung der Verbindung;
  7. Bei Verbindungsabbruch den IPIP-Tunnel löschen.

Ich habe lange nachgedacht und ĂŒberlege weiterhin, was ich fĂŒr den Datenaustausch zwischen den Knoten verwenden kann. Am einfachsten und schnellsten fĂŒr mich ist derzeit die Arbeit ĂŒber Yandex.Disk.

  • Erstens ist es einfach in der Benutzung – es sind 3 Schritte erforderlich: erstellen, lesen, löschen. Mit curl sieht das so aus:
    Erstellen:
    curl -s -X MKCOL --user "$usename:$password" https://webdav.yandex.ru/$folder

    Lesen:

    curl -s --user "$usename:$password" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/$folder

    Löschen:

    curl -s -X DELETE --user "$usename:$password" https://webdav.yandex.ru/$folder
  • Zweitens, es ist einfach zu installieren:
    apt install curl

Zur Bestimmung der externen IP-Adresse und des UDP-Ports wird der Befehl stun-client verwendet:

stun stun.sipnet.ru -v -p $1 2>&1 | grep "MappedAddress"

Installation mit dem Befehl:

apt install stun-client

Zur Einrichtung des Tunnels werden die Standardtools des Betriebssystems aus dem Paket iproute2 verwendet. Es gibt eine Vielzahl von Tunneln, die mit den vorhandenen Mitteln (L2TPv3, GRE usw.) erstellt werden können, aber ich habe IPIP gewĂ€hlt, weil es die geringste zusĂ€tzliche Belastung fĂŒr das System verursacht. Ich habe L2TPv3 ĂŒber UDP ausprobiert und war enttĂ€uscht, die Geschwindigkeit fiel um das Zehnfache, aber das könnten verschiedene EinschrĂ€nkungen von Anbietern oder Ähnlichem sein. Da der IPIP-Tunnel auf der IP-Ebene arbeitet, wird ein FOU-Tunnel verwendet, um auf der Ebene der UDP-Ports zu arbeiten. FĂŒr die Einrichtung eines IPIP-Tunnels benötigt man:

— die FOU-Modul laden:

modprobe fou

— auf dem lokalen Port lauschen:

ip fou add port $localport ipproto 4

— den Tunnel erstellen:

ip link add name fou$name type ipip remote $remoteip local $localip encap fou encap-sport $localport encap-dport $remoteport

— das Schnittstellen des Tunnels aktivieren:

ip link set up dev fou$name

— die interne lokale und die interne entfernte IP-Adresse des Tunnels zuweisen:

ip addr add $intIP peer $peerip dev fou$name

Tunnel entfernen:

ip link del dev fou$name

ip fou del port $localport

Die Überwachung des Tunnelzustands erfolgt durch periodisches Pingen der internen IP-Adresse des Tunnel-Remote-Knotens mit dem Befehl:

ping -c 1 $peerip -s 0

Das periodische Pingen dient in erster Linie der Aufrechterhaltung des Kanals, da sonst bei InaktivitÀt des Tunnels auf den Routern die NAT-Tabellen gelöscht werden könnten, und die Verbindung dann abbricht.

Wenn der Ping ausfÀllt, wird das IPIP-Tunnel entfernt und wartet auf die Bereitschaft des Remote-Knotens.

Hier ist das eigentliche Skript:

#!/bin/bash
username="username@yandex.ru"
password="password"
folder="vpnid"
intip="10.0.0.1"
localport=`shuf -i 10000-65000 -n 1`
cid=`shuf -i 10000-99999 -n 1`
tid=`shuf -i 10-99 -n 1`
function yaread {
        curl -s --user "$1:$2" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/$3 | sed 's/></>n</g' | grep "displayname" | sed 's/<d:displayname>//g' | sed 's/</d:displayname>//g' | grep -v $3 | grep -v $4 | sort -r
}
function yacreate {
        curl -s -X MKCOL --user "$1:$2" https://webdav.yandex.ru/$3
}
function yadelete {
        curl -s -X DELETE --user "$1:$2" https://webdav.yandex.ru/$3
}
function myipport {
        stun stun.sipnet.ru -v -p $1 2>&1 | grep "MappedAddress" | sort | uniq | awk '{print $3}' | head -n1
}
function tunnel-up {
	modprobe fou
	ip fou add port $4 ipproto 4
	ip link add name fou$7 type ipip remote $1 local $3 encap fou encap-sport $4 encap-dport $2
	ip link set up dev fou$7
	ip addr add $6 peer $5 dev fou$7
}
function tunnel-check {
	sleep 10
        pings=0
        until [[ $pings == 4 ]]; do
                if ping -c 1 $1 -s 0 &>/dev/null;
                        then    echo -n .; n=0
                        else    echo -n !; ((pings++))
                fi
		sleep 15
        done
}
function tunnel-down {
	ip link del dev fou$1
	ip fou del port $2
}
trap 'echo -e "nDisconnecting..." && yadelete $username $password $folder; tunnel-down $tunnelid $localport; echo "IPIP tunnel disconnected!"; exit 1' 1 2 3 8 9 14 15
until [[ -n $end ]]; do
    yacreate $username $password $folder
    until [[ -n $ip ]]; do
        mydate=`date +%s`
        timeout="60"
        list=`yaread $username $password $folder $cid | head -n1`
        yacreate $username $password $folder/$mydate:$cid
        for l in $list; do
                if [ `echo $l | sed 's/:/ /g' | awk {'print $1'}` -ge $(($mydate-65)) ]; then
			#echo $list
                        myipport=`myipport $localport`
                        yacreate $username $password $folder/$mydate:$cid:$myipport:$intip:$tid
                        timeout=$(( $timeout + `echo $l | sed 's/:/ /g' | awk {'print $1'}` - $mydate + 3 ))
                        ip=`echo $l | sed 's/:/ /g' | awk '{print $3}'`
                        port=`echo $l | sed 's/:/ /g' | awk '{print $4}'`
                        peerip=`echo $l | sed 's/:/ /g' | awk '{print $5}'`
			peerid=`echo $l | sed 's/:/ /g' | awk '{print $6}'`
			if [[ -n $peerid ]]; then tunnelid=$(($peerid*$tid)); fi
                fi
        done
        if ( [[ -z "$ip" ]] && [ "$timeout" -gt 0 ] ) ; then
                echo -n "!"
                sleep $timeout
        fi
    done
    localip=`ip route get $ip | head -n1 | sed 's|.*src ||' | cut -d' ' -f1`
    tunnel-up $ip $port $localip $localport $peerip $intip $tunnelid
    tunnel-check $peerip
    tunnel-down $tunnelid $localport
    yadelete $username $password $folder
    unset ip port myipport
done
exit 0

Variablen username, passwort und folder mĂŒssen auf beiden Seiten identisch sein, wĂ€hrend intip — unterschiedlich sein können, zum Beispiel: 10.0.0.1 und 10.0.0.2. Die Zeit auf den Knoten sollte synchronisiert sein. Das Skript kann folgendermaßen gestartet werden:

nohup script.sh &

Ich möchte darauf hinweisen, dass das IPIP-Tunnel aus Sicht der Sicherheit nicht sicher ist, da der Datenverkehr nicht verschlĂŒsselt wird, aber dies lĂ€sst sich leicht mit IPsec lösen, diesem Artikeles schien mir einfach und verstĂ€ndlich.

Ich verwende dieses Skript bereits seit einigen Wochen fĂŒr die Verbindung zu meinem Arbeits-PC und habe keine Probleme festgestellt. Es ist praktisch, da ich es eingerichtet habe und es einfach funktioniert.

Vielleicht haben Sie Anmerkungen oder VorschlÀge, ich wÀre froh, sie zu hören.

Vielen Dank fĂŒr Ihre Aufmerksamkeit!

Quelle: habr.com

60GB SSD 8Gb DDR4