LINUX.ORG.RU
решено ФорумAdmin

Переключение между двумя провайдерами

 ,


7

4

Привет! Появился второй провайдер. Потребности в балансировке трафика нет, но зафейловерить линк во внешний канал не помешает. Основной провайдер предоставляет прямой линк (30 мегабит), резервный дает PPPoE (5 мегабит). Шлюзом выступает Debian 7.

Для выполенения задачи было решено держать оба линка поднятыми и в случае падения первого - переключать default gateway, подгружать альтернативный конфиг iptables и удаленно замеменять конфигурацию squid через SSH на прокси-сервере. Другой конфиг сквида разрешает доступ в интернет только тем пользователям, которым внешняя сеть критична для работы (Это около 15% всех пользоватлей). Учитывая специфику предприятия и то, что ширина резервного канала довольно мала, мне показалось это оптимальным решением.

Для автоматизации процесса я написал скрипт, который запускается по крону каждые пять минут. В моей ситуации это приемлемое время недоступности внешнего канала.

Проблема вот в чем. Мне нужно проверять доступность обоих каналов независимо от того, какой сейчас используется. Для этого я выбрал самый простой способ - ping -I $iface. Правильно ли? Есть что-то лучше?

Когда активный основной линк, я могу пинговать с обоих интерфейсов, -I eth6 и -I ppp0 работают и отвечают. Когда шлюз по умолчанию меняется на резервный, -I ppp0 работает, а вот -I eth6 перестает, - «destination host unreachable». В итоге, после переключения на резервный канал, обратно я уже не возвращаюсь, потому что проверка основного канала заканчивается провалом независимо от его состояния. Почему так?

# основной
# ip r
default via 85.1.1.45 dev eth6
10.0.0.0/21 dev eth1  proto kernel  scope link  src 10.0.0.2
90.2.66.97 dev ppp0  proto kernel  scope link  src 90.2.2.2
85.1.1.44/30 dev eth6  proto kernel  scope link  src 85.1.1.46
# резервный
# ip r
default dev ppp0 scope link
10.0.0.0/21 dev eth1  proto kernel  scope link  src 10.0.0.2
90.2.66.97 dev ppp0  proto kernel  scope link  src 90.2.2.2
85.1.1.44/30 dev eth6  proto kernel  scope link  src 85.1.1.46
Как заставить это работать? Что можно улучшить/упростить? AS, BGP не предлагать :]

Любую информацию предоставлю. Скрипт прикладываю. Баш только изучаю, если есть замечания, ошибки, предложения - пожалуйста, расскажите :)

#!/bin/bash
LOG="/var/log/isp-connect.log"
exec &>>$LOG
CURRENT_IF=$(ip r | grep "default" | awk '{print $5}')
PINGHOST1="8.8.8.8"
PINGHOST2="8.8.4.4"
WAN1_IF="eth6"
WAN1_IP="85.1.1.46"
WAN1_GW="85.1.1.45"
WAN2_IF="ppp0"
WAN2_IP="90.2.2.2"
DATE=$(date "+%m/%d/%y %H:%M:%S")
SENDEMAIL="sendemail -f gw01@domain.ru -t log@domain.ru -u \"WAN STATUS\" -s mail01.my.domain.ru"
#
CHECK_WAN1() { ping -q -c 5 -I $WAN1_IF $PINGHOST1 > /dev/null || ping -q -c 5 -I $WAN1_IF $PINGHOST2 > /dev/null; }
CHECK_WAN2() { ping -q -c 5 -I $WAN2_IF $PINGHOST1 > /dev/null || ping -q -c 5 -I $WAN2_IF $PINGHOST2 > /dev/null; }
ROUTE_WAN1() { route del default; route add default gw $WAN1_GW $WAN1_IF; sleep 5; }
ROUTE_WAN2() { route del default; route add default dev $WAN2_IF; sleep 5; }
#
if [ "$CURRENT_IF" == $WAN1_IF ]; then
        CHECK_WAN1
        if [ $? -ne 0 ]; then
                echo "[FAIL] $DATE WAN1 link is DOWN."
                echo "[INFO] $DATE Checking WAN2."
                CHECK_WAN2
                if [ $? -ne 0 ]; then
                        echo "[FAIL] $DATE ISP-SWITCH failed. Cannot connect to WAN2."
                        $SENDEMAIL -m "[FAIL] $DATE WAN1 and WAN2 are down. Switching is not possible."
                        exit
                else
                        ROUTE_WAN2
                        echo "[OK] $DATE WAN2 connection established."
                        bash /etc/iptables/wan2.ipt
                        # ssh to squid-server placeholder
                        # echo "[OK] $DATE squid configuration changed to EMERGENCY."
                        $SENDEMAIL -m "[WARN] WAN1 is down. Switched to WAN2."
                        exit
                fi
        else
                echo "[INFO] $DATE WAN1 link is UP. There is nothing to do."
                exit
        fi
