Определение момента, когда поднялся VPN тоннель.

Версия 2. Исправленная и дополненная.

Преамбула.

Продолжаю начатое тут.
Обычно тоннель поднимается командой pppd call providername где providername – файл настроек соединения в /etc/ppp/peers. Команда особого интерактива не дает, а хотелось бы, например, чтоб выводился «прогресс-бар», когда соединение устанавливается, а по прошествии максимального времени ожидания, если соединение так и не поднялось, выводилось сообщение о таймауте. Если соединение поднялось до истечения таймаута – выводилось сообщение об успехе.
Скрипту будет передаваться единственный параметр – имя необходимого файла настроек соединения из /etc/ppp/peers
Что должен делать скрипт.

1. Проверить собственные параметры, и если файл настроек не указан – выдать соответствующее сообщение и прекратить работу.
2. Проверить наличие переданного файла настроек
3. Проверить, не поднято ли уже это соединение, если поднято – выдать сообщение и прекратить работу
4. Проверить параметр настройки unit – номер будущего интерфейса ppp. Если его нет – найти первый свободный (его и использует pppd). Если параметр unit присутствует, то проверить, свободен ли он, если нет – найти первый свободный.
5. Запустить установку соединения.
6. В цикле проверять, установилось ли оно, или истек заданный таймаут
7. По завершению цикла оповестить пользователя об успехе или истечении таймаута.
Недочет предыдущей версии скрипта.

Главный недочет предыдущей версии скрипта — жестко заданная проверка наличия соединения командой ping, после получения сведений об установленном соединении из вывода команды ifconfig. Пинговался адрес полученного шлюза. Дело в том, что некоторые провайдеры не позволяют пинговать шлюз, или вообще отключают возможность использования протокола ICMP, поэтому необходимо дать пользователю возможность через параметры командной строки указать, нужно ли использовать дополнительную проверку с помощью ping и какой адрес в случае необходимости пинговать.
Заодно мною были добавлены дополнительные информационные сообщения, а также немного видоизменен основной рабочий цикл.

Задаем переменные

WTIMEOUT=120 #Максимальное время ожидания
PEERFILE="/etc/ppp/peers/" # имя файла с настройками ppp-соед-я
ADDR="" #адрес, который нужно пинговать
PING=0 #флаг, показывающий включена ли проверка ping


Проверка параметров

#Проверяем параметры
if [ -z "$1" ]; then
echo "USE myconn  [-p] [address]";
echo "Options:";
echo " - file in /etc/ppp/peers";
echo "[-p] - use ping check";
echo "[address] - address to ping, default use gateway address";
exit 2;
fi

Если первый параметр пуст (-z), то выводим сообщения об используемых скриптом параметрах и завершаем работу с кодом завершения 2 (exit 2).
Если параметры заданы выводим пользователю сообщение о том, какое соединение будет использовано:
echo "Connecting to "$1;

Далее проверяем, необходима ли проверка соединения с помощью ping, и задан ли адрес, который нужно пинговать. Попутно выдаем пользователю соответствующие сообщения:

if [ "$2" = "-p" ]; then #если второй параметр скрипта="-p"
#будем пинговать
PING=1; #устанавливаем флаг включающий проверку
echo -n "Ping test address..."
if [ -n "$3" ]; then #и проверяем задан ли адрес
ADDR=$3; #если да, сохраняем его в переменную
echo $ADDR;
else #иначе будет использован адрес шлюза
echo "Gateway address.";
fi
else
echo "No ping test"
fi

Проверка существования файла

PEERFILE="$PEERFILE""$1";
#Проверяем, есть ли файл
if [ ! -e "$PEERFILE" ]; then
echo "File "$PEERFILE" not found";
exit 1;
fi


В переменную $PEERFILE где уже записан путь к файлам настроек соединений, добавим первый параметр ($1), переданный скрипту. Если файл не существует (! -e), выводим сообщение об ошибке и завершаем работу.

Проверка, не установлено ли уже соответствующее соединение.

Можно сделать это следующим образом. Когда соединение поднято, pppd висит в памяти, и его вместе с параметрами запуска будет видно в выводе команды ps , чем можно и воспользоваться, скормив вывод ps команде grep. Останется только подсчитать количество найденных grep строк, если их будет больше одной (а одна точно будет, т.к. в вывод команды ps попадет и сама команда grep с ее параметрами), значит pppd уже пытается поднять или поднял указанное соединение и можно завершить работу, выдав соответствующее сообщение.
Строки grep сам и посчитает, достаточно указать ему параметр -c

#Проверяем, не соединено ли уже
#(в списке процессов д.б. 2 строки pppd call NNN, если соединено)
TMPGREP=`ps ax|grep "pppd call $1" -c`
if [ $TMPGREP -gt 1 ]; then
echo "ERROR: $1 already connected"
exit 1;
fi


Поиск первого свободного номера ppp тоннеля.

Для этого достаточно в цикле вызывать команду ifconfig с параметром pppN, где N – проверяемый номер и проверять код возврата ifconfig. Если он 0 (нормальное завершение), такое соединение уже поднято. Если не 0 – информацию о соединении получить не удалось, значит, оно отсутствует. Способ, возможно, кривоват, так что выскажитесь в комментариях, если есть предложить что лучше. Но у меня пока сбоев не было.

#Ищем первый свободный номер для ppp соединения
FIRSTFREE=0;
ifconfig ppp$FIRSTFREE >/dev/null 2>/dev/null
while [ $? -eq 0 ]; do
let "FIRSTFREE = FIRSTFREE+1";
ifconfig ppp$FIRSTFREE >/dev/null 2>/dev/null
done


2>/dev/null перенаправляет вывод из stderr в нуль-устройство, чтоб не загромождать вывод неизбежными сообщениями об ошибках, когда свободный номер будет найден.

Поиск в файле конфигурации соединения параметра unit и извлечение номера соединения.

Найдем параметр unit с помощью grep, и запишем его значение в переменную
PPPUNIT=`grep "unit" $PEERFILE`
Удалим из полученной строки слово unit и пробелы с помощью стандартных средств bash.
PPPUNIT=${PPPUNIT//unit/}
PPPUNIT=${PPPUNIT// /}
Если параметр unit был корректно указан в файле конфигурации соединения, то он будет записан в переменную $PPPUNIT, если нет – переменная $PPPUNIT будет пуста.

Проверка

Теперь нужно проверить, указан ли параметр unit, если не указан, то выдать сообщение о том, что система использует первый свободный, отобразить его и присвоить его значение переменной $PPPUNIT. Если параметр unit указан – проверить, свободен ли он (таким же способом, которым мы искали номер первого свободного, только, естественно, без цикла). Если свободен – вывести на экран сообщение об этом, если занят – предупредить пользователя, что будет использован первый свободный номер и присвоить его значение переменной $PPPUNIT.

if [ -z "$PPPUNIT" ]; then
#Если параметр unit не указан, система использует первый
# свободный при поднятии соединения
echo "Parameter 'unit' not found, system use first free
($FIRSTFREE)";
PPPUNIT=FIRSTFREE;
else
#Если указан, то проверить занят он или нет
ifconfig ppp$PPPUNIT >/dev/null 2>/dev/null
if [ $? –eq 0 ]; then		#занят
echo “WARNING! Unit #$PPPUNIT is already in use, system use
first free ($FIRSTFREE)";
PPPUNIT=FIRSTFREE;
else			#свободен
echo "Unit $PPPUNIT is free. OK."
fi
fi


Установка соединения.

#устанавливаем соединение
pppd call $1

Основной рабочий цикл
Перед циклом заведем две переменную-флаг, показывающую, поднялось ли соединение.
CONNECTED=0 #флаг
И выведем сообщение
echo -n "Wait ("$WTIMEOUT" secounds): "
Параметр -n отключает перенос строки в команде echo, соответственно, следующее сообщение будет выведено на ту же строку.
В цикле командой echo выводим точку, командой sleep 1 устанавливаем секундную задержку, а далее пытаемся получить из вывода команды ifconfig строчку с данными об IP, таким же способом и IP шлюза с помощью команды awk.
Если IP получен, проверяем нужна ли проверка с помощью ping. Если проверка нужна, то проверяем, задан ли IP для ping или нужно пинговать полученный IP шлюза, если IP для IP для ping не задан, устанавливаем его значение равным полученному. Если задан — оставляем таким же, если проверка ping не нужна вообще, устанавливаем в 1 флаг $CONNECTED в 1 и прерываем цикл.
Если проверка ping нужна – пытаемся пропинговать ранее заданный IP. Если пинг успешен (код завершения команды ping будет = 0), то устанавливаем в 1 флаг $CONNECTED. И прерываем цикл.
Если условия не сработали – отнимаем от переменной $WTIMEOUT единицу и уходим на следующую итерацию цикла. Цикл продолжается, пока не достигнуто значение времени ожидания =0.
Важно: в команде ping надо не забыть указать интерфейс, через который будем пинговать. Делается это с помощью ключа -I имя_интерфейса. Можно также выставить количество посылаемых узлу пакетов в 1 с помощью ключа -c
Например: ping -I ppp1 -c 1 10.20.4.4

while [ $WTIMEOUT -ge 0 ]; do
echo -n ".";
sleep 1
#***************************************************
#пытаемся получить строчку с IP шлюза
TMPGREP=`ifconfig ppp$PPPUNIT 2>/dev/null|grep "destination"`
if [ -n "$TMPGREP" ]; then #если строчка есть
TMPGREP=`echo $TMPGREP|awk ' {print $6} '` #извлекаем awk IP шлюза
if [ -n "$TMPGREP" ];then #если он не пустой
if [ $PING -eq 1 ]; then #если надо пинговать
if [ -z "$ADDR" ]; then #если адрес не задан - адрес будет шлюза
ADDR=$TMPGREP
fi
ping -c 1 -I ppp$PPPUNIT $ADDR >/dev/null 2>/dev/null #пингуем
if [ $? -eq 0 ]; then # если получилось
CONNECTED=1 #устанавливаем флаг соед-я
break #Прерываем цикл. Ура!!
fi
else #если пинговать не надо
CONNECTED=1 # устанавливаем флаг соед-я
break #Прерываем цикл. Ура!!
fi
fi
fi
#***************************************************
let "WTIMEOUT=WTIMEOUT-1";
done

После выхода из цикла выводим echo без параметров (чтоб следующие строки выводились с новой), проверяем состояние флага и выводим соответствующее сообщение.

if [ $CONNECTED -eq 0 ]
then
echo "Connection timeout over :("
exit 1
else
echo "Connected sucsesseful!"
exit 0
fi


Скрипт на pastebin
Скачать с mega.nz

Бонусом сделал еще одну версию

Куда прикрутил обратный отсчет времени с мельницей, скрестив этот скрипт с вот этим.
Скрипт на pastebin
Скачать с mega.nz

Это перепост заметки из моего блога на LJ.ROSSIA.ORG
Оригинал находится здесь: http://lj.rossia.org/users/hex_laden/329224.html
Прокомментировать заметку можно по ссылке выше.