Openvpn на Adnroid прозрачное переключение между WiFi и «Мобильными данными» без разрыва соединений

от автора

Здравствуйте, разрешите поделиться своим опытом.

Есть приложения критичные к разрывам связи, переподключение происходит мучительно и вообще не всегда. Поставил перед собой цель, сделать прыжки маршрутов и физических подключений прозрачными, что бы связь была постоянной и 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/
Открываем файл
\src\de\schaeuffelhut\android\openvpn\service\ManagementThread.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  
Клиент

На клиенте также необходимо задать директиву 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 сессии происходит, не по таймауту, а так как положено.

Буду рад любой критике и замечаниям. Желаю стабильного коннекта!

ссылка на оригинал статьи http://habrahabr.ru/post/157687/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *