Pull to refresh

Автоконфигурация сетевых интерфейсов в Debian GNU/Linux

Reading time 6 min
Views 18K
Решил рассказать о вариантах решения достаточно часто встречающейся проблемы для обладателей мобильных устройств. Проблема заключается в том, что зачастую лаптопы подключаются к достаточно большому количеству разных сетей, в которых далеко не всегда есть DHCP-сервер, либо же DHCP-сервер «отдаёт» не все необходимые настройки, либо же отдаёт неверные.

Автоподнятие интерфейса


Автоподнятие интерфейса — вещь достаточно удобная, так как избавляет от необходимости каждый раз вручную поднимать и опускать интерфейс (хотя я знаю людей, которым больше по душе именно ручное поднятие интерфейсов). В большинстве случаев для этих целей можно использовать замечательную программу ifplugd.

Настройка ifplugd не представляет особой сложности сама по себе, но как минимум в Debian есть возможность опции указать в файле /etc/default/ifplugd. Стоит обратить внимание на опцию -d — время между определением отключения среды передачи данных (кабеля) и деконфигурацией интерфейса, возможно, имеет смысл его увеличить, так как весьма неприятно, когда из-за случайно выдернутого кабеля разрываются соединения. Опцию -u же, напротив, можно установить в небольшое значение — поднять интерфейс при появлении кабеля практически никогда не вредно.

Теоретически, ifplugd умеет работать и с отключаемыми адаптерами (параметр HOTPLUG_INTERFACES в конфигурационном файле), на практике же мне не удалось заставить его работать, потому это было сделано через udev. Всего у меня использовалось три Wi-Fi-адаптера, один из них подключался как PC Card (PCMCIA), другой — через USB, а позже появился встроенный, подключаемый через MiniPCI. Соответственно, в /etc/udev/rules.d/80-LOCAL-wlan-start.rules постепенно дописывались правила:
# WLAN adapters
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", KERNEL=="wlan*", RUN+="/etc/network/wlan-up $env{INTERFACE}"

SUBSYSTEM=="rfkill", ACTION=="change", ENV{RFKILL_STATE}=="1", ENV{RFKILL_NAME}=="phy0", ENV{RFKILL_TYPE}=="wlan", RUN+="/etc/network/wlan-up wlan2"
SUBSYSTEM=="rfkill", ACTION=="change", ENV{RFKILL_STATE}=="0", ENV{RFKILL_NAME}=="phy0", ENV{RFKILL_TYPE}=="wlan", RUN+="/sbin/ifdown wlan2"
SUBSYSTEM=="rfkill", ACTION=="change", ENV{RFKILL_STATE}=="2", ENV{RFKILL_NAME}=="phy0", ENV{RFKILL_TYPE}=="wlan", RUN+="/sbin/ifdown wlan2"

В этом файле первая после комментария строка отрабатывает при подключении внешнего адаптера. Остальные же строки отрабатывают при получении события rfkill, которое означает изменение состояния переключателя встроенного Wi-Fi. Значения параметров здесь указаны для драйвера b43, у других драйверов они могут отличаться. К сожалению, так и не удалось придумать, как не указывать при этом имя интерфейса вручную.
Содержимое файла /etc/network/wlan-up:
#!/bin/sh

export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

if=$1

logger -s -t wlan "Bringing up $if"

ifconfig $if up

poweroff=$(iwconfig $if|grep Tx-Power=off|wc -l)

if [ $poweroff -ne 0 ] ; then
        ifconfig $if down
        exit
fi

ifup --force $if 2>&1 | logger -s -t wlan

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

Файл /etc/network/wlan-up изначально представлял собой достаточно сложный bash-скрипт, производивший поиск сетей, а также предпринимавший попытки входа в них, но был значительно упрощён после того, как я случайно наткнулся на пакет guessnet, о котором речь пойдёт ниже.

Автоконфигурация сети


После того, как интерфейс, собственно, поднят, возникают две проблемы. Первая заключается в том, чтобы в случае Wi-Fi определить, какая из знакомых нам сетей доступна. Вторая заключается в том, чтобы непосредственно эту сеть настроить, если по какой-либо причине настройка по протоколу DHCP невозможна.

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

guessnet пользуется такой возможностью механизма настройки сети в Debian /etc/network/interfaces, как логические интерфейсы и ключевое слово mapping. Логические интерфейсы, как подсказывает их название, представляют собой ни что иное, как профили настроек сети, которые можно применить при поднятии того или иного интерфейса примерно так: ifup eth0=home. Ключевое слово mapping представляет возможность написать скрипт, который осуществляет сопоставление физического интерфейса логическому.

Всё, что необходимо, чтобы подключить guessnet в /etc/network/interfaces — добавить секцию (stanza) mapping для соответствующего интерфейса:
mapping eth1
        script guessnet-ifupdown
        # map default: dhcp
        # map debug: true
        # map verbose: true
        map syslog: true

Ключевое слово map приобретает новый смысл: оно используется для передачи параметров в guessnet (про оригинальный смысл можно прочитать на странице interfaces (5) или в документации на ifupdown). Но об этом позже.

Сами профили определяются в точности так же, как и любые другие интерфейсы, с одной лишь только разницей: имя логического интерфейса не совпадает с именем физического (хотя может содержать его как подстроку в себе):
iface no-link inet manual
        test missing-cable
        pre-up echo No link present.
        pre-up false
        
iface dhcp inet dhcp

iface eth-home inet dhcp
        post-up route del default 2>&1 >/dev/null || true
        post-up pon dsl-eth-vpn
        post-up ifup ipv6-vps
        pre-down ifdown ipv6-vps
        pre-down poff dsl-eth-vpn
        test peer address 192.168.1.1 mac 00:19:CB:48:02:2A source 192.168.1.5

iface eth-lab inet static
        address 192.168.23.238
        netmask 255.255.255.224
        gateway 192.168.23.225
        test peer address 192.168.23.225

Как видно, в настройках каждого из приведённых логических интерфейсов присутствует ключевое слово test. Очень гибкая архитектура программы ifupdown позволяет сторонним программам (таким, как guessnet) перехватить их обработку либо просто игнорировать их (хотя конкретно guessnet работает иначе — он просто разбирает файл заново). Слово test позволяет задать условие, по которому будет выбрана та или иная конфигурация.

Логический интерфейс no-link рекомендуется создавать специально для того, чтобы guessnet не пытался запускать другие тесты при отсутствии кабеля.

Конфигурация dhcp принимает все настройки по DHCP, не заменяя ничего. Её бывает полезно установить как конфигурацию по умолчанию — большинство сетей всё же имеют хоть какой-нибудь DHCP сервер, слушая указания которого можно хотя бы получить базовый набор настроек.

Конфигурация eth-lab используется в сети с DHCP-сервером, который игнорирует запросы от незнакомых ему клиентов. Потому делается arping шлюза в данной сети — такой запрос отработает только в случае, если оба узла находятся в одном физическом сегменте сети.

Наконец, конфигурация eth-home одновременно использует настройки от DHCP-сервера, но дополнительно поднимает IPv6-туннель, а также удаляет IPv4-шлюз по умолчанию, фактически превращая машину в IPv6-only узел. Ещё одна особенность: для того, чтобы получить доступ к «другому концу» IPv6-туннеля, используется ADSL-соединение, которое любезно предоставляет модем Zyxel серии P-660. У модемов этой марки есть одна интересная прихоть: они игнорируют анонимные ARP-запросы (а именно такие рассылает по умолчанию guessnet). Чтобы избежать этого, в явном виде прописан адрес, который записывается в поле «источник» пакета. Кроме того, здесь в явной форме указан MAC-адрес модема.

Кроме этого, как я уже упоминал, guessnet умеет обнаруживать Wi-Fi-сети. Для этого ключевое слово test поддерживает опцию wireless:
iface wifi-MTS.BY inet dhcp
        test wireless essid MTS.BY
        wireless-essid MTS.BY
        wireless-key off

Необходимо учесть, что достаточно часто guessnet не может корректно определить «наличие кабеля» для Wi-Fi (оно и понятно), из-за чего в mapping для Wi-Fi-интерфейса нужно добавлять строку map !no-link.

Более подробно с возможностями guessnet можно ознакомиться на странице guessnet(8). Также к пакету прилагается неплохая документация с примерами использования. В принципе, ничто не мешает использовать guessnet и в других дистрибутивах — существует режим работы, «отвязанный» от специфичного для Debian пакета ifupdown.

Пара штрихов


Одна из сетей, в которых мне довольно часто приходится бывать, имеет DHCP-сервер, настроенный так, что запросы на обновление адреса отправляются два раза в минуту. Вместе с тем, через DHCP раздаются не подходящие мне настройки DNS и раз в полминуты перезаписывают мой /etc/resolv.conf. Решением такой проблемы может быть следующий скрипт (для dhclient3):

case $reason in
RENEW)
        make_resolv_conf () {
                true
        }
        ;;
*)
        return
        ;;
esac

Поместить его нужно в /etc/dhcp3/dhclient-enter-hooks.d под любым именем.

Конечно, такое решение является довольно грубым «хаком», так что возможно, установка пакета resolvconf и «подкручивание» приоритетов интерфейсов будут более предпочтительными.

Кроме того, рекомендуется поставить пакет ifupdown-extra, в нём есть несколько полезных дополнений к стандартным возможностям ifupdown.

Вот, пожалуй, и всё, что хотелось рассказать. Спасибо за внимание.
Tags:
Hubs:
+63
Comments 32
Comments Comments 32

Articles