Simpel UDP hole punching ved hjælp af IPIP-tunnel-exemplet

Goddag!

I denne artikel vil jeg fortælle, hvordan jeg realiserede (endnu en) skript i Bash til at forbinde to computere, der befinder sig bag NAT, ved hjælp af teknologien UDP hole punching på eksemplet med OS Ubuntu/Debian.

Oprettelsen af forbindelsen består af flere trin:

  1. Start af noden og vent på, at den fjerne node er klar;
  2. Bestemmelse af den eksterne IP-adresse og UDP-port;
  3. Overførsel af den eksterne IP-adresse og UDP-port til den fjerne node;
  4. Modtagelse af den eksterne IP-adresse og UDP-port fra den fjerne node;
  5. Oprettelse af IPIP-tunnel;
  6. Overvågning af forbindelsen;
  7. Ved afbrydelse af forbindelsen skal IPIP-tunnelen slettes.

Jeg har længe overvejet, hvad der kan bruges til dataudveksling mellem noderne, det mest enkle og hurtige for mig lige nu er at arbejde via Yandex.Disk.

  • For det første er det enkelt at bruge - der kræves 3 handlinger: oprette, læse, slette. Ved hjælp af curl er det:
    Opret:
    curl -s -X MKCOL --user "$usename:$password" https://webdav.yandex.ru/$folder

    Læs:

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

    Slet:

    curl -s -X DELETE --user "$usename:$password" https://webdav.yandex.ru/$folder
  • For det andet er det let at installere:
    apt install curl

Til bestemmelse af den eksterne IP-adresse og UDP-port bruges stun-client med kommandoen:

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

Installation med kommandoen:

apt install stun-client

Til oprettelse af tunnelen bruges standardværktøjer fra OS-pakken iproute2. Der findes mange tunneler som kan oprettes med standardværktøjer (L2TPv3, GRE osv.), men jeg valgte IPIP, fordi det skaber minimal ekstra belastning på systemet. Jeg prøvede L2TPv3 oven på UDP og blev skuffet, hastigheden faldt med 10 gange, men det kan være forskellige begrænsninger relateret til udbydere eller noget andet. Da IPIP-tunnelen fungerer på IP-niveau, bruges FOU-tunnelen til at arbejde på UDP-portniveau. For at oprette IPIP-tunnelen skal man:

— indlæse FOU-modulet:

modprobe fou

— lytte på lokal port:

ip fou add port $localport ipproto 4

— oprette tunnelen:

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

— aktivere tunnelens interface:

ip link set up dev fou$name

— tildele interne lokale og interne fjerne IP-adresser til tunnelen:

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

Slet tunnelen:

ip link del dev fou$name

ip fou del port $localport

Overvågningen af tunnelens tilstand sker ved hjælp af periodisk ping af den interne IP-adresse på den fjernliggende nodes tunnel med kommandoen:

ping -c 1 $peerip -s 0

Det periodiske ping er primært nødvendigt for at opretholde kanalen, ellers kan NAT-tabellerne på routerne blive renset, hvis tunnelen står stille, og så vil forbindelsen bryde.

Hvis pinget forsvinder, fjernes IPIP-tunnelen og venter på klarhed fra den fjernliggende node.

Selve scriptet:

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

Variabler brugernavn, adgangskode og folder skal være de samme på begge sider, mens intip — skal være forskellige, for eksempel: 10.0.0.1 og 10.0.0.2. Tiden på noderne skal være synkroniseret. Scriptet kan startes sådan:

nohup script.sh &

Jeg vil gerne påpege, at IPIP-tunnelen ikke er sikker, eftersom trafikken ikke er krypteret, men dette kan nemt løses ved hjælp af IPsec fra denne artikel, som jeg syntes var enkel og forståelig.

Jeg har brugt dette script til at forbinde til min arbejds-PC i flere uger og har ikke bemærket problemer. Det er praktisk, da jeg satte det op og glemte det.

Hvis du har kommentarer eller forslag, vil jeg være glad for at høre dem.

Tak for din opmærksomhed!

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster