Perfuração UDP simples usando um túnel IPIP como exemplo

Boa hora do dia!

Neste artigo, quero contar como implementei (mais um) um script Bash para conectar dois computadores localizados atrás de NAT usando a tecnologia de perfuração UDP usando o sistema operacional Ubuntu/Debian como exemplo.

Estabelecer uma conexão consiste em várias etapas:

  1. Iniciando um nó e aguardando que o nó remoto esteja pronto;
  2. Determinação do endereço IP externo e porta UDP;
  3. Transferir um endereço IP externo e uma porta UDP para um host remoto;
  4. Obtenção de um endereço IP externo e porta UDP de um host remoto;
  5. Organização de um túnel IPIP;
  6. Monitoramento de conexão;
  7. Se a conexão for perdida, exclua o túnel IPIP.

Pensei muito e ainda penso no que pode ser usado para trocar dados entre nós, o mais simples e rápido para mim no momento é trabalhar através do Yandex.disk.

  • Em primeiro lugar, é fácil de usar - são necessárias 3 ações: criar, ler, excluir. Com curl isso é:
    Crio:

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

    Ler:

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

    Excluir:

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

Para determinar o endereço IP externo e a porta UDP, use o comando stun-client:

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

Instalação com comando:

apt install stun-client

Para organizar um túnel, são usadas ferramentas padrão do sistema operacional do pacote iproute2. Existe muitos túneis que pode ser aumentado usando meios padrão (L2TPv3, GRE, etc.), mas escolhi IPIP porque cria carga adicional mínima no sistema. Tentei L2TPv3 sobre UDP e fiquei desapontado, a velocidade caiu 10 vezes, mas podem ser várias restrições relacionadas a provedores ou qualquer outra coisa. Como o túnel IPIP opera no nível IP, o túnel FOU é usado para operar no nível da porta UDP. Para organizar um túnel IPIP você precisa:

— carregue o módulo FOU:

modprobe fou

- ouça a porta local:

ip fou add port $localport ipproto 4

- crie um túnel:

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

— elevar a interface do túnel:

ip link set up dev fou$name

— atribuir endereços IP locais internos e remotos internos do túnel:

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

Excluir um túnel:

ip link del dev fou$name

ip fou del port $localport

O estado do túnel é monitorado executando ping periodicamente no endereço IP interno do túnel do nó remoto com o comando:

ping -c 1 $peerip -s 0

O ping periódico é necessário principalmente para manter o canal, caso contrário, quando o túnel estiver ocioso, as tabelas NAT nos roteadores poderão ser apagadas e a conexão será interrompida.

Se o ping desaparecer, o túnel IPIP será excluído e aguardará a prontidão do host remoto.

O roteiro em si:

#!/bin/bash
username="[email protected]"
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

Variáveis nome de usuário, senha и dobrador deve ser igual em ambos os lados, mas olhadinha - diferente, por exemplo: 10.0.0.1 e 10.0.0.2. A hora nos nós deve ser sincronizada. Você pode executar o script assim:

nohup script.sh &

Gostaria de chamar a atenção para o fato de que o túnel IPIP não é seguro do ponto de vista do tráfego não ser criptografado, mas isso pode ser facilmente resolvido usando IPsec sobre Este artigo, parecia simples e compreensível para mim.

Tenho usado esse script para conectar-me a um PC de trabalho há várias semanas e não notei nenhum problema. Conveniente em termos de configurá-lo e esquecê-lo.

Talvez você tenha comentários e sugestões, ficarei feliz em ouvir.

Obrigado!

Fonte: habr.com

Adicionar um comentário