¡Buen día!
En este artículo quiero contarles cómo implementé () un script en Bash para conectar dos computadoras que están detrás de un NAT, utilizando la tecnología de UDP hole punching, en el ejemplo de los sistemas operativos Ubuntu/Debian.
La organización de la conexión consta de varios pasos:
- Iniciar el nodo y esperar a que el nodo remoto esté listo;
- Determinar la dirección IP externa y el puerto UDP;
- Enviar la dirección IP externa y el puerto UDP al nodo remoto;
- Recibir la dirección IP externa y el puerto UDP del nodo remoto;
- Organizar un túnel IPIP;
- Monitorear la conexión;
- Al romperse la conexión, borrar el túnel IPIP.
He estado pensando durante mucho tiempo y todavía pienso, qué 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 sencillo de usar: solo se necesitan 3 acciones: crear, leer, eliminar. Con curl esto es:
Crear:curl -s -X MKCOL --user "$usename:$password" https://webdav.yandex.ru/$folderLeer:
curl -s --user "$usename:$password" -X PROPFIND -H "Depth: 1" https://webdav.yandex.ru/$folderEliminar:
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 se usa el cliente stun con el comando:
stun stun.sipnet.ru -v -p $1 2>&1 | grep "MappedAddress"Instalar con el comando:
apt install stun-clientPara organizar un túnel se utilizan las herramientas estándar del sistema operativo del paquete iproute2. Existen que se pueden crear con las herramientas estándar (L2TPv3, GRE, etc.), pero elegí IPIP porque genera una carga adicional mínima en el sistema. Probé L2TPv3 sobre UDP y me decepcioné, la velocidad se redujo diez veces, pero esto puede deberse a diversas limitaciones relacionadas con los proveedores u otras razones. Dado que el túnel IPIP funciona a nivel IP, se utiliza un túnel FOU para operar a nivel de puertos UDP. Para organizar el túnel IPIP, se necesita:
— cargar el módulo FOU:
modprobe fou— escuchar el puerto local:
ip fou add port $localport ipproto 4— crear el túnel:
ip link add name fou$name type ipip remote $remoteip local $localip encap fou encap-sport $localport encap-dport $remoteport— activar la interfaz del túnel:
ip link set up dev fou$name— asignar la dirección IP interna local y la dirección IP interna remota del túnel:
ip addr add $intIP peer $peerip dev fou$nameEliminar el túnel:
ip link del dev fou$nameip fou del port $localportEl monitoreo del estado del túnel se realiza mediante un ping periódico a la dirección IP interna del túnel del nodo remoto, con el comando:
ping -c 1 $peerip -s 0El ping periódico es necesario principalmente para mantener el canal, de lo contrario, en caso de inactividad del túnel, las tablas NAT en los routers pueden limpiarse y la conexión se romperá.
Si el ping se pierde, el túnel IPIP se elimina y espera la disponibilidad del nodo remoto.
El script 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 0Variables username, password y folder deben ser iguales en ambos lados, mientras que intip serán diferentes, por ejemplo: 10.0.0.1 y 10.0.0.2. La hora en los nodos debe estar sincronizada. El script se puede iniciar así:
nohup script.sh &Quiero señalar que el túnel IPIP no es seguro en términos de que el tráfico no está cifrado, pero esto se puede resolver fácilmente con IPsec por , me pareció simple y comprensible.
He estado usando este script para conectarme a mi PC de trabajo durante varias semanas y no he notado problemas. Es conveniente en el sentido de que lo configuré y me olvidé.
Puede que tenga comentarios y sugerencias, estaré encantado de escucharlos.
¡Gracias por su atención!
Fuente: habr.com
