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

Guten Tag!

In diesem Artikel möchte ich beschreiben, wie ich (noch ein) Bash-Skript entwickelt habe, um zwei Computer, die sich hinter NAT befinden, mithilfe der UDP-Hole-Punching-Technologie zu verbinden, am Beispiel von Ubuntu/Debian.

Die Verbindung erfolgt in mehreren Schritten:

  1. Start des Knotens und Warten auf die Bereitschaft des entfernten Knotens;
  2. Bestimmung der externen IP-Adresse und des UDP-Ports;
  3. Übertragung der externen IP-Adresse und des UDP-Ports an den entfernten Knoten;
  4. Empfang der externen IP-Adresse und des UDP-Ports vom entfernten Knoten;
  5. Etablierung eines IPIP-Tunnels;
  6. Überwachung der Verbindung;
  7. Bei Verbindungsabbrüchen den IPIP-Tunnel entfernen.

Ich habe lange nachgedacht und denke immer noch darüber nach, was ich zum Austausch von Daten zwischen den Knoten verwenden kann; für mich ist derzeit die einfachste und schnellste Lösung die Nutzung von Yandex.Disk.

  • Erstens ist es einfach zu verwenden – es sind nur 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 ist es einfach zu installieren:
    apt install curl

Zur Bestimmung der externen IP-Adresse und des UDP-Ports verwenden Sie den stun-client mit dem Befehl:

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

Installation mit dem Befehl:

apt install stun-client

Für die Organisation des Tunnels werden die integrierten Werkzeuge des Betriebssystems aus dem Paket iproute2 verwendet. Es gibt eine Vielzahl von Tunneln, die man mit den integrierten Mitteln aufbauen kann (L2TPv3, GRE usw.), aber ich habe mich für IPIP entschieden, da 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 10-Fache, aber dies könnten verschiedene Einschränkungen mit den Providern oder anderen Faktoren sein. Da der IPIP-Tunnel auf der IP-Ebene funktioniert, wird ein FOU-Tunnel verwendet, um auf der Ebene der UDP-Ports zu arbeiten. Für die Einrichtung eines IPIP-Tunnels sind folgende Schritte erforderlich:

— Laden Sie das FOU-Modul:

modprobe fou

— Lokalen Port ab hören:

ip fou add port $localport ipproto 4

— Tunnel erstellen:

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

— Schnittstelle des Tunnels aktivieren:

ip link set up dev fou$name

— Interne lokale und 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

Der Zustand des Tunnels wird durch regelmäßiges Pingen der internen IP-Adresse des Remote-Knotens überwacht, mit dem Befehl:

ping -c 1 $peerip -s 0

Das regelmäßige Pingen ist vor allem wichtig, um den Kanal aufrechtzuerhalten, da sonst bei Inaktivität des Tunnels auf Routern die NAT-Tabellen gelöscht werden können und die Verbindung dann unterbrochen wird.

Wenn das Pingen ausbleibt, wird der IPIP-Tunnel entfernt und wartet auf die Bereitschaft vom Remote-Knoten.

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 Benutzername, password und Ordner müssen auf beiden Seiten identisch sein, während intip unterschiedlich sein kann, zum Beispiel: 10.0.0.1 und 10.0.0.2. Die Zeit auf den Knoten sollte synchronisiert sein. Das Skript kann wie folgt gestartet werden:

nohup script.sh &

Ich weise darauf hin, dass der IPIP-Tunnel aus Sicherheitsgründen unsicher ist, da der Verkehr nicht verschlüsselt ist, aber dies lässt sich leicht mit IPsec lösen. in diesem Artikel, erschien mir einfach und verständlich.

Ich benutze dieses Skript seit mehreren Wochen, um mich mit meinem Arbeits-PC zu verbinden, und habe keine Probleme festgestellt. Es ist praktisch, da ich es eingerichtet und vergessen habe.

Vielleicht haben Sie Anmerkungen oder Vorschläge, ich freue mich darauf, diese zu hören.

Vielen Dank für Ihre Aufmerksamkeit!

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster