Eenvoudige UDP hole punching met een IPIP-tunnel als voorbeeld

Goedenavond!

In dit artikel wil ik vertellen hoe ik (nog een) Bash-script heb geïmplementeerd om twee computers, die zich achter NAT bevinden, met behulp van de technologie UDP hole punching via het voorbeeld van Ubuntu/Debian met elkaar te verbinden.

Het opzetten van de verbinding bestaat uit verschillende stappen:

  1. Starten van de node en wachten op de gereedheid van de externe node;
  2. Bepalen van het externe IP-adres en UDP-poort;
  3. Doorgeven van het externe IP-adres en UDP-poort aan de externe node;
  4. Ontvangen van het externe IP-adres en UDP-poort van de externe node;
  5. Opzetten van een IPIP-tunnel;
  6. Monitoring van de verbinding;
  7. Bij verbroken verbinding de IPIP-tunnel verwijderen.

Ik heb lang nagedacht en denk nog steeds na over wat ik kan gebruiken voor gegevensuitwisseling tussen nodes. Op dit moment lijkt het me het eenvoudigste en snelste om via Yandex.Disk te werken.

  • Ten eerste is het eenvoudig in gebruik — het zijn 3 stappen: maken, lezen, verwijderen. Met curl is dat:
    Aanmaken:
    curl -s -X MKCOL --user "$usename:$password" https://webdav.yandex.ru/$folder

    Lezen:

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

    Verwijderen:

    curl -s -X DELETE --user "$usename:$password" https://webdav.yandex.ru/$folder
  • Ten tweede is het eenvoudig te installeren:
    apt install curl

Voor het bepalen van het externe IP-adres en UDP-poort worden de stun-client gebruikt met het commando:

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

Installeren met het commando:

apt install stun-client

Voor het opzetten van de tunnel worden de standaardhulpmiddelen van het OS uit het iproute2-pakket gebruikt. Er zijn veel tunnels die met standaardhulpmiddelen kunnen worden opgezet (L2TPv3, GRE, enz.), maar ik heb voor IPIP gekozen omdat het minimale extra belasting voor het systeem creëert. Ik heb L2TPv3 boven UDP geprobeerd en was teleurgesteld; de snelheid viel met een factor 10, maar dat kan verschillende beperkingen hebben die verband houden met providers of andere zaken. Omdat de IPIP-tunnel op IP-niveau werkt, wordt een FOU-tunnel gebruikt om op UDP-poorten te kunnen werken. Voor het opzetten van een IPIP-tunnel is het nodig:

— laad de FOU-module:

modprobe fou

— luister naar de lokale poort:

ip fou add port $localport ipproto 4

— maak de tunnel aan:

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

— activeer de tunnelinterface:

ip link set up dev fou$name

— wijs het interne lokale en interne externe IP-adres van de tunnel toe:

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

Verwijder de tunnel:

ip link del dev fou$name

ip fou del port $localport

De statusmonitoring van de tunnel gebeurt door periodiek te pingen naar het interne IP-adres van de tunnel aan de andere kant met het commando:

ping -c 1 $peerip -s 0

Periodiek pingen is vooral nodig om de verbinding te behouden; anders kunnen bij inactiviteit de NAT-tabellen op de routers worden gewist, waardoor de verbinding wordt verbroken.

Als de ping wegvalt, wordt de IPIP-tunnel verwijderd en wacht deze tot de andere kant weer beschikbaar is.

Eigenlijk is hier het script:

#!/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

Variabelen username, password en map moeten gelijk zijn aan beide zijden, maar intip — verschillend, bijvoorbeeld: 10.0.0.1 en 10.0.0.2. De tijd op de knooppunten moet gesynchroniseerd zijn. Het script kan als volgt worden uitgevoerd:

nohup script.sh &

Ik wil erop wijzen dat de IPIP-tunnel niet veilig is omdat het verkeer niet wordt versleuteld, maar dit kan eenvoudig worden opgelost met IPsec. dit artikel, het leek me eenvoudig en duidelijk.

Ik gebruik dit script al een paar weken om verbinding te maken met mijn werk-PC en heb geen problemen ondervonden. Het is handig omdat ik het eenmaal heb ingesteld en het verder kan vergeten.

Misschien heeft u opmerkingen of suggesties, ik hoor het graag.

Bedankt voor uw aandacht!

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster