例として IPIP トンネルを使用した単純な UDP ホールパンチ

一日の良い時間!

この記事では、私がどのように実装したかを説明したいと思います (もう一つ) 例として Ubuntu/Debian OS を使用し、UDP ホールパンチ技術を使用して NAT の背後にある XNUMX 台のコンピューターを接続するための Bash スクリプト。

接続の確立は、いくつかの手順で構成されます。

  1. ノードを起動し、リモート ノードの準備が完了するのを待ちます。
  2. 外部 IP アドレスと UDP ポートを決定します。
  3. 外部 IP アドレスと UDP ポートをリモート ホストに転送します。
  4. リモート ホストから外部 IP アドレスと UDP ポートを取得します。
  5. IPIP トンネルの構成。
  6. 接続監視。
  7. 接続が失われた場合は、IPIP トンネルを削除します。

私は長い間考えてきましたが、ノード間でデータを交換するために何が使用できるかを今でも考えていますが、現時点で私にとって最も簡単で最速なのは、Yandex.disk を使用することです。

  • まず、使い方が簡単です。作成、読み取り、削除の 3 つのアクションが必要です。 カールを使用すると、次のようになります。
    作成:

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

    読む:

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

    消去:

    curl -s -X DELETE --user "$usename:$password" https://webdav.yandex.ru/$folder
  • 次に、インストールが簡単です。
    apt install curl

外部 IP アドレスと UDP ポートを確認するには、stun-client コマンドを使用します。

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

コマンドによるインストール:

apt install stun-client

トンネルを構成するには、iproute2 パッケージの標準 OS ツールが使用されます。 存在する たくさんのトンネル これは標準的な手段 (L2TPv3、GRE など) を使用して発生させることができますが、システムへの追加負荷が最小限に抑えられるため、IPIP を選択しました。 UDP 経由で L2TPv3 を試してみましたが、速度が 10 分の XNUMX に低下してがっかりしました。ただし、これらはプロバイダーなどに関連するさまざまな制限である可能性があります。 IPIP トンネルは IP レベルで動作するため、FOU トンネルは UDP ポート レベルで動作するために使用されます。 IPIP トンネルを構成するには、次のものが必要です。

— FOU モジュールをロードします。

modprobe fou

— ローカルポートをリッスンします:

ip fou add port $localport ipproto 4

— トンネルを作成します。

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

— トンネルインターフェイスを起動します。

ip link set up dev fou$name

— トンネルの内部ローカルおよび内部リモート IP アドレスを割り当てます。

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

トンネルを削除します。

ip link del dev fou$name

ip fou del port $localport

トンネルの状態は、次のコマンドを使用してリモート ノード トンネルの内部 IP アドレスに定期的に ping を送信することによって監視されます。

ping -c 1 $peerip -s 0

定期的な ping は主にチャネルを維持するために必要です。そうしないと、トンネルがアイドル状態のときにルーター上の NAT テーブルがクリアされ、接続が切断される可能性があります。

ping が消えると、IPIP トンネルは削除され、リモート ホストからの準備が完了するまで待機します。

スクリプト自体:

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

変数 ユーザ名, password и フォルダ 両側で同じになるはずですが、 先端 - 異なります。例: 10.0.0.1 と 10.0.0.2。 ノード上の時刻は同期する必要があります。 次のようにスクリプトを実行できます。

nohup script.sh &

IPIP トンネルはトラフィックが暗号化されていないという観点からは安全ではありませんが、これは IPsec over を使用すると簡単に解決できるという事実に注意していただきたいと思います。 この記事、それは私にとってシンプルで理解できるように思えました。

数週間にわたってこのスクリプトを使用して職場の PC に接続していますが、何も問題はありません。 設定しても忘れても便利です。

おそらくコメントや提案があるでしょう。喜んで耳を傾けます。

ありがとうございました!

出所: habr.com

コメントを追加します