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

一日の良い時間!

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

接続を確立するには、いくつかの手順を実行します。

  1. ノードを起動し、リモート ノードの準備が整うまで待機します。
  2. 外部 IP アドレスと UDP ポートを決定します。
  3. 外部 IP アドレスと UDP ポートをリモート ノードに渡します。
  4. リモート ノードから外部 IP アドレスと UDP ポートを取得します。
  5. IPIP トンネルの構成。
  6. 接続監視。
  7. 接続が切断された場合は、IPIP トンネルを削除します。

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

  • まず、使い方は簡単です。必要なのは、作成、読み取り、削除の3つのアクションだけです。curl を使えば、次のようになります。
    作成する:
    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レベルで動作するため、UDPポートレベルで動作させるにはFOUトンネルを使用します。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 アドレスを割り当てます。

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="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

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

nohup script.sh &

IPIPトンネルはトラフィックが暗号化されていないため安全ではないことに注意してください。ただし、これはIPsecを使用することで簡単に解決できます。 この記事それは私にとっては単純かつ明確に思えました。

このスクリプトを使って仕事用のPCに接続して数週間経ちますが、今のところ問題は起きていません。設定しておけばあとは忘れてしまえるので便利です。

おそらく、コメントや提案があると思いますが、喜んでお聞きします。

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

出所: habr.com

DDoS 保護機能を備えた信頼性の高いサイト用ホスティング、VPS VDS サーバーを購入する 🔥 DDoS攻撃対策付きの信頼性の高いウェブサイトホスティング、VPS/VDSサーバーを購入しましょう | ProHoster