Толик Панков
hex_laden
............ .................. ................
Толик Панков [userpic]
Скрипт, для получения информации о выделенных ip и шлюзе и установки маршрутизации.

Преамбула.
Дело в том, что у меня на рабочем компьютере поднимаются сразу несколько VPN-тоннелей. Для работы, сразу с двумя офисами, здесь, в Италии, и до основного, два бесплатных VPN для разных Интернет-сервисов, один из которых должен предоставлять маршрут по умолчанию, местный тоннель (пофлудить на скрытом форуме). Для всего этого зоопарка заведены соответствующие таблицы маршрутизации в файле /etc/iproute2/rt_tables. Понятно, что после того, как тоннели поднялись, в соответствующие таблицы надо прописать соответствующие маршруты по умолчанию. DHCP в данном случае применим с огромным геморроем, маршруты надо прописывать вручную, но это тоже не менее геморройная задача, т.к. провайдеры иногда меняют шлюзы, я меняю провайдеров VPN, админы тоже могут изменить шлюз и/или выделенный IP. В общем, задолбало меня все руками перенастравивать и я решил написать скрипт, который это будет делать за меня. Заодно вспомнить bash, где знал, и узнать что-то новое, где не знал.
Будем исходить из того, что имеется уже поднятый VPN-тоннель и нам необходимо прописать в нужную таблицу нужный маршрут по-умолчанию.

Откуда взять нужные ip.
Из вывода команды ifconfig. В моей системе вывод ifconfig pppX, где X, номер соответствующего интерфейса ppp выглядит так:
ppp1: flags=4305 <UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1396
     inet x.x.x.x netmask 255.255.255.255 destination y.y.y.y
     ppp txqueuelen 3 (Point-to-Point Protocol)
     RX packets 2055 bytes 1502924 (1.4 MiB)
     RX errors 0 dropped 0 overruns 0 frame 0
     TX packets 1747 bytes 179542 (175.3 KiB)
     TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0


Где x.x.x.x – IP, присвоенный машине провайдером VPN, а y.y.y.y – IP шлюза провайдера, через который и должен пролегать маршрут.

Команды для установки маршрутизации
Команда добавления маршрута в соответствующую таблицу выглядит примерно так:
ip route add default dev имя_тоннеля via адрес_шлюза src адрес_машины table таблица
Например:
ip route add default dev ppp1 via 10.1.15.1 src 10.1.22.12 table suomi_office
Если таблица не указана (параметр table отсутствует), то маршрут будет установлен, как маршрут по умолчанию.
Обязательными параметрами в данном случае является указание на соответствующий шлюз (dev), остальные – дополнительные.

Для удаления маршрута по умолчанию используется команда:
ip route del default table таблица
или
ip route del default для удаления основного маршрута по-умолчанию.

Что должен делать скрипт
Основное:
1. Проверить, действительно ли поднят указанный интерфейс
2. Есть ли в файле /etc/iproute2/rt_tables соответствующая таблица маршрутизации, если таблица не указана – установить основной маршрут по умолчанию
3. Получить нужные IP
4. Вывести на экран summary – полученные IP и параметры.
5. Установить маршрутизацию, сообщив об успехе, либо ошибке

Дополнительно:
1. Проверить корректность заданных параметров
2. Вывести справку по собственным параметрам при наличии запроса, либо при запуске без параметров.
3. Иметь возможность указать или не указать необязательные параметры для команды маршрутизации.

Помощь
Напишем функцию, выводящую справку по параметрам скрипта:
print_help () {
     echo "use setroute [OPTIONS] [table]";
     echo " pptp tunnel interface (e.g. ppp0)";
     echo "Options:";
     echo "v - set VIA parameter";
     echo "s - set SRC parameter";
     echo "t - set routing table";
     echo "[table] - routing table (in /etc/iproute2/rt_tables)";
     echo "if the table is not specified, used default route table";
}


Проверка первого параметра и запроса помощи.
Если скрипт запущен без параметров, необходимо выдать справку по параметрам и завершить работу:
if [ -z "$1" ]; then #Если 1 опции нет
     print_help #Печатаем справку и уходим
     exit 2
fi


