LINUX.ORG.RU

Поправить bash-скрипт.

 


0

2

Наколякал скрипт для сервака с двумя gw чтобы при падении первого переключаться на второй, а после «воскрешения» первого возращаться на него же.

Вот что получилось:

#!/bin/bash

GW1=0.0.0.0
DEF_INTERFACE=ppp0
GW2=192.168.1.1
SECOND_INTERFACE=eth1

while true
do
       if ping -I $DEF_INTERFACE -c5 8.8.8.8 | grep "100% packet loss" > /dev/null
        then
            ip route del default via $GW1 dev $DEF_INTERFACE
            ip route add default via $GW2 dev $SECOND_INTERFACE
        else
            if route | grep default | grep 192.168.1.1 > /dev/null
             then
                 ip route del default via $GW2 dev $SECOND_INTERFACE
                 ip route add default via $GW1 dev $DEF_INTERFACE
            fi
       fi

done


Всё работает отлично, но после первого переключения в консоль ползут сообщения вида:

RTNETLINK answers: No such process

RTNETLINK answers: File exists

То есть где-то не срабатывает условие-if? Попрошу не кидаться тапками, т.к bash ещё плохо знаю.

т.к bash ещё плохо знаю.

У вас не с bash проблема, а в алгоритме. Всё время пингуемтся с одного интерфейса и пытается переключаться.

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

А чем плох? Мы же постоянным пингом одного интерфейса следим за ним и когда он падает поднимаем запасной gw, а этот убираем. А когда он поднимается, то поднимаем и его.

FluffyPillow ()

Например, ppp0 упал, т.е. его вообще больше нет в системе, и тогда исчезнут все маршруты через него, в том числе и дефолтный. И тогда при вызове ip route del default тебе выдаст RTNETLINK answers: No such process.

Нюансов много. Без подробного описания, в какой момент вылезло то или иное сообщение, и что в этот момент происходило, сказать ничего нельзя.

Еще совет: попробуй заменить del+add на ip route replace default. Одна команда вместо двух, плюс не будет спамить при отсутствии дефолтного маршрута.

Баш не тестил.

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

А чем плох?

После переключения у вас пропадёт ping основного канала. Самое простое в таких случаях пинговать удалённую сторону (того основного провайдера), когда пакеты туда пойдут без default gw

Вы вообще до сих пор не продетектили в каком «then» у вас происходит ругань?

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

Тогда пару вопросов:

1. Физический дисконнект кабеля == простому исчезновению коннекта для системы? Или же есть отличия в тайм-ауте и очистки таблицы от маршрутов упавшего интерфейса?

2. Если моё решение не очень, то есть какие идеи как можно сделать? Хоть куда копать подскажите.

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

Так если переключение сработало, то значит основной интерфейс упал и пинг не будет отрабатывать на «ок», но сама команда ведь будет долбить оставшийся в системе gw и если интерфейс поднимится, то и пинг снова пойдет, и произойдёт обратное переключение. Другое дело, что я не учёл, что система сама удаляет умершие маршруты с интерфейсами.

Вы вообще до сих пор не продетектили в каком «then» у вас происходит ругань?

       if ping -I $DEF_INTERFACE -c5 8.8.8.8 | grep "100% packet loss" > /dev/null
        then
            ip route del default via $GW1 dev $DEF_INTERFACE
            ip route add default via $GW2 dev $SECOND_INTERFACE

Этот кусок кода выполняется даже после переключения на резервный канал, при условии что основной канал отключён.

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

Этот кусок кода выполняется даже после

Ну дык. А я о чём вам сказал?

Вам надо узнать ifconfig ppp0 | grep -o 'destination [0-9][0-9.]*' Если это выдало адрес, значить основной интерфейс поднят и есть адрес провайдерского маршрутизатора, вот его и пингуем, а не 8.8.8.8

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

1. Физический дисконнект кабеля == простому исчезновению коннекта для системы? Или же есть отличия в тайм-ауте и очистки таблицы от маршрутов упавшего интерфейса?

Зависит от софта, но pppd не будет сразу выходить и подождет какой-то таймаут, ЕМНИП.

2. Если моё решение не очень, то есть какие идеи как можно сделать? Хоть куда копать подскажите.

Проверять текущий дефолтный маршрут, потом менять его при необходимости, например

#!/bin/bash

while true; do
	if ping -I $DEF_INTERFACE -c5 8.8.8.8 | grep "100% packet loss" > /dev/null; then
		if ip route get 8.8.8.8 | grep ppp0; then
			ip route replace default via $GW2
		fi
	elif ip route get 8.8.8.8 | grep eth1; then
		ip route replace default dev ppp0
	fi
done

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

Короче, вот наваял по быстрому, не знаю, как переключает, но по крайней мере тестирует нормально :)

#!/bin/bash

GW1=0.0.0.0
DEF_INTERFACE=ppp0
GW2=192.168.1.1
SECOND_INTERFACE=eth1

while true; do
        P=$(ifconfig $DEF_INTERFACE 2> /dev/null | grep -o 'destination [0-9][0-9.]*')
        P=${P#destination }
        if [[ -n $P ]]; then
                P=$(ping -I $DEF_INTERFACE -c5 $P 2> /dev/null)
                if [[ -n $P ]]; then
                        grep -q "100% packet loss" <<< "$P" && P=
                fi
        fi
        if [[ -z $P ]]; then
                echo Can not ping from $GW1
                if ! route -n | grep -q $GW2 ; then
                        echo Change default gw to $GW2
                        ip route del default via $GW1 dev $DEF_INTERFACE
                        ip route add default via $GW2 dev $SECOND_INTERFACE
                fi
        elif route -n | grep -q $GW2 ; then
                echo Change default gw to $GW1
                ip route del default via $GW2 dev $SECOND_INTERFACE
                ip route add default via $GW1 dev $DEF_INTERFACE
        fi
done

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

Проверять текущий дефолтный маршрут

У него тогда вообще ничего работать не будет :))

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

У вас не bash? Я что-то не соображу как тут настоящий bash может выругаться...

Точно, на ash такое сообщение. Предупреждать надо, у вас в вашем срипте стоит bash

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

Скрипт отрабатывает как надо.

На самом деле у таких скриптов есть недостаток: у провайдера может тоже канал отказать. Потому надо иметь какой-то хост в Интернете, на который можно проверять всегда только с основного канала с выставленным роутом на него как только появился основной канал. То есть работоспособность этого адреса вам на самом деле через запасной канал не нужна совсем. Потому гугл тут не подойдёт.

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

Пинговать 2-3 разных адреса и в случае если они все не отвечают - переключать каналы у себя?

Тут логика не простая. Несколько адресов никак не спасут. Вам надо оставаясь на запасном канале (в том по причине переключения от результатов тестирования специального хоста в Инете) умудриться протестировать через основной этот тестовый хост. Проблема в том, что если хост будет не доступен, то вы оторвёте доступ на него через запасной как минимум в момент тестирования.

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

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

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

«Балансировка нагрузки» магию не совершит. После переключения на резервный канал как проверить, что основной ожил? Только один способ — включить его и проверить. В этот момент, если на основном канале всё так же нет линка, интернета не будет вообще. Это может быть недолго, но будет.

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

Если отвалится один, то второй будет работать без всякого переключения.

Балансировка без изменения IP адреса у ваших соединений возможна только с договорённостью у одного провайдера или при наличии PI AS и прочих BGP :)

Но для простоты я применяю способ попроще: выбираем перектрёстно: на основном проверяем адрес маршрутизатора запасного и наоборот. Но тут уже автомат не получится, надо прописать эти адреса и следить, что они так и продолжают провайдерами использоваться в качестве инфрастуктурных маршрутизаторов с доступностью по ping.

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

Для дома и так жир два канала иметь, так что пока посижу на скрипте с простым «переключением». :)

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

Увы и ах, но после продолжительного разрыва ppp интерфейс удаляется. Можно ли как-то сохранить его бесконечно долго? Опция ppp persist не помогает.

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

Увы и ах, но после продолжительного разрыва ppp интерфейс удаляется.

Естественно.

Можно ли как-то сохранить его бесконечно долго?

Это принципиально не правильно. У вас на интерфейсе есть IP адрес, а он больше недоступен.

Проблема то в чём? Скрипт тот по идее должен среагировать на отсуствие интерфейса как на отсуствие ping-а (мой так и вообще ping не запускает). Или у вас потом меняется имя интерфейса с ppp0 на ppp1?

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

Проблема в том, что когда после удаления интерфейса (ppp0) он снова доступен, скрипт не может добавить роут на него т.к просто нет такого интерфейса.

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

Смотрите:

Я проверяю скрипт путём отключения медиаконвертера (ppp0). Скрипт спокойно переключает локалку на второй канал (eth1). Я выжидаю минут 10 и снова включаю конвертер. Скрипт должен бы настроить снова роуты на ppp0, но т.к такого интерфейса больше нет сама команда начинает ругаться «No such device».

Только сейчас подумал - а как вообще, если интерфейса ppp больше нет, скрипт понимает что он «есть» и пытается его поднять?

Понятно объяснил?

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

Какая команда у вас ругается? У меня стоит «ifconfig $DEF_INTERFACE 2> /dev/null» Если интерфейса нет, то переменная пуста и ping не вызывается.

Только сейчас подумал - а как вообще, если интерфейса ppp больше нет, скрипт понимает что он «есть» и пытается его поднять?

Какой скрипт? Вы про переключалку? Она ничего не подымает. Интерфейс подымает у вас скорее всего демон ppp, в режиме pppoe, если я угадал.

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

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

Всё нормально.

FluffyPillow ()
3 марта 2018 г.
Ответ на: комментарий от vodz

Я апну мертвую тему и спрошу: почему нельзя пинговать тот же гугл? Мы же в ping указываем интерфейс через который пинговать.

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

Если вы сделали не переключение default, а source-routing, то тогда вам надо будет не такой простой скрипт, а скрипт, который будет менять src у всех программ, которые захотят пользоваться другим каналом.

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

Я опять туплю.

Зачем менять у всех программ src если нам надо просто узнать пингуется ли 8.8.8.8 через дефолтный интерфейс и если нет, то переписать роут на резервный канал? С этим же справится указание нужного интерфейса для команды ping. Нет?

Ваш скрипт прекрасно работает, но иногда у провайдера отваливается сеть, хотя их gw прекрасно пингуется и соответственно скрипт не срабатывает. Вот и хотелось бы добавить после пинга gw (при его успехе) пинг 8.8.8.8.

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

С этим же справится указание нужного интерфейса для команды ping. Нет?

Указание интерфейса — это с какого интерфейса создавать пакеты, то бишь src IP. В какой интерфейс эти пакеты попадут — определяются рутингом, либо по умолчанию по dst-IP либо при указании рутинга по src-IP. Если исходящий интерфейс не совпадает с указанным исходным, то само ядро ещё должно пророутить (forward) внутри себя. Но роут будет всегда, даже опция в IP, которая отключает рутинг просит делать это уже вне хоста, потому на это обычно все не соглашаются и игнорируют эту опцию.

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

Теперь понял. И как Вы раньше говорили - мне нужен определенный хост в Интернете на который будет роут только с основного канала и который будет доступен только с основного канала?

Как-то так?

ip route add xxx.xxx.xxx.xxx dev ppp0

А на хосте в Интернете правилом iptables режем все подключения не от IPшника основного канала.

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

А на хосте в Интернете правилом iptables режем все подключения не от IPшника основного канала

Хм, а зачем?

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

Так такой вариант будет работать?

Ваш вариант — просто дополненный странными условиями к исходному алгоритму. Нет необходимости иметь хост, доступный только по одному каналу.

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

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

Ваше же сообщение.

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

Ваше же сообщение.

Там всё правильно написано. Так как адрес только для теста поднятия соседнего канала, то вы сами себе отрубите доступ через другой канал, но не фильтрами, а рутингом. Фильтры совсем не при чём.

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

Ну я уже прям не знаю, как тут ещё можно повернуть, что может быть не понятно. Ну да, именно так. Там же всего две простые мысли: 1) если вам надо проверять, что не только канал до провайдера поднялся и не только до ближайшего маршрутизатора, то проверять надо на каком-то удаленном от провайдера хосту; 2) этот хост желательно не переключать туда сюда, ни к чему хорошему это всё равно не приведёт, а сделать доступным только по основному каналу для его проверки .

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

Да всё понятно давно. Я просто имею некоторые трудности с написанием того что думаю :)

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

Я конечно Вас задолбал по этой теме, но пожалуйста, посмотрите - немного переделал ваш скрипт:

              internal_checking_ip=$(ifconfig $DEF_INTERFACE 2> /dev/null | grep -o 'destination [0-9][0-9.]*')
              checking_ip=${internal_checking_ip#destination }
              if [[ -n "$checking_ip" ]]; then
                             if checking_ip=$(ping -I $DEF_INTERFACE -c3 $checking_ip 2> /dev/null) ; then
                                  checking_ip=$(ping -I $DEF_INTERFACE -c10 $EXTERNAL_CHECKING_IP 2> /dev/null)
                             fi
                        if [[ -n "$checking_ip" ]]; then
                                  echo "$checking_ip"  | grep -q "100% packet loss" && checking_ip=
                                  echo "$checking_ip"  | grep -q "Operation not permitted" && checking_ip=
                        fi
              fi

Теперь если шлюз дефолтного канала пингуется успешно, то выполняется ещё пинг внешнего адреса на который есть роут у дефолтного канала. Фаерволлом на моём роутере так же запрещен пинг этого адреса НЕ с интерфейса дефолтного канала, т.к спустя какое-то время дефолтный интерфейс отваливается и пинг шлётся через резервный канал постоянно.

И если скрипт ловит на пинге gw и/или внешнего хоста полную потерю пакетов или «operation non permitted» (от фаерволла), то он переключает канал на резервный. Как только пинг на gw и/или внешний хост появляется, то он переключает систему обратно.

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

но пожалуйста, посмотрите

Посмотрел. Не понятно, как у вас осуществляется «т.к спустя какое-то время дефолтный интерфейс отваливается и пинг шлётся через резервный канал постоянно», если всегда проверка идёт от -I $DEF_INTERFACE, а первое условие вообще было написано, чтобы не проверять ping, если интерфейса нет.

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

Вот полный, допиленный до максимума, скриптец:

Теперь сначала мы получаем IP шлюза дефолтного канала, и если находим, то дальше получаем второй хоп за шлюзом дефолтного канала и пингуем его, потом пингуем внешний хост, и если на этих двух пингах всё нормально и дефолтный канал не «забракован», то запускается пинг проверки качества дефолтного канала на потери (50 пакетов) и если не дошло больше 10% пакетов - дефолтный канал бракуется.

И если дефолтный канал «бракованный», то идёт переключение на запасной.

#!/bin/bash
if [ $# -lt 5 ]; then exit; fi

GW1=$1 # first ISP IP for routes
GW2=$2 # second ISP IP for routes
DEF_INTERFACE=$3 # first ISP interface
FAILOVER_INTERFACE=$4 # second ISP interface
EXTERNAL_CHECKING_IP=$5 # address for external checking
NOW_GW_FILEINFO="/etc/network/failover.info" # file for display current active gw

if [ ! -f $NOW_GW_FILEINFO ]; then touch $NOW_GW_FILEINFO; fi

while true; do
              internal_checking_ip=$(ifconfig $DEF_INTERFACE 2> /dev/null | grep -o 'destination [0-9][0-9.]*') # get default gw for main ISP
              checking_ip=${internal_checking_ip#destination } # get clean gw IP for main ISP
              if [[ -n "$checking_ip" ]]; then # if gw IP is exist, than ping first second hop after this gw and after ping external IP
                             secondhopip=$(traceroute -i $DEF_INTERFACE  $EXTERNAL_CHECKING_IP | head -n 3 | awk '(NR == 3)' | awk '{print $2}' | grep -o 'destination [0-9][0-9.]*')
                             if checking_ip=$(ping -q -n -I $DEF_INTERFACE -c3 $secondhopip 2> /dev/null) ; then
                                  checking_ip=$(ping -q -n -I $DEF_INTERFACE -c50 $EXTERNAL_CHECKING_IP 2> /dev/null)
                             fi
                        if [[ -n "$checking_ip" ]]; then
                                  echo "$checking_ip"  | grep -q "100% packet loss" && checking_ip= # if any of ping return loss or denied too - clean var
                                  echo "$checking_ip"  | grep -q "Operation not permitted" && checking_ip=
                                  if [[ -n "$checking_ip" ]]; then # if var not clean, ping external host for lost % and if loss % is too high - clean var
                                    checking_ip=$(ping -q -n -I $DEF_INTERFACE -c50 $EXTERNAL_CHECKING_IP | tail -2 | awk '{print $6}' | awk '(NR == 1)' | cut -d '%' -f1 2> /dev/null)
                                    if [ "$checking_ip" -lt "90" ]; then checking_ip= ; fi # if ping return packets lower than 90% - clean var
                                  fi
                        fi
              fi

              if [[ -z "$checking_ip" ]]; then # if var was cleaned - switch ISP
                       if ! route -n | grep -q $GW2 ; then
                                  echo -e "Now GW is: $GW2\n$FAILOVER_INTERFACE" > $NOW_GW_FILEINFO
                                  ip route replace default via $GW2 dev $FAILOVER_INTERFACE

                       fi
                 elif route -n | grep -q $GW2 ; then
                                  echo -e "Now GW is: $GW1\n$DEF_INTERFACE" > $NOW_GW_FILEINFO
                                  ip route replace default via $GW1 dev $DEF_INTERFACE
                               
              fi

done

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

Пора уже задуматься, что этот скрипт при отсутствии вызва ping-а будет жрать 100% CPU. Какой смысл пинговать адрес, полученный traceroute? Он по определению был доступен на момент traceroute. И кто вам обещал, что именно на 3-й строке хоп у всех будет доступен traceroute? Конструкции:

tail -2 | awk '{print $6}' | awk '(NR == 1)' | cut -d '%' -f1
чудовищны! Если вы вызываете мощный и тормозной обработчик текстов как awk, то и делайте всё им, в одном скрипте awk!

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

И кстати да, отъедает 10% от g4600.

Когда интерфейса нет, оно даже не доходит до awk.

Это такие операции с awk дорогие?

И это тоже. У вас там много чего дорогого.

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

Когда интерфейса нет, оно даже не доходит до awk.

Так а что отъедает столько ресурсов когда нет интерфейса? Тогда же минуются все дорогие куски кода.

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