Есть приложения критичные к разрывам связи, переподключение происходит мучительно и вообще не всегда. Поставил перед собой цель, сделать прыжки маршрутов и физических подключений прозрачными, что бы связь была постоянной и TCP коннект не рвался.
И поможет в этом старый, добрый и ламповый «openvpn». Но установка и настройка — тема давно избитая, трогать её, здесь не планируется.
Что нам нужно:
Сервер Linux с постоянным IP (vps, vds, dedic или просто домашний)
Openvpn на сервере и клиенте, настроенные на работу по UDP.
iproute2 — собственно для фокуса нужна работающая утилита ip на Android.
Так же потребуется внести изменения в исходный код openvpn-settings c последующий сборкой apk.
Приступим
Начнем с правки «openvpn-settings». Суть в том, что при любом изменении в сетевой конфигурации нативному демону openvpn посылается команда SIGUSR1, что заставляет его отключаться и подключаться по новой. Для наших целей это вред, но нам все равно желательно иметь возможность выполнять некоторые действия при изменении сетевой конфигурации.
Кто-то скажет, но ведь есть persist-tun. Отвечаю, работает не так, как хочется, openvpn дергает скрипты «лежать-вставать» каждый раз, когда приходит SIGUSR1. И в принципе отличить по имеющимся переменным перезапуск от старта, без выкрутасов почти не возможно. Это подрывает всю затею, а хочется просто и надежно.
Поэтому мы заменим отправку SIGUSR1 на вызов shell скрипта.
Скачиваем исходники командой hg clone https://code.google.com/p/android-openvpn-settings/
Открываем файл
srcdeschaeuffelhutandroidopenvpnserviceManagementThread.java
Находим функцию public void sendSignal(int s) её мы и будем редактировать.
Комментируем отправку SIGUSR1 и вставляем вызов нашего скрипта с правами рут.
Файл скрипта у нас будет называться точно так же как и файл подключения, но дополненный расширением sh.
Должно получится так:
case SIGUSR1:
//sendCommand( new SimpleCommand( "signal SIGUSR1" ) );
new Shell(
"OpenVPN-Settings-ip-route",
String.format(
"/system/bin/sh %s.sh",
Util.shellEscape(mDaemonMonitor.mConfigFile.getAbsolutePath())
),
Shell.SU
).run();
break;
Всё, можно собирать, подписывать, устанавливать. Перед установкой обязательно деинсталлируйте старый «openvpn-setting» из системы, так как просто замена может оставить в кеше прежнюю версию байткода.
Далее проверим конфигурации подключения openvpn.
Сервер
На сервере требуется внести директиву float, поскольку мы будем менять физическое соединение, а с ним и IP. Так же сервер должен транслировать внутренний адрес нашего клиента во внешнюю сеть. Для настройки этого мне удобней использовать директивы up и down.
up "/bin/sh /etc/openvpn/androidupdown"
down "/bin/sh /etc/openvpn/androidupdown"
Скрипт должен быть примерно таким:
NATGW="1.2.3.4" # ip с которого будет натится клиент
NATDEV="eth0" # интерфейс с которого клиент получает доступ в общую сеть
NATNET="10.9.8.0/24" # vpn подсеть, которую надо транслировать во внешний мир
sysctl -w net.ipv4.ip_forward=1
ACT="A"
[ "down" = "$script_type" ] && ACT="D"
iptables -t nat -$ACT POSTROUTING -s $NATNET -o $NATDEV -j SNAT --to-source $NATGW
Клиент
Во первых надо убрать директиву redirect-gateway, если она есть. Но задать up и down с указанием скрипта, который выполнит настройку маршрутов. Если вы помните, мы условились, что имя файла будет повторять имя конфигурации с добавление sh. Да, мы будем вызывать один и тот же скрипт и из демона, и из монитора.
Если конфигурация называется «testconfig.ovpn», то пусть будет так:
up "/system/bin/sh /sdcard/openvpn/testconfig.ovpn.sh"
down "/system/bin/sh /sdcard/openvpn/testconfig.ovpn.sh"
Cкрипт
# Общая часть
export ANDROID_PROPERTY_WORKSPACE=12,66560 # Может зависеть от устройства
export PATH=/system/bin:/system/xbin:$PATH
setprop net.dns1 8.8.8.8 # всем известный DNS
setprop net.dns2 8.8.4.4
# Сработает только при вызове демоном openvpn.
if [ "init" = "$script_context" ]; then
ACT="add"
[ "down" = "$script_type" ] && ACT="del"
ip route $ACT throw $remote_1 table 100
ip route $ACT default dev $dev table 100
ip rule $ACT table 100 pref 1000
fi
Здесь стоит немного пояснить.
Мы создаем альтернативную таблицу маршрутизации под номером 100 в которой указываем шлюзом по умолчанию vpn туннель. И добавляем правило маршрутизации, которое заворачивает весь трафик в нашу таблицу под номером 100, раньше чем он попадет в основную таблицу под именем main.
В альтернативной таблице есть маршрут, который выталкивает пакеты предназначенные для сервера, обратно в цепочку правил. Далее они попадут в основную таблицу маршрутизации. Там они смогут найти основной маршрут физического соединения. Но, а если не найдут, такое случатся во время реконнектов, то это не страшно. Openvpn может потерпеть некоторое время, но разумеется не вечно. Только сообщения в логах, напомнят об отсутствии связи непродолжительное время.
Не забывайте скрипты должны заканчиваться переводом строки.
Для разрешения запуска скриптов на Android, необходимо установить «Built-in + script» в «Preferences» которые вызываются по долгому тапу на подключении. Теперь можно пробовать.
В результате тестирования оказалось, что некоторые программы сами проверяют состояние сети и пытаются переподключаться во время любых изменений. Тут либо просить авторов исправить ситуацию. Либо самостоятельно де компилировать и исправлять. В любом случае закрытие TCP сессии происходит, не по таймауту, а так как положено.
Буду рад любой критике и замечаниям. Желаю стабильного коннекта!