11 октября 2014 г.

Автоматический запуск скриптов при подключении NetworkManager

Я очень долго отказывался от использования NetworkManager (далее NM) и жил с традиционным и понятным ifup. Но теперь, пожалуй, пришло время приобщиться к цивилизации. Тем более, что NM стал уже не такой сурово глючный, как раньше, да и что там говорить, удобно на ноутбуке перещёлкиваться между проводом, WiFi и GSM-модемом.

Единственной преградой на пути к благам цивилизации для меня было то, что мне приходится использовать программу VTun для создания туннеля между домом и работой, через который я также хожу в глобальную сеть, а NM не поддерживает VTun из коробки. Конечно, всегда можно наваять пару скриптов, которые будут периодически смотреть поднятые интерфейсы, пинговать различные ip-адреса и заниматься прочими делами, чтобы понять в каком состоянии находится сеть. Но, как оказалось, можно решить эту задачу и более красивым способом с помощью NetworkManager Dispatcher Service. В systemd он обычно имеет имя NetworkManager-dispatcher.service.

NetworkManager Dispatcher Service это сервис, который при изменении состояния NM, дёргает скрипты из каталога /etc/NetworkManager/dispatcher.d и передает в них в качестве параметров командной строки имя сетевого интерфейса, у которого изменилось состояние, и новое значение этого самого состояния - up или down (vpn-up и vpn-down для VPN-соединений). 

Типичный шаблон скрипта в каталоге /etc/NetworkManager/dispatcher.d может выглядеть, например, так:
#!/bin/bash

IF=$1      # имя сетевого интерфейса, с которым связано событие
STATUS=$2  # новое состояние сетевого интерфейса

if [ $IF = "eth0" ]; then
  case $STATUS in
    up)
      # команды выполняемые после установления соединения
      ;;
    down)
      # команды выполняемые после разрыва соединения
      ;;
  esac
fi

Для того, чтобы Dispatcher Service смог запустить ваш скрипт, ему должен быть назначен владелец root и у него должно быть право на выполнение этого скрипта.

Дома я хожу в интернет через роутер, который умеет раздавать этот самый интернет по проводам и по WiFi. Мне хотелось, чтобы у меня в NM было два соединения. Первое соединение обычное, которое выполняет подключение к роутеру и получает от него IP по DHCP, после чего у меня появляется доступ в интернет через провайдера. Второе соединение делает изначально всё тоже самое что и первое, но после подключения к роутеру осуществляет ещё запуск программы VTun для установки туннеля и добавляет несколько статических маршрутов, которые должны направлять пакеты на локальные адреса моего провайдера через реальный интерфейс, а не через туннель. Туннель используется как для доступа к машинам на работе, так и для хождения в интернет через рабочий канал.

Т. к. я хочу иметь два соединения в NM, повешанных на один сетевой интерфейс, и при запуске только одного из этих соединений должен также подниматься туннель, то скрипту необходимо знать имя активного соединения. Активные соединения можно посмотреть командой nmcli c show --active. Эта команда выводит список всех активных соединений в виде таблицы. Но мне нужно проверить активно ли одно конкретное соединение с указанным именем. Это можно сделать, передав выше указанной команде идентификатор интересующего меня соединения: nmcli c show --active id ID_СОЕДИНЕНИЯ. После выполнения этой команды для активного соединения будет выведено множество строк с различными параметрами этого соединения, а если выполнить эту команду для неактивного соединения, то вывод будет пустой. Я, честно говоря, не понял чем отличаются и отличаются ли вообще понятия ID соединения и NAME соединения в NetworkManager, но по моим наблюдениям они всегда совпадают.

Основываясь на вышесказанном, мой скрипт получился таким:

#!/bin/bash

IF=$1
STATUS=$2

VTUND_SCRIPT=/usr/local/etc/vtun/vtund.sh

REQUIRED_IF="eth0"
REQUIRED_CON="vtun"

if [ $IF = $REQUIRED_IF ]; then
  case $STATUS in
    up)
      # посчитать количество строк - если соединение неактивно, то их кол-во = 0
      (( `nmcli c show --active id $REQUIRED_CON | wc -l`  > 0 )) && VTUN_CONNECTION=true || VTUN_CONNECTION=false

      if [ $VTUN_CONNECTION = true ]; then
        $VTUND_SCRIPT &
      fi
      ;;
    down)
        killall vtund.sh
        killall vtund
      ;;
  esac
fi

В скрипте, при поднятии соединения на интерфейсе eth0, сначала проверяем не является ли активным соединение с именем vtun, которое я назначил в настройках NM соединению, отвечающему за поднятие туннеля, и привязал его явно к интерфейсу eth0:


Если на интерфейсе eth0 поднялось соединение с именем vtun, значит можно запускать скрипт для установки туннеля: "$VTUND_SCRIPT" &. Обратите внимание, что для вызова скрипта, запускающего программу vtun, я использую амперсанд, чтобы этот скрипт выполнялся в фоновом режиме. Это очень важный момент, потому что у Dispatcher Service есть таймаут для выполнения каждого скрипта из каталога /etc/NetworkManager/dispatcher.d и если какой-либо скрипт не успевает выполниться за время таймаута, Dispatcher Service просто грохает его. Ещё я заметил, что в некоторых дистрибутивах скрипты Dispatcher Service запускаются с очень бедным окружением, поэтому возможно придется прописывать полные пути до каких-либо программ, используемых в этих скриптах. Но в принципе, это нормально и вообще является правилом хорошего тона для подобных автоматически запускаемых скриптов. При опускании интерфейса eth0 убиваются скрипт vtund.sh и сам демон vtund.

Вот в принципе и всё о чем хотелось написать. Ещё немного почитать о Dispatcher Service можно здесь. Там есть пара примеров и более подробная информация о таймауте.

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

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