Добър ден!
В тази статия искам да споделя как реализирах () скрипт на Bash за свързване на два компютъра, намиращи се зад NAT, с използване на технологията UDP hole punching на примера на ОС Ubuntu/Debian.
Организацията на връзката се състои от няколко стъпки:
- Стартиране на възел и изчакване на готовност на отдалечения възел;
- Определяне на външния IP адрес и UDP порта;
- Предаване на външния IP адрес и UDP порта на отдалечения възел;
- Получаване на външния IP адрес и UDP порта от отдалечения възел;
- Организация на IPIP тунела;
- Мониторинг на връзката;
- При прекъсване на връзката, да се премахне 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. Съществуват които могат да бъдат конфигурирани с вградените средства (L2TPv3, GRE и т.н.), но избрах IPIP, защото създава минимална допълнителна натовареност на системата. Опитах L2TPv3 над UDP и се разочаровах, скоростта спадна десетократно, но това може да бъде свързано с различни ограничения от страна на доставчиците или нещо друго. Тъй като 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$nameip fou del port $localportМониторинг на състоянието на тунела се извършва чрез периодично пингване на вътрешния IP адрес на тунела на отдалечения възел с командата:
ping -c 1 $peerip -s 0Периодичното пингване е необходимо най-вече за поддържане на канала, в противен случай при бездействие на тунела на рутерите NAT таблиците могат да се изчистят и тогава връзката ще се прекъсне.
Ако пингът изчезне, то 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Променливи username, password и folder трябва да бъдат еднакви от двете страни, а intip — различни, например: 10.0.0.1 и 10.0.0.2. Времето на възлите трябва да бъде синхронизирано. Скриптът може да се стартира по следния начин:
nohup script.sh &Обърнете внимание, че IPIP тунелът не е безопасен от гледна точка на това, че трафикът не е шифрован, но това лесно се решава с помощта на IPsec. , той ми се стори прост и разбираем.
Използвам този скрипт за свързване с работния си компютър вече няколко седмици и не съм имал проблеми. Удобно е, защото го настроих и забравих.
Възможно е да имате коментари и предложения, ще се радвам да ги чуя.
Благодаря за вниманието!
Източник: habr.com