#
elif [ "$CURRENT_IF" == $WAN2_IF ]; then
        CHECK_WAN1
        if [ $? -ne 0 ]; then
                echo "[INFO] $DATE WAN1 link is still DOWN."
                exit
        else
                echo "[INFO] $DATE WAN1 link looks like UP."
                echo "[INFO] $DATE Trying WAN1."
                ROUTE_WAN1
                CHECK_WAN1
                        if [ $? -ne 0 ]; then
                                echo "[FAIL] $DATE Cannot verify WAN1 connection."
                                echo "[INFO] $DATE ISP-SWITCH was canceled."
                                ROUTE_WAN2
                                exit
                        else
                                echo "[OK] $DATE WAN1 connection established."
                                echo "[OK] $DATE Changing squid3 configuration to MAIN."
                                $SENDEMAIL -m "[OK] WAN1 is back. Switched to WAN1."
                                bash /etc/iptables/wan1.ipt
                                # ssh to squid-server placeholder
                                exit
                        fi
        fi
fi

Помоги ЛОР там, где не смог помочь гугл :(


Подтянусь, тоже интересно

dvrts ★★★ ()
Ответ на: комментарий от zolden

Если я правильно понимаю, при падении основного провайдера линк будет жить. А значит и маршрут не исчезнет, а вес у него больше независимо от того, работает он или нет; по второму маршруту, с меньшим весом, никто так и не пойдет. Я ошибаюсь?

strnd ()
Ответ на: комментарий от strnd

Не ошибаешься. Нужно сделать две routing table, для каждого провайдера, и добавить соотв. правила:

ip rule add from $IP1 lookup $rt_table1
ip rule add from $IP2 lookup $rt_table2
После этого для пакетов, выходящих с интерфейса, будет применяться таблица нужного провайдера, с нужным default gw. Задача достаточно частая, вот например мой велосипед: https://github.com/selivan/inet-failover/

selivan ★★★ ()
Последнее исправление: selivan (всего исправлений: 1)

Я тебе просто кину информацию для размышления, не претендую ни на что, просто тебе на подумать...

У меня примерно аналогичная ситуация в удалённом филиале. Как показала моя практика - все эти пинги, чудо автоматизация, куча скриптов, которые там раз в пять минут запускаются, потом раз в десять минут прыгают на колене... - Всё это УГ. Вот поломается у твоего провайдера частично маршрутизация... - А такое может быть. И ты возьмёшь и своим чудо скриптом уйдёшь на резерв. А тебе может этого вовсе и не нужно, так-как поломаться может и провайдер выше...

Есть такой демон: ifupd или как-то так. - В общем оно реагирует на появление кабеля в сет. интерфейсе. Я повесил на него ethernet модем, сам модем пустил через рубильник. - Когда пропадает Интернет, чувак идёт, нажимает рубильник - появляется запасной канал. Я звоню к провайдеру который поломался, и выясняю в чём дело. Когда провайдер меня убеждает что всё починили, идёт чувак и выключает рубильник. - ifupd запускает нужные скрипты и всё становится как было. - Этим я решаю много проблем, и ухожу от многих глюков.

DALDON ★★★★★ ()
Ответ на: комментарий от selivan

Получается пинг не работает потому что не тот default gateway? Тогда почему такой проблемы нет с основным провайдером? Когда я на primary канале, могу пинговать с резервного интерфейса. А когда на резервном - не могу пинговать с основного. Почему так происходит?

strnd ()
Ответ на: комментарий от strnd

Я ошибаюсь?

Да
Если у тебя станет недоступным gw1, то зачем на него слать пакеты?

zolden ★★★★★ ()
Ответ на: комментарий от selivan

Вот так это работает с основным каналом.

ping -I ppp0 8.8.8.8
PING 8.8.8.8 (8.8.8.8) from 90.2.2.2 ppp0: 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_req=1 ttl=49 time=33.8 ms

ping -I eth6 8.8.8.8
PING 8.8.8.8 (8.8.8.8) from 85.1.1.46 eth6: 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_req=1 ttl=48 time=33.3 ms

Вот так это не работает на резерве.
ping -I ppp0 8.8.8.8
PING 8.8.8.8 (8.8.8.8) from 90.2.2.2 ppp0: 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_req=1 ttl=49 time=33.8 ms

ping -I eth6 8.8.8.8
PING 8.8.8.8 (8.8.8.8) from 85.1.1.46 eth6: 56(84) bytes of data.
From 85.198.125.146 icmp_seq=1 Destination Host Unreachable

мне интересен этот момент. В чем разница? Меняется только default gateway и конфиг iptables с другими переменными $WAN_IF и $WAN_IP

$IPT -t nat -A POSTROUTING -o $WAN_IF -j SNAT --to-source $WAN_IP

strnd ()
Ответ на: комментарий от strnd

Дай выводы route -n в обоих случаях.

Мое подозрение - при работе с основным каналом у тебя присутствуют 2 шлюза по умолчанию, после перехода на резерв остается только один.

menzoberronzan ()
Последнее исправление: menzoberronzan (всего исправлений: 1)

1. пиши функции по человечески, без всяких >/dev/null и «||». (первое лечится ключом -q, а второе if/fi).

2. проверять можно просто if ! CHECK_WAN1; then...

3. не нужно ломать логику программы с помощью exit внутри if.

4. bash /etc/iptables/wan1.ipt тут надо source, а лучше функцию (возможно из source).

emulek ()
Ответ на: комментарий от DALDON

У меня примерно аналогичная ситуация в удалённом филиале. Как показала моя практика - все эти пинги, чудо автоматизация, куча скриптов, которые там раз в пять минут запускаются, потом раз в десять минут прыгают на колене... - Всё это УГ.

++

emulek ()
Ответ на: комментарий от strnd

Значит он всё равно пингует с основного. Можешь проверить traceroute с опцией -i , увидишь одинаковые маршруты через основного провайдера. Что-то ты там с nat перемудрил.

Если нужна возможность использовать интернет одновременно с обоих каналов(что даёт возможность проверки резервного канала при использовании основного), надо делать так:

- добавить с основную таблицу маршрутов обоих провайдеров, с разными метриками
- создать дополнительные таблицы маршрутов для каждого провайдера. В таблице должна присутствовать его подсеть и его default gw
- добавить в правила прохождения таблиц маршрутизации (ip rule show): для исходящих пакетов с соотв. IP - использовать соотв. таблицу

Ещё есть вариант с multipath routing - задавать веса маршрутов через разные шлюзы, но такого не пробовал. Пример: http://kb.kaminskiengineering.com/node/33

DALDON: у меня такая схема работала в нескольких филиалах. Вполне надёжно и ничего сложного, если разобраться.

selivan ★★★ ()
Ответ на: комментарий от DALDON

Как показала моя практика - Всё это УГ
оно реагирует на появление кабеля в сет. интерфейсе

Жуть какая...а сам сет. интерфейс резервируется?

zolden ★★★★★ ()
Ответ на: комментарий от selivan

Интересная идея, но нет, маршруты разные.

# traceroute -i eth6 ya.ru
traceroute to ya.ru (213.180.193.3), 30 hops max, 60 byte packets
 1  85-1-1-45.rst.unitline.ru (85.1.1.45)  0.945 ms  0.953 ms  0.935 ms
 2  10.61.4.109 (10.61.4.109)  2.674 ms  3.777 ms  3.761 ms
 3  10.61.4.105 (10.61.4.105)  3.755 ms  3.743 ms  3.706 ms
 4  10.61.0.1 (10.61.0.1)  255.468 ms  255.462 ms  255.431 ms^C
# traceroute -i ppp0 ya.ru
traceroute to ya.ru (213.180.193.3), 30 hops max, 60 byte packets
 1  asr1-ats66.aaanet.ru (90.2.66.97)  0.658 ms  0.632 ms  0.596 ms
 2  g0-ats32-red.aaanet.ru (213.27.31.71)  0.905 ms  0.916 ms  0.887 ms
 3  ler-cr01-xe-0-0-1.0.rnd.stream-internet.net (212.188.16.37)  1.132 ms  1.112 ms  1.082 ms
 4  m9-cr04-be8.61.msk.stream-internet.net (212.188.29.5)  21.299 ms  23.618 ms  23.742 ms
 5  m9-cr02-po6.msk.stream-internet.net (195.34.59.242)  20.901 ms  21.008 ms  21.138 ms

menzoberronzan, я в первом посту дал вывод ip r. В route -n, так же, один default gateway в обоих случаях.

strnd ()
Ответ на: комментарий от emulek

По всем пунктам понял, учту, исправлю, спасибо. Подскажите пожалуйста только, чем плох exit? В чем нарушение логики?

strnd ()
Ответ на: комментарий от zolden

Жуть, не жуть, а была ситуация: вот взял провайдер, и две площадки в разных концах московской области стал маршрутизировать через... Внимание: Франкфурт... - То есть мои ping по VPN с 4-5ms поднялись до 80ms, а часть пакетов так и вовсе терялась. Мой запасной канал радио. Там задержки в силу того что радио: сами по себе уже 50-90ms.

Ну вот скажите: на каком канале я должен сидеть из этих двух был? А провайдер поменял маршрутизацию, ибо магистральные провайдеры про меж себя разосрались там... - Я задолбал там всех... - Они пол года трахали мне мозг... Или ещё были чудеса: пока они там разруливали с маршрутизацией, у них иногда бывали потери по 10-20 пакетов, на уровне магистральных провайдеров. - Вот скажите как бы повела себя чудо скриптовая пинговалка которую Вы тут так хвалите? Она бы переключала мне всё зад-назад... И был бы бардак! А если этот чудо набор «автоматики» затупит..? - Мне пришлось бы ехать на другой конец Подмосковья и разруливать там эти ситуации т.к. сис. админа в офисе нету. Только приходящий (который не понимает в серверах особо).

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

Так что считаю, что все эти чудо автоматы с ping в своей основе - опасная штука. Одно дело когда ты рядом с машинкой, другое когда она от тебя за сотни км.

DALDON ★★★★★ ()
Ответ на: комментарий от strnd

чем плох exit? В чем нарушение логики?

в нарушении логики. «если Вася идиот, то он не может писать нормальный код». Тут логика есть. А в фразе

если Вася идиот
  exit
то он не может писать нормальный код
логики нет.

Т.е. точка выхода должна быть одна. Либо точек выхода может быть много, но они все должны иметь одинаковый смысл(и в этом случае лучше делать функцию с несколькими return).

В bash'е такой говнокод даже работает, но вот в сишечке ты сразу поимеешь множество проблем, начиная с утечек памяти.

Т.е. если тебе нужен мультивыбор, то его нужно оформлять в виде функции, или case'а.

Это не относится к ЯП, это скорее вопрос стиля. Говнокод с кучей exit работает, но его нереально поддерживать. Тут exit является аналогом goto.

emulek ()
Ответ на: комментарий от DALDON

Вот скажите как бы повела себя чудо скриптовая пинговалка которую Вы тут так хвалите?

а каждое решение хорошо только в своих условиях. У вашего решения тоже есть недостатки. Человеческий фактор, не слышали? Скрипт не спит и не бухает, в отличие от вашего чувака. И жрать не просит.

emulek ()
Ответ на: комментарий от emulek

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

Человеческий фактор - не, не слышал. Так-как включить выключатель, может не один человек, а по звонку, это может сделать любой адекватный человек в офисе.

DALDON ★★★★★ ()
Последнее исправление: DALDON (всего исправлений: 1)
Ответ на: комментарий от DALDON

Я пишу ребят, не ради спора, а просто - может кому пригодится. :)

DALDON ★★★★★ ()

Вот мой вариант:

#!/bin/sh
#v.001a 2014-09-09

 GW_main="gw 192.168.21.1"
 Int_main="eth0"
 GW_res="dev ppp0"
 Int_res="ppp0"
 LAN_host="172.30.4.0"
 host="172.30.4.65"
 operator="beeline"
 chk="0"
 delay="5"
 on_off_log="1"
 log="/var/log/routeswitch"
 
 setmaingw(){
 route del -net $LAN_host netmask 255.255.255.0 $GW_main metric 1
 route del -net $LAN_host netmask 255.255.255.0 $GW_res metric 0
 route add -net $LAN_host netmask 255.255.255.0 $GW_main metric 0
 route add -net $LAN_host netmask 255.255.255.0 $GW_res metric 1
 }
 
 setresgw(){
 route del -net $LAN_host netmask 255.255.255.0 $GW_main metric 0
 route del -net $LAN_host netmask 255.255.255.0 $GW_res metric 1
 route add -net $LAN_host netmask 255.255.255.0 $GW_main metric 1
 route add -net $LAN_host netmask 255.255.255.0 $GW_res metric 0
 }
 
 chkdefault(){
# echo ***$DATE Chek Route default
# echo $(ip r | grep "default" | awk '{print $3}') _ $Int_res
 if [ "$(ip r | grep "default" | awk '{print $3}')" == "$Int_res" ];
 then
#	echo ***$DATE Route del default
	route del default
# else
#	echo ***$DATE Route default is missing
 fi	
 }
 
 
#
 # Start Script
 echo $(date "+%m/%d/%y %H:%M:%S") Start Script RouteSwitch
 sleep 5
 echo $(date "+%m/%d/%y %H:%M:%S") Add IP 192.168.21.100
 ifconfig eth0:0 192.168.21.100
 sleep 5
 echo $(date "+%m/%d/%y %H:%M:%S") Kill pppd
 killall pppd
 sleep 5
 echo $(date "+%m/%d/%y %H:%M:%S") Call pppd $operator
 pppd call $operator
 sleep 5
 chkdefault
 sleep 5
 echo  Set main channel
 route del default
 route add -net $LAN_host netmask 255.255.255.0 $GW_main metric 0
 route add -net $LAN_host netmask 255.255.255.0 $GW_res metric 1
 if [ $on_off_log == "1" ]
 then
	echo $(date "+%m/%d/%y %H:%M:%S") Start. Set main channel >> $log
 fi
 while :
 do
	echo $(date "+%m/%d/%y %H:%M:%S") Check main channel
	chkdefault
	sleep 5
	echo $(date "+%m/%d/%y %H:%M:%S") Ping from $Int_main to $host
	ping -c 10 -I $Int_main $host > /dev/null
	if [ $? -ne 0 ];
	then
		echo $(date "+%m/%d/%y %H:%M:%S") Main channel is not available
		echo $(date "+%m/%d/%y %H:%M:%S") Check reserve channel
		chkdefault
		sleep 5
		echo $(date "+%m/%d/%y %H:%M:%S") Ping from $Int_res to $host
		ping -c 10 -I $Int_res $host > /dev/null
		if [ $? -ne 0 ];
		then
			echo $(date "+%m/%d/%y %H:%M:%S") Main and Reserve channels is not available
			if [ $on_off_log == "1" ];
			then
				echo $(date "+%m/%d/%y %H:%M:%S") Main and Reserve channels is not available >> $log
			fi
		else
			if [ $chk == 0 ];
			then
				echo $(date "+%m/%d/%y %H:%M:%S") Set reserve channel
				setresgw
				if [ $on_off_log == "1" ];
				then
					echo $(date "+%m/%d/%y %H:%M:%S") Set reserve channel >> $log
				fi
				echo $(date "+%m/%d/%y %H:%M:%S") wait $delay
				sleep $delay
				let "chk = 1"
			else
				echo $(date "+%m/%d/%y %H:%M:%S") Reserve channel is available.
			fi
		fi
	else
		if [ $chk == 1 ];
		then
			echo $(date "+%m/%d/%y %H:%M:%S") Set main channel
			setmaingw
			if [ $on_off_log == "1" ];
			then
				echo $(date "+%m/%d/%y %H:%M:%S") Set main channel >> $log
			fi
			sleep 5
			let "chk = 0"
		else
			echo $(date "+%m/%d/%y %H:%M:%S") Main channel is available
			echo $(date "+%m/%d/%y %H:%M:%S") Wait $delay second
			sleep  $delay
		fi
	fi
 done

 exit
Есть нюанс. Когда устанавливается модемное соединение, в таблице маршрутизации появляется шлюз по умолчанию собственно модема. И тогда основной канал через интерфейс eth0 не пингуется. По этому перед проверкой канала появляется функция chkdefault, которая проверяет наличие этой и если есть удаляет default. По этому же поводу использую изменение метрики, а не установку шлюза по умолчанию. Как только в таблице маршрутизации появляется default, всё работает через основной шлюз, независимо от того какой интерфейс используется при пингах.

dartalex ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.