Если первый параметр -h или --help, поступить аналогично

if [ "$1" = "-h" ] ;then # Если в 1 опции запрос помощи
     print_help # Печатаем справку и уходим
     exit 2
fi


if [ "$1" = "--help" ] ;then # Если в 1 опции запрос помощи
     print_help # Печатаем справку и уходим
     exit 2
fi


Установка флагов и переменной для имени таблицы маршрутизации
SETVIA=0 #Флаг, указывающий, надо ли устанавливать параметр via
SETSRC=0 #Флаг, указывающий, надо ли устанавливать параметр src
ROUTINGTABLE="" #Имя таблицы маршрутизации


Проверка остальных параметров
if echo "$2"| grep -q "v"; then #Устанавливать ли via
     SETVIA=1
fi


Команда echo "$2" отправит второй параметр скрипта на STDOUT, а команда grep считает содержимое параметра и выдаст код завершения 0, если в нем присутствует символ “v”, что команда if в свою очередь примет, как TRUE, и установит соответствующий флаг в 1

Аналогично и для установки параметра src
if echo "$2"| grep -q "s"; then #Устанавливать ли src
     SETSRC=1
fi


Установка таблицы маршрутизации и проверка ее наличия в /etc/iproute2/rt_tables
if echo "$2"| grep -q "t"; then #Устанавливать ли таблицу
     if [ -n $3 ]; then #Если да, то проверить, заполнен ли третий параметр
           if grep -wq "$3" "/etc/iproute2/rt_tables"; then #Если заполнен, то проверить, есть ли она
                 ROUTINGTABLE=$3
           else
                 echo "Error! Table $3 not found in rt_tables"
                 exit 1
           fi
     fi
fi


Для этого необходимо проверить, надо ли устанавливать таблицу таким же способом, как и предыдущие параметры. Если надо (во втором параметре скрипта присутствует символ “t”), то проверить, заполнен ли третий параметр (имя таблицы). Третий параметр не должен быть пустым (-n во вложенном условии). Если он не пуст, то необходимо проверить, есть ли указанная таблица в файле /etc/iproute2/rt_tables, для чего в следующем вложенном условии выполняется команда grep -wq "$3" "/etc/iproute2/rt_tables где
w – искать слово целиком
q – не выводить найденные строчки на STDOUT
$3 – третий параметр скрипта
И далее указание файла /etc/iproute2/rt_tables

Если таблица найдена, ее имя присваивается переменной ROUTINGTABLE, а если не найдена, скрипт выводит соответствующее сообщение об ошибке и завершает работу.

Проверка готовности интерфейса
#Проверяем, поднят ли интерфейс
ifconfig $1>/dev/null
if [ "$?" -ne 0 ]; then
     echo "Interface $1 error ";
     exit 1
fi


Для того, чтобы проверить, поднялся ли наш ppp-тоннель достаточно вызвать команду ifconfig, которой передано в качестве единственного параметра имя интересующего нас интерфейса (в данном случае, он передан в первом параметре скрипта т.е. в переменной $1). Если интерфейс поднят – команда ifconfig завершится с кодом возврата 0, который наш скрипт может получить из системной переменной $?. Если код возврата не равен 0 (-ne) скрипт выводит ошибку и завершает работу.

Извлечение IP-адресов.
Необходимые нам адреса находятся в строчке
inet x.x.x.x netmask 255.255.255.255 destination y.y.y.y
которую выведет команда ifconfig pppX (X – номер интересующего нас тоннеля)

Отgrep’аем ее командой
ifconfig $1|grep "destination"
(grep найдет нам искомую строчку, которая содержит слово "destination") и запишем результат в переменную TMPGREP, используя конструкцию `` (обратные одинарные кавычки).
TMPGREP=`ifconfig $1|grep "destination"`;#Grep'аем 'destination' (на той же строчке и 'inet')

Проверим, получили ли мы строчку с нужными данными:
if [ -z "$TMPGREP" ]; then
     echo "Error! IP data not found!"
     exit 1
fi

Если полученная строка пуста (-z), значит произошла ошибка. Завершаем работу скрипта.

Далее используем утилиту awk, чтобы извлечь непосредственно IP-адреса и записать их в соответствующие переменные.
SRC=`echo $TMPGREP|awk ' {print $2} '` # С помощью awk извлекаем нужные
VIA=`echo $TMPGREP|awk ' {print $6} '` # столбцы с IP (разделитель пробел)


Утилита awk воспринимает переданные (по умолчанию с STDIN) на обработку данные, как набор значений ("колонок", как например в файле CSV) разделенных разделителем (по умолчанию пробелом). Соответственно нам в данном случае надо взять вторую ($2) и шестую ($6). Здесь после знака $ указаны не параметры скрипта, а номера "колонок".

Формирование команды для установки маршрута и вывод summary.
После всего вышеизложенного, дальнейший код должен быть понятен. Проверяем установленные ранее флаги, в случае необходимости добавляем нужные ключевые слова.

# Выводим summary и формируем элементы команды
echo "Device: $1 OK"
if [ $SETSRC -eq 1 ]; then
     echo "SRC set. IP: $SRC"
     SRC="src "$SRC
else
     echo "SRC not set"
     SRC=""
fi

if [ $SETVIA -eq 1 ]; then
     echo "VIA set. IP: $VIA"
     VIA="via "$VIA
else
     echo "VIA not set"
     VIA=""
fi

if [ -z $ROUTINGTABLE ]; then
     echo "Use default routing table."
else
     echo "Routing table: "$ROUTINGTABLE
     ROUTINGTABLE="table "$ROUTINGTABLE
fi


Выполняем команду установки маршрута и проверяем результат
#Устанавливаем роутинг.
ip route del default $ROUTINGTABLE
ip route add default dev $1 $VIA $SRC $ROUTINGTABLE
if [ $? -eq 0 ]; then
     echo "Routing set!"
else
     echo "Error detected!"
fi


Приложение. Используемая литература и скрипт целиком.
1. Краткое описание команды if. Копия
2. Краткое описание команды awk Копия
3. Скрипт. Копия на pastebin.

Comments

Если уж ты используешь iproute2 для установки маршрутов, то, имхо, логично было бы использовать его же и для получения ip-адресов, вместо немного устаревшего ifconfig.

Подскажи, если не сложно, как это сделать с помощью iproute? Я многого не знаю и только учусь.

Да там почти то же самое.

% ip a show dev tun0
26: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 100
    link/none 
    inet 10.8.1.10 peer 10.8.1.9/32 scope global tun0
       valid_lft forever preferred_lft forever

Ну а далее grep и так далее.

Собственно, весь зоопарк из ifconfig, route и так далее заменён на ip (addr|route|link). Вообще это всё мелочи, в большинстве дистрибутивов по-умолчанию есть как ifconfig, так и ip, так что можешь ничего не переделывать.

Благодарю. Все равно, хотелось бы унифицировать (уж лучше одна команда/утилита, чем сто разных), а потом я подумал, что есть что-то типа ip get gateway или ip get чтототам, чтоб сразу выдало ip шлюза и присвоенный, без лишнего grep (чего я не знаю). Посмотрел в содержание мануала, не увидел и переспросил.

О, проверил у себя. Заработало. Но присвоенный ip (для параметра src), там выдрать сложнее, awk уже выдаст лишнее, придется отрезать /32 или составлять регулярку. из ifconfig выдрать проще. Так что действительно, оставлю так.

(Anonymous)

так будет лучше:
# Если в 1 опции запрос помощи
case "$1" in
-h|--help)
print_help # Печатаем справку и уходим
exit 2
;;
esac

>Будем исходить из того, что имеется уже поднятый VPN-тоннель и нам
>необходимо прописать в нужную таблицу нужный маршрут по-умолчанию.
>
>Откуда взять нужные ip.
>Из вывода команды ifconfig. В моей системе вывод ifconfig pppX, где X,
>номер

pppd при подключении выполняет скрипты /etc/ppp/{ip-pre-up,ip-up,ip-down}. обычная практика - выполнять из этих скриптов команды в момент, когда ppp соединение установлено или разорвано. pppd сообщает туда в виде параметров практически все, что нужно знать о поднятом соединении - оба ip адреса туннеля, имя интерфейса, название подключения и т.д.