Хотите поблагодарить автора блога - жмите здесь !
1 2 0
1 2 0

Нажмите на рекламный баннер выше, если хотите поблагодарить автора блога !
Написание каждой статьи заняло в сотни раз больше времени, чем один клик.

воскресенье, июня 23, 2013

TPLINK 1043ND (OpenWrt) - подключение двух внешних каналов (Dual WAN) основного и резервного.

Для повышения надежности соединения, возник вопрос подключения одновременно двух внешних каналов (Dual WAN). При чем оба с "динамическими" IP. При этом нужно было обеспечить возможность заходить снаружи на сервер, не зависимо от того на каком он из каналов, обеспечив работоспособность L2TP/IPSec туннеля. Выбор каналов осуществляется их тестированием, так как "падение связи по каналу" далеко не всегда связано с "падением интерфейса" этого канала. В качестве "основного" на данный момент используется канал "домашней сети", в качестве "резервного" - CDMA 3G. Скрипты которые будут представлены, рассчитаны на работу с макс 3мя каналами (на будущее). В материале будет приведено все что необходимо для вышеуказанного. А именно - скрипт и конфигурация для работы с 2мя (основным и резервным, или 3мя - основным и двумя резервными) внешними каналами, автоматического тестирования их работоспособности и выбора рабочего, скрипт для работы с динамическим DNS, для обеспечения доступа снаружи, и возможности использовать свой ВЭБ сервер на рутере, и его доступность, а также настройка L2TP/IPSec туннеля, и скрипт, необходимый для его бесперебойной работы в случае смены активного внешнего канала. Все материалы взаимосвязаны, но я буду их описывать несколькими отдельными публикациями - по мере наличия времени. Все описанное, пригодно для любого сервера или роутера, работающего под ОС Linux. Максимум что может потребоваться для адаптации под другие дистрибутивы Линукса - смена путей, установка доп пакетов.

Настройка двух внешних каналов (DualWan) в OPENWRT.

 Само по себе поднятие каналов, производится стандартными средствами OS, которые в разных дистрибутах могут отличаться.
Для OpenWrt, может вызвать какието проблемы только поднятие 3G, хотя на самом деле это делается достаточно просто, через вэб интерфейс управления. Но на всякий случай привожу секции конфига, связанные с 3G каналом для OPENWRT:
В файл /etc/config/network добавлена секция:
config interface 'CDMADnepr'
        option proto '3g'
        option device '/dev/ttyACM0'
        option service 'evdo'
        option username 'IT'
        option password 'IT'
        option dns '8.8.8.8 8.8.4.4'
        option peerdns '0'
        option metric '50'
В /etc/ppp/options у меня сейчас:
debug
#logfile /dev/null
logfile /var/log/ppp.log
noipdefault
noaccomp
nopcomp
nocrtscts
lock
maxfail 0
lcp-echo-failure 5
lcp-echo-interval 1

Отладку понятно можно отключить. А lcp-echo-interval и lcp-echo-failure, особенно если Вы находитесь в месте, где 3G не очень стабильно (плохое покрытие) - рекомендую увеличить. Это в частности касается массива теремки-2 в Киеве :)

После подьема обеих каналов (один у Вас вероятно для OPENWRT будет на интерфейсе eth0.2, и второй на интерфейсе  CDMADnepr (или как Вы его назовете в /etc/config/network), проверьте что они работают каждый по отдельности - выключите оба, потом включите один из них - проверьте что связь есть. Потом снова выключите оба, потом включите другой и тоже проверьте. PS На других дистрибутах имена интерфейсов могут быть другими.

Итак - два канала подняли. Убедились что каждый из них работает по отдельности.

А теперь самое интересное и вкусное - скрипт, который обеспечивает тестирование каналов и автоматическое переключение на резервные и назад, в случае отказа основного канала, или первого резервного.


root@Dnepr_Root:/etc/config# cat /usr/sbin/ChanCheck
#!/bin/bash
LogFile="/var/log/ChanCheck.log"
#Ifaces name
NOIF='NONE'
IFM=eth0.2
IFR1=3g-CDMADnepr
IFR2=$NOIF
IFMA=wan
IFR1A=CDMADnepr
IFR2A=$NOIF
#Ifaces status. 1 - down, 0 - OK
MOK=0
R1OK=0
R2OK=0
#Ifaces exist. 1 - not exist, 0 - exist
MEX=0
R1EX=0
R2EX=0
#IP addresses VIA for ifaces for default routing
MIP=''
R1IP=''
R2IP=''
#Metrics for interfaces
MM='1'
MR1='20'
MR2='20'
#Default for interface exist 1 - not exist, 0 - exist
MDEF=0
R1DEF=0
R2DEF=0
#Current interface in use. 0 - none, 1 - Main, 2 - Res1, 3 - Res2
CURIF=0
CURIFN=$IFM
#Which interface may be in use. 0 - none, 1 -Main, 2 - Res1, 3 - Res2
WORKIF=0
StopScript=0
#External programs, starting after changing to each interface
NOPROG='NONE'
PROGME='YES'
PROGM='/usr/sbin/ChanIPSecM'
PROGR1E='YES'
PROGR1='/usr/sbin/ChanIPSecR1'
PROGR2E=$NOPROG
PROGR2=$NOPROG


function checkdefault {
        ip ro li | grep default | grep $IFM  > /dev/null 2>&1
        MDEF=$?
        ip ro li | grep default | grep $IFR1  > /dev/null 2>&1
        R1DEF=$?
        ip ro li | grep default | grep $IFR2  > /dev/null 2>&1
        R2DEF=$?
        if [ $IFM != $NOIF ]
        then
                ifconfig $IFM | grep UP > /dev/null 2>&1
                MEX=$?
        else
                MEX=1
        fi
        if [ $IFR1 != $NOIF ]
        then
                ifconfig $IFR1 | grep UP > /dev/null 2>&1
                R1EX=$?
        else
                R1EX=1
        fi
        if [ $IFR2 != $NOIF ]
        then
                ifconfig $IFR2 | grep UP > /dev/null 2>&1
                R2EX=$?
        else
                R2EX=1
        fi

        if [ $MEX -eq 0 ]
        then
                if [ $MDEF -ne 0 ]
                then
                        ifdown $IFMA > /dev/null 2>&1
                        ifup $IFMA > /dev/null 2>&1
                        logger -p user.warning -t Channel_Switch "Restore from fault for interface: $IFM"
                        date >> $LogFile
                        echo "Restore from fault for interface: $IFM" >> $LogFile
                        CURIF=0
                        sleep 2
                fi
         fi
         if [ $R1EX -eq 0 ]
         then
                if [ $R1DEF -ne 0 ]
                then
                        ifdown $IFR1A > /dev/null 2>&1
                        ifup $IFR1A > /dev/null 2>&1
                        logger -p user.warning -t Channel_Switch "Restore from fault for interface: $IFR1"
                        date >> $LogFile
                        echo "Restore from fault for interface: $IFR1" >> $LogFile
                        CURIF=0
                        sleep 2
                fi
         fi
         if [ $R2EX -eq 0 ]
         then
                if [ $R2DEF -ne 0 ]
                then
                        ifdown $IFR2A > /dev/null 2>&1
                        ifup $IFR2A > /dev/null 2>&1
                        logger -p user.warning -t Channel_Switch "Restore from fault for interface: $IFR2"
                        date >> $LogFile
                        echo "Restore from fault for interface: $IFR2" >> $LogFile
                        CURIF=0
                        sleep 2
                fi
         fi
}

function checkchan {
        ping -c 4 -I $IFM 8.8.8.8 > /dev/null 2>&1
        MOK=$?
        ping -c 4 -I $IFR1 8.8.4.4 > /dev/null 2>&1
        R1OK=$?
        ping -c 4 -I $IFR2 8.8.8.8 > /dev/null 2>&1
        R2OK=$?

        ifconfig $IFM  > /dev/null 2>&1
        MEX=$?
        ifconfig $IFR1  > /dev/null 2>&1
        R1EX=$?
        ifconfig $IFR2  > /dev/null 2>&1
        R2EX=$?

        MIP=$(ip ro li | grep default | grep $IFM | cut -d' ' -f3)      > /dev/null 2>&1
        R1IP=$(ip ro li | grep default | grep $IFR1 | cut -d' ' -f3)    > /dev/null 2>&1
        R2IP=$(ip ro li | grep default | grep $IFR2  | cut -d' ' -f3)   > /dev/null 2>&1

        if [ $MOK -eq 0 ]
        then
                WORKIF=1
                CURIFN=$IFM
                MM=1
                MR1=20
                MR2=20
        elif [ $R1OK -eq 0 ]
        then
                WORKIF=2
                CURIFN=$IFR1
                MM=20
                MR1=1
                MR2=20
        elif [ $R2OK -eq 0 ]
        then
                WORKIF=3
                CURIFN=$IFR2
                MM=20
                MR1=20
                MR2=1
        else
                WORKIF=0
                CURIFN='All channels BAD !'
                MM=1
                MR1=20
                MR2=20
        fi

        if [ $WORKIF -ne $CURIF ]
        then
                date >> $LogFile
                ip ro del default dev $IFM       > /dev/null 2>&1
                ip ro del default dev $IFR1      > /dev/null 2>&1
                ip ro del default dev $IFR2      > /dev/null 2>&1
                if [ $MEX -eq 0 ]
                then
                        if [ -n $MIP ]
                        then
                                ip ro add default dev $IFM via $MIP metric $MM
                        fi
                fi
                if [ $R1EX -eq 0 ]
                then
                        if [ -n $R1IP ]
                        then
                                ip ro add default dev $IFR1 via $R1IP metric $MR1
                        fi
                fi
                if [ $R2EX -eq 0 ]
                then
                        if [ -n $R2IP ]
                        then
                                ip ro add default dev $IFR2 via $R2IP metric $MR2
                        fi
                fi
                CURIF=$WORKIF
                ip ro flush cache
                logger -p user.warning -t Channel_Switch "Current channel interface is: $CURIFN"
                echo "Current channel interface is: $CURIFN" >> $LogFile
                if [ $MM -eq 1 ]
                then
                        if [ $PROGME != $NOPROG ]
                        then
                                 $PROGM > /dev/null 2>&1
                                 logger -p user.warning -t Channel_Switch "Ipsec restart script $PROGM, executed"
                                 echo "Ipsec restart script $PROGM, execute" >> $LogFile
                        fi
                fi
                if [ $MR1 -eq 1 ]
                then
                        if [ $PROGR1E != $NOPROG ]
                        then
                                 $PROGR1 > /dev/null 2>&1
                                 logger -p user.warning -t Channel_Switch "Ipsec restart script $PROGR1, executed"
                                 echo "Ipsec restart script $PROGR1, execute" >> $LogFile
                        fi
                fi
                if [ $MR2 -eq 1 ]
                then
                        if [ $PROGR2E != $NOPROG ]
                        then
                                 $PROGR2 > /dev/null 2>&1
                                 logger -p user.warning -t Channel_Switch "Ipsec restart script $PROGR2, executed"
                                 echo "Ipsec restart script $PROGR2, execute" >> $LogFile
                        fi
                fi
        fi
}

echo "ChanCheck started" > $LogFile
date >> $LogFile
echo "==========================================================================" >> $LogFile

while [  $StopScript -ne 1 ]; do
        checkdefault
        checkchan
        sleep 10
done
===============================
Запуск скрипта прописывается в /etc/rc.local строкой:
/usr/sbin/ChanCheck&
Настройки соединения 3G CDMA приведены для оператора InterTeleCom Ukraine

Коротко об настройке скрипта и принципе его работы.

Для тестирования каналов, необходимо чтобы все они были включены - это понятно. При этом для каждого в таблицу роутинга вписан "default". Чтобы реальный траффик шел по нужному нам каналу, скрипт изменяет  только метрики интерфейсов каждого из каналов (точнее метрики записей default route для каждого из нитерфейсов). В переменных MM, MR1, MR2 - вписаны метрики, присваемые каналам, MM - основного, и далее двух резервных. ВАЖНО - Метрика основного канала должна быть установлена в 1 - в нее же устанавливается метрика активного, если основной "падает" - это необходимо для правильного вызова программ, срабатывающих при смене канала (необходимых для работы L2TP/IPSec).
В переменных
IFM=eth0.2
IFR1=3g-CDMADnepr
IFR2=$NOIF
IFMA=wan
IFR1A=CDMADnepr
IFR2A=$NOIF
Вписаны имена интерфейсов. Почему они вписаны дважды. В прошивке OpenWrt, для использования команд типа "ip ro li...", используется одно имя интерфейса (они в переменных IFM, IFR1, IFR2), а для использования в коммандах ipup,  ifdown - другие - они в переменных IFMA, IFR1A, IFR2A. У Вас на других дистрибутивах эти имена могут и совпадать. Тогда соответственно вторые 3 переменных будут повторять значения первых 3х. Если соответствущего интерфейса нет (у меня напр сча 2 канала), то переменные имени соответствующего интерфейса утсанавливаются в "$NOIF" как в примере для IFR2, IFR2A.
Остальные переменные рабочие, их менять не надо, кроме
NOPROG='NONE'
PROGME='YES'
PROGM='/usr/sbin/ChanIPSecM'
PROGR1E='YES'
PROGR1='/usr/sbin/ChanIPSecR1'
PROGR2E=$NOPROG
PROGR2=$NOPROG
Эти переменные отвечают за запуск программ настройки (в моем случае туннеля L2Tp/IPSec)  на новые параметры, при смене активного интерфейса. Соотв при переходе напр на "первый резервный", запускается программа, указанная в переменной PROGR1=. Если для какого то канала (или для всех) никаких доп программ запускать не нужно, обе переменные для этого канала ставятся в значение "$NOPROG". Как для второго резервного канала в приведенном примере.

Да. а как же я обеспечил тестирование работоспособности каналов, по которым в данный момен траффик не идет ? Ну например того что "основной встал", когда у нас трафик идет по резервному ? Очень просто. В примерах что я видел это делали сложным "source routing", и кучей доп таблиц. Но по моему этот путь излишен сложен и соотв излишен глюкав (достаточно попробвовать штатный скрипт MULTIWAN входящий в OpenWRT, чтобы это понять). Есть куда более простой:
        ping -c 4 -I $IFM 8.8.8.8 > /dev/null 2>&1
        MOK=$?
Обычный пинг, с задаванием ему имени интерфейса, роутинг через который хотим протестить.

Продолжение следует. напомню, что нас еще ждет скрипт для динамического DNS, а также настройка L2TP/IPSec, настройка L2TP/IPSec в условиях возможной смены рабочего канала "на лету", а также описание как настроить клиентов в Android и Windows7 для L2TP/IPSec, и особенности. Ну а потом может еще какие нюансы работы с OPENWRT, которые мне показались крайне полезными. также, если когото заинтересует и будут просьбы - могу описать настройку ASTERISK (SIP телефонии) на рутере OPENWRT, с шифрованием (фик кто подслушает - перехватит).
  
 



Комментариев нет:

Отправить комментарий

Закрыть окно X
Пожалуйста, потратьте несколько секунд на поддержку блога и его автора
Нажмите на рекламную ссылку: Рекламная ссылка для поддержки блога, или на баннер вверху справа страницы.