Împingerea simplă UDP hole punching pe exemplul tunelului IPIP

Bună ziua!

În acest articol vreau să împărtășesc cum am implementat (încă unul) un script Bash pentru conectarea a două computere, aflate în spatele NAT, folosind tehnologia UDP hole punching pe sistemele de operare Ubuntu/Debian.

Organizarea conexiunii constă din mai mulți pași:

  1. Pornirea nodului și așteptarea pregătirii nodului la distanță;
  2. Determinarea adresei IP externe și a portului UDP;
  3. Transmiterea adresei IP externe și a portului UDP nodului la distanță;
  4. Obținerea adresei IP externe și a portului UDP de la nodul la distanță;
  5. Organizarea tunelului IPIP;
  6. Monitorizarea conexiunii;
  7. La întreruperea conexiunii, șterge tunelul IPIP.

M-am gândit mult și mă gândesc încă ce aș putea folosi pentru schimbul de date între noduri, cel mai simplu și rapid pentru mine în acest moment este lucrul prin Yandex.Disk.

  • În primul rând, este simplu de utilizat — sunt necesare 3 acțiuni: creați, citiți, ștergeți. Cu ajutorul curl, acestea sunt:
    Creează:
    curl -s -X MKCOL --user "$usename:$password" https://webdav.yandex.ru/$folder

    Citește:

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

    Șterge:

    curl -s -X DELETE --user "$usename:$password" https://webdav.yandex.ru/$folder
  • În al doilea rând, este simplu de instalat:
    apt install curl

Pentru a determina adresa IP externă și portul UDP se folosește comanda stun-client:

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

Instalare cu comanda:

apt install stun-client

Pentru organizarea tunelului se folosesc instrumentele standard ale sistemului de operare din pachetul iproute2. Există numeroase tuneluri care pot fi ridicate cu ajutoare standard (L2TPv3, GRE etc.), dar am ales IPIP pentru că acesta creează o sarcină suplimentară minimă asupra sistemului. Am încercat L2TPv3 peste UDP și am fost dezamăgit, viteza a scăzut de 10 ori, dar acest lucru poate fi din cauza diferitelor limitări legate de furnizori sau din alte motive. Deoarece tunelul IPIP funcționează la nivelul IP, se folosește tunelul FOU pentru a funcționa la nivelul porturilor UDP. Pentru organizarea tunelului IPIP este necesar:

— să încarci modulul FOU:

modprobe fou

— să asculți un port local:

ip fou add port $localport ipproto 4

— să creezi un tunel:

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

— să ridici interfața tunelului:

ip link set up dev fou$name

— să aloci adresele IP interne locale și externe ale tunelului:

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

Șterge tunelul:

ip link del dev fou$name

ip fou del port $localport

Monitorizarea stării tunelului se realizează prin ping-uri periodice ale adresei IP interne a tunelului gazdei de la distanță, cu comanda:

ping -c 1 $peerip -s 0

Ping-ul periodic este necesar în primul rând pentru a menține canalul, altfel, în cazul inactivității tunelului, tabelele NAT de pe routere pot fi șterse, moment în care conexiunea se va întrerupe.

Dacă ping-ul dispare, tunelul IPIP este șters și așteaptă disponibilitatea de la gazda de la distanță.

Iată scriptul:

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

Variabile username, parola și folder trebuie să fie identice de ambele părți, în timp ce intip — diferite, de exemplu: 10.0.0.1 și 10.0.0.2. Timpul pe noduri trebuie să fie sincronizat. Scriptul poate fi pornit astfel:

nohup script.sh &

Menționez că tunelul IPIP nu este sigur din punct de vedere al faptului că traficul nu este criptat, dar acest lucru poate fi ușor rezolvat cu ajutorul IPsec, deoarece această articole, mi s-a părut simplu și clar.

Folosesc acest script pentru a mă conecta la PC-ul de lucru de câteva săptămâni și nu am observat probleme. Este convenabil din punct de vedere că l-am configurat și l-am uitat.

Poate aveți observații și sugestii, aș fi bucuros să le aud.

Vă mulțumesc pentru atenție!

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster