Simple perforación de agujeros UDP usando un túnel IPIP como ejemplo

¡Buena hora del día!



En este artículo quiero contarles cómo implementé ( otro ) script en Bash para conectar dos computadoras detrás de NAT usando la tecnología de perforación de agujeros UDP usando el ejemplo del sistema operativo Ubuntu / Debian.



Establecer una conexión consta de varios pasos:



  1. Iniciar un nodo y esperar que el nodo remoto esté listo;
  2. Determinar la dirección IP externa y el puerto UDP;
  3. Transferencia de dirección IP externa y puerto UDP al host remoto;
  4. Obtener una dirección IP externa y un puerto UDP de un host remoto;
  5. Organización de un túnel IPIP;
  6. Monitoreo de la conexión;
  7. Si se pierde la conexión, elimine el túnel IPIP.


Pensé durante mucho tiempo y sigo pensando que se puede usar para intercambiar datos entre nodos, lo más simple y rápido para mí en este momento es trabajar a través de Yandex.Disk.



  • Primero, es fácil de usar: necesita 3 pasos: crear, leer, eliminar. Con curl esto es:

    Crear:



    curl -s -X MKCOL --user "$usename:$password" https://webdav.yandex.ru/$folder


    Leer:

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


    Eliminar:



    curl -s -X DELETE --user "$usename:$password" https://webdav.yandex.ru/$folder
  • En segundo lugar, es fácil de instalar:



    apt install curl


Para determinar la dirección IP externa y el puerto UDP, use el comando stun-client:



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


Instalación con el comando:



apt install stun-client


Para organizar el túnel, se utilizan las herramientas estándar del sistema operativo del paquete iproute2. Hay muchos túneles que se pueden configurar por medios estándar (L2TPv3, GRE, etc.), pero elegí IPIP porque crea una carga adicional mínima en el sistema. Probé L2TPv3 sobre UDP y me decepcionó, la velocidad se redujo en un factor de 10, pero puede haber varias restricciones relacionadas con los proveedores u otra cosa. Dado que el túnel IPIP funciona a nivel IP, el túnel FOU se utiliza para operar a nivel de puerto UDP. Para organizar un túnel IPIP, debe:



- cargar el módulo FOU:



modprobe fou


- escuchar puerto local:



ip fou add port $localport ipproto 4


- crea un túnel:



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


- abrir la interfaz del túnel:



ip link set up dev fou$name


- Asignar la dirección IP del túnel interno local y remoto interno:



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


Eliminar túnel:



ip link del dev fou$name


ip fou del port $localport


El estado del túnel se supervisa haciendo ping periódicamente a la dirección IP interna del túnel del host remoto con el comando:



ping -c 1 $peerip -s 0


Se necesita un ping periódico principalmente para mantener el canal; de lo contrario, si el túnel está inactivo en los enrutadores, las tablas NAT se pueden borrar y luego la conexión se interrumpirá.



Si se pierde el ping, el túnel IPIP se cae y espera a que el host remoto esté listo.



El guión en sí:



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


Las variables de nombre de usuario , contraseña y carpeta deben ser las mismas en ambos lados, pero el intip debe ser diferente, por ejemplo: 10.0.0.1 y 10.0.0.2. El tiempo en los nodos debe estar sincronizado. Puede ejecutar el script de esta manera:



nohup script.sh &


Le llamo la atención sobre el hecho de que el túnel IPIP es inseguro desde el punto de vista del hecho de que el tráfico no está encriptado, pero esto se resuelve fácilmente usando IPsec en este artículo , me pareció simple y directo.



He estado usando este script para conectarme a una PC que funciona durante varias semanas y no he notado ningún problema. Conveniente en términos de configuración y olvido.



Quizás tenga comentarios y sugerencias, me alegrará saberlo.



¡Gracias por tu atención!



All Articles