1 Modern Linux - network virtualizing 2 Москва май-июль 2006 Алексей Барабанов.
Современный Linux сервер: виртуализация сетевых устройств. Часть 2.
Соединим идеи виртуализации сети с принятыми в SUSE Linux стартовыми скриптами и создадим адаптивную сетевую коммутационную настройку.
В первой части статьи [1] были предложены пути и детально разобраны принципы искусственной виртуализации сетевых интерфейсов. Виртуализация не просто для усиления названа искусственной. Создание по специальному сетевому мосту на каждый используемый интерфейс является не самоцелью. Таким образом, достигаются следующие преимущества (см. [1]):
1.Постоянство схемы разметки и наименования сетевых устройств;
2.Независимость настройки сетевых сервисов от состояния реальных линий связи;
3.Автоматизация сетевой коммутации;
4.Удобство подключения виртуальных серверов.
Если просуммировать вышесказанное, то получится, что виртуализация позволяет создать независимую от состава оборудования, то есть стабильную сетевую разметку, на основе которой можно построить внутреннюю серверную архитектуру, обладающую устойчивостью к отказам и масштабируемостью. И самое главное, что такая архитектура может быть положена в основу многоцелевых и весьма разнообразных систем. Говоря иначе, таким путем будет разработан собственный серверный стандарт.
Но для начала формализуем процедуру виртуализации, так как без этого не получить корректного способа включения ее в стартовую последовательность Linux.
Алгоритм виртуализации.
Вот как представляется последовательность действий по виртуализации, согласно первой части статьи [1]:
1.Переводим все устройства в новый именной ряд с помощью udev;
2.Меняем их настройку так, чтобы обеспечить включение в состав сетевых мостов;
3.Создаем необходимое число сетевых мостов с требуемыми настройками;
4.Коммутируем реальные устройства с нужными сетевыми мостами.
После этого получаем виртуальный эквивалент старой настройки. Теперь задумаемся как подобное можно соединить со стандартными стартовыми скриптами. Пункт № отрабатывается при старте udev. Ничего менять не нужно, кроме некоторой правки имен устройств в /etc/udev/rules.d/30-net_persistent_names.rules. Как уже было предложено, назовем все реальные устройства как hweth*. Тогда строка, переименовывающая соответствующее устройство, например eth3, будет выглядеть следующим образом:
# grep eth3 /etc/udev/rules.d/30-net_persistent_names.rules SUBSYSTEM=="net", ACTION=="add", --перенос строки-- SYSFS{address}=="00:14:85:21:1b:2f", IMPORT="/sbin/rename_netiface %k hweth3" 2 Modern Linux - network virtualizing Подробнее о применяемой в настройках udev нотации можно прочесть в [2] и, конечно же, в документации, сопровождающей пакет udev. Здесь лишь поясню, что в приведенной строке задается переименование сетевого устройства с аппаратным адресом 00:14:85:21:1b:2f в процессе его добавления в систему. После выполнения данного правила в системе появится устройство не с тем именем, что выдается ядром в порядке очередности, а с требуемым, в нашем случае это будет hweth3 (от hardware ethernet). Теперь должно быть уже понятно, что если случайно назначаемое имя совпадет с одним из уже используемых, то скрипт rename_netiface не сможет выполнить свою задачу. И не важно, что данное совпадение может носить временный характер. Чтобы такого конфликта не возникало, рекомендуется использовать нестандартные имена. Нам такая рекомендация только на руку!
Рисунок 1. Настройка безадресного физического интерфейса.
Далее, вторым шагом, надо сделать все реальные устройства безадресными, или, например, установить им адрес 0.0.0.0, чтобы можно было воспользоваться в настройке и управлении стандартными системными скриптами. Если на этапе установки системы YaST-ом были уже настроены сетевые интерфейсы, то достаточно поправить им адрес, если же нет, то надо создавать их сразу с нулевым адресом (см. рисунок 1). Для уже описанного в предыдущем абзаце сетевого устройства файл параметров будет выглядеть так:
# cat /etc/sysconfig/network/ifcfg-eth-id-00:14:85:21:1b:2f BOOTPROTO='static' 3 Modern Linux - network virtualizing BROADCAST='' IPADDR='0.0.0.0' MTU='' NAME='Giga-byte CK804 Ethernet Controller' NETMASK='255.255.255.0' NETWORK='' REMOTE_IPADDR='' STARTMODE='auto' UNIQUE='DkES.QUKldky+OPE' USERCONTROL='no' _nm_name='bus-pci-0000:00:0a.0' PREFIXLEN='' Этот файл создавался с помощью YaST. Но можно его написать и самостоятельно. Более того, поскольку для всех интерфейсов эти файлы имеют практически одинаковый вид, отличаются лишь комментарием - оператор NAME - и уникальным номером конфигурации, который легко получить из начального, просто меняя в каждом последующем один или более символ на следующий в алфавитном порядке, то очень легко автоматизировать эту стадию настройки. Надо только придерживаться того правила, что имя файла формируется на основании физического адреса интерфейса по схеме ifcfg-eth id-MAC.
Подготовленные подобным образом файлы настроек сетевых устройств будут полностью совместимы с существующей системой запуска сетевой подсистемы на сервере. И в процессе выполнения скрипта /etc/init.d/network все перечисленные интерфейсы будут подняты как безадресные и совершенно пассивные с точки зрения возможностей передачи трафика.
На третьем шаге нам потребуется создать набор необходимых сетевых мостов. Это первый нестандартный шаг. Нужно сначала создать нужное число мостов с помощью команды brctl addbr eth*. Вот тут мы будем использовать удобные и привычные имена сетевых интерфейсов, начинающихся с традиционного префикса eth. То есть воспользоваться ими мы сможем лишь тогда, когда ВСЕ реальные интерфейсы будут переименованы в иной именной ряд. Если в ходе работы будут добавляться или меняться сетевые карты, то эту процедуру надо будет повторять. Тогда имена на eth станут свободными, и их можно будет использовать в качестве имен сетевых мостов, которые, собственно, и послужат основой сетевой виртуализации. Как только мосты созданы, ими можно манипулировать с помощью стандартных сетевых скриптов. Для этого в директории /etc/sysconfig/network надо создать файлы с сетевыми настройками, названные по схеме ifcfg-eth*, где eth* соответствует имени сетевого моста, которому эти настройки предназначены. Например, некоторому сетевому мосту eth2, используемому как канал в Интернет, может соответствовать следующий файл настроек:
# cat /etc/sysconfig/network/ifcfg-eth BOOTPROTO='static' UNIQUE='alZb.Tm92cpV9oKE' IPADDR='213.xx.xx.xxx' NETMASK='255.255.255.248' BROADCAST='213.xx.xx.xxx' NETWORK='213.xx.xx.xxx' MTU='' NAME='Virtual device eth2 VDSL' STARTMODE='auto' REMOTE_IPADDR='' USERCONTROL='no' 4 Modern Linux - network virtualizing PREFIXLEN='' POST_UP_SCRIPT='/etc/fw/fw-on-internet' POST_DOWN_SCRIPT='/etc/fw/fw-off-internet' Перечень опций можно узнать из man ifup. Здесь приведены реальные данные VDSL соединения. Все опции очевидны и тривиальны кроме двух последних строк, отвечающих за настройку сетевого экрана. Но пока эту тему не будем затрагивать, так как о ней поговорим особо чуть позже. Кстати, и для этой операции можно использовать YaST (см.
рисунок 2).
Рисунок 2. Создание виртуального устройства.
Значение в выделенном на рисунке 2 поле будет использовано при генерации имени такого виртуального устройства. В данном случае 0, то есть образуется eth0, и все параметры будут записаны в файл ifcfg-eth0.
Здесь уже становится понятным, что, хотя мы описываем последовательность настройки с номером 3, но в стартовой последовательности эти действия надо будет выполнить до старта скрипта /etc/init.d/network, иначе конфигурационные файлы виртуальных интерфейсов не будут обработаны и сетевые мосты не будут подняты и настроены нужным образом. Таким образом, реальный номер этого шага скорее будет лодин с половиной или два без четверти, как угодно.
5 Modern Linux - network virtualizing Последнее, четвертое действо, связывание реальных устройств с сетевыми мостами, не только можно выполнить в любое время после старта скрипта /etc/init.d/network, но и, самое приятное, что его можно независимо от всего остального повторять, меняя настройки. Как и в шаге 3, на данном этапе придется использовать нестандартный скрипт.
Итак, у нас вышло, что все необходимое можно реализовать двумя дополнительными скриптами после некоторой модификации существующих настроек. Рассмотрим подробнее.
Стартовая схема.
Теперь давайте выделим из перечисленного ту часть, которую надо повторять при каждом старте, считая, что все необходимые шаги по настройке виртуализованного интерфейса уже выполнены однажды. В этом случае число шагов несколько сократится:
1.Создадим необходимое число сетевых мостов, соответствующих используемыми сетевыми интерфейсами;
2.Поднимем и реальные, и виртуальные интерфейсы;
3.Скоммутируем реальные устройства с нужными сетевыми мостами.
В перечисленном выше шаг 3 полностью соответствует стандартному скрипту /etc/init.d/network. Дополнительно надо не забыть перед стартом /etc/init.d/boot.udev проконтролировать, что в конфигурации udev не возникло нежелательных устройств, занимающих используемые нами системные имена eth*. Вспомним здесь же о необходимости предустановить настройки сетевого экрана. Обе задачи можно совместить, если скрипты, их выполняющие, вызвать из /etc/init.d/boot.local. Полностью стартовая последовательность должна выглядеть, как представлено на рисунке 3. Рассмотрим ее подробнее.
В некоторой точке настройки системы (№1, рисунок 3) выполняется скрипт boot.local, предназначенный для размещения дополнительных пользовательских функций, которые должны выполниться после системного старта. Это прекрасное место для инициализации сетевого экрана и контроля настроек udev. Сначала заблокируем весь трафик скриптом fw init :
#!/bin/sh iptables -F iptables -t nat -F iptables -t mangle -F iptables -X iptables -t nat -X iptables -t mangle -X iptables -P INPUT DROP iptables -P OUTPUT DROP iptables -P FORWARD DROP Затем, разрешим работу интерфейса lo скриптом fw-main:
#!/bin/sh IPT=$(which iptables) LIST1=("-A OUTPUT" "-A INPUT" "-A FORWARD" "-t nat -A POSTROUTING") LIST2=("-o" "-i" "-i" "-o") LIST3=("OUTPUT" "INPUT" "FORWARD" "POSTROUTING") 6 Modern Linux - network virtualizing SIZE= IF=lo I= while [ "$I" -lt "$SIZE" ] ;
do J=${LIST1[$I]} K=${LIST2[$I]} L=${LIST3[$I]} $IPT $J $K $IF -j ACCEPT $IPT $J -j LOG --log-prefix "main $L DROP " --log-level notice $IPT $J -j DROP I=$((I+1)) done $IPT -P INPUT ACCEPT $IPT -P OUTPUT ACCEPT $IPT -P FORWARD ACCEPT Рисунок 3. Стартовая последовательность.
7 Modern Linux - network virtualizing Этот скрипт поместит в каждую цепочку разрешение для трафика через lo, явное протоколирование и уничтожение всего остального трафика, и поменяет политики на разрешительные. Сохранение политики DROP не позволит с помощью анализа логов понять причину неправильной работы сети в случае ошибок в настройке сетевого экрана.
А так, если уничтожение пакетов происходит явным образом, можно обнаружить, куда исчезает трафик. Скрипты являются частью системы управления сетевым экраном, которая здесь не описана. Разделение их на два независимых файла сделано для того, что бы можно было каждой функцией воспользоваться по отдельности. Например, fw-init просто очищает все настройки iptables и блокирует трафик.
Ну и напоследок чрезвычайно простая программка fixudev, удаляющая все строки в настройках udev, изменяющие имена в те, что используются в схеме виртуализации:
#!/bin/sh N=/etc/udev/rules.d/30-net_persistent_names.rules T=/tmp/30-net_persistent_names.rules.$$ cat $N | grep -v "k eth" >$T mv $T $N Такой скрипт, конечно же, может испортить настройку нового сетевого интерфейса, произведенную через YaST, но он не позволит разрушить уже созданную сетевую настройку из-за конфликта имен.
В точке 2 (рисунок 3) отработает boot.udev, который по стандартным зависимостям выполняется после boot.local, и настроит необходимое для работы службы udev.
И теперь, в точке 3 (рисунок 3), все готово для создания виртуальных устройств сетевых мостов. Этим будет заниматься специальный стартовый скрипт bridgeprep. Полный его текст, точно также как тексты всех используемых здесь программ, размещается в прилагаемом архиве [3]. Все стартовые скрипты SUSE пишутся по стандартному шаблону /etc/init.d/skeleton. Поэтому отмечу лишь актуальные и системно-зависимые части. Во первых, в самом начале создается управляющая секция, согласно LSB:
### BEGIN INIT INFO # Provides: bridgeprep # Required-Start: boot.udev # Required-Stop: boot.udev # Default-Start: 2 3 # Default-Stop:
# Short-Description: Ethernet bridge # Description: Start Bridge to allow configure Bridge device ### END INIT INFO Здесь задается специальный идентификатор - bridgeprep, который позволит ссылаться на этот скрипт из управляющих секций других скриптов, далее указывается очередность - после boot.udev, и уровни выполнения в которые скрипт включается по умолчанию. Если скрипт с такой управляющей секцией установить командой chkconfig bridgeprep on, то он будет включен в уровни 2,3,5 на любое свободное место очередности в стартовой последовательности после boot.udev, а в стоповой - до boot.udev.
Во-вторых, проверяется программное окружение и настраиваются внутренние переменные:
8 Modern Linux - network virtualizing # Check for missing binaries (stale symlinks should not happen) BRIDGE_BIN=/sbin/brctl test -x $BRIDGE_BIN || ( echo "not found $BRIDGE_BIN" ;
exit 5 ) # Check for existence of needed config file and read it BRIDGE_CONFIG=/etc/sysconfig/bridge test -r $BRIDGE_CONFIG || ( echo "not found $BRIDGE_CONFIG" ;
exit 6 ). $BRIDGE_CONFIG Отсутствие brctl или собственных управляющих файлов скрипта приведет к аварийному завершению его работы. В этом же фрагменте производится чтение управляющего файла, который стандартно располагается в /etc/sysconfig c совершенно неоригинальным именем bridge, и представляет собой просто список переменных с присваиваемыми им значениями, заданный в формате командной оболочки bash. Это тоже совершенно стандартное решение для подобных системных параметров, и их задание и модификацию можно производить как из консоли с помощью редактора, так и используя оснастку в YaST. Содержание файла /etc/sysconfig/bridge будем рассматривать синхронно с кодом, его использующим.
Далее, в-третьих, небольшая характерная для SUSE косметическая добавка:
# Shell functions sourced from /etc/rc.status:
. /etc/rc.status # Reset status of this service rc_reset Это позволит отражать результат работы скрипта в протоколе так, как принято для скриптов SUSE.
Ну и, наконец, актуальная часть. Рассмотрим только стартовую ветку оператора case. Все остальное работает точно таким же образом. Вот часть кода, отвечающего за старт:
case "$1" in start) echo "Starting bridge " for i in ${BRIDGE_LIST} ;
do $BRIDGE_BIN addbr $i P="BRIDGE_FD_$i" ;
P=${!P} ;
[ "1$P" != "1" ] && $BRIDGE_BIN setfd $i $P P="BRIDGE_HELLO_$i" ;
P=${!P} [ "1$P" != "1" ] && $BRIDGE_BIN sethello $i $P P="BRIDGE_STP_$i" ;
P=${!P} ;
[ "1$P" != "1" ] && $BRIDGE_BIN stp $i $P done $BRIDGE_BIN show echo -n # Remember status and be verbose rc_status -v ;
;
Предполагаю, что вы знакомы с синтаксисом языка сценариев bash, если нет, то подробности можно почерпнуть в отличном руководстве [4]. В указанном программном фрагменте используются косвенные ссылки. Сначала в переменной BRIDGE_LIST задается список сетевых мостов, которые будут созданы, например, так:
BRIDGE_LIST="eth0 eth1 eth2 eth3" 9 Modern Linux - network virtualizing Переменная $i пробегает в цикле все значения из этого списка и применяется для последовательного создания всех устройств в команде brctl addbr $i. Дополнительно для каждого сетевого моста можно задать несколько параметров в формате BRIDGE_парамет_имя, например, так:
BRIDGE_FD_eth0= BRIDGE_HELLO_eth0= BRIDGE_STP_eth0=off Если параметр определен в файле конфигурации /etc/sysconfig/bridge, то производится его настройка для соответствующего виртуального устройства. Поскольку все наши сетевые мосты будут использоваться исключительно как средства виртуализации, то представленные выше значения для FD, HELLO и STP, а именно 0,0 и off, надо указать для каждого сетевого моста в обязательном порядке.
Как видите, все очень просто. Новый стартовый скрипт надо установить принятым в SUSE способом:
# cp bridgeprep /etc/init.d # ln Цs /etc/init.d/bridgeprep /sbin/rcbridgeprep # chkconfig bridgeprep on После выполнения данного скрипта в процессе старта системы будут созданы сетевые мосты, и их можно будет поднять, как и другие сетевые устройства, с помощью стандартного скрипта network, на рисунке 3 пункт 4. Но надо непременно указать, чтобы скрипт network выполнялся ПОСЛЕ скрипта bridgeprep. Для этого в дистрибутивном скрипте подправим одну строчу в управляющей секции LSB:
# Required-Start: $local_fs bridgeprep Где укажем, что работа скрипта network теперь зависит еще и от bridgeprep. Таким образом, при очередной перенастройке местоположения network в стартовой последовательности chkconfig network off ;
chkconfig network on, этот скрипт займет правильное положение согласно рисунку 3.
Если до запуска network в основном происходит настройка локальных сервисов, то после network будут запущены ВСЕ сетевые устройства независимо от состояния внешних линий, и можно смело запускать также и сетевые сервисы... - все равно ни один из них не будет доступен снаружи. Полностью сервер подключится к сети лишь после отработки скрипта bridgeup (пункт 5, рисунок 3). Этот скрипт, отмеченный на рисунке голубым контуром, является нестандартным дополнением и построен аналогично рассмотренному ранее bridgeprep. Но у него уже иная стартовая зависимость:
# Required-Start: random bridgeprep network И, конечно же, иной алгоритм работы. Этот скрипт производит сборку сетевых мостов, то есть подключение к ним физических устройств. Полностью его работу рассмотрим в следующем разделе, а сейчас приведем лишь фрагмент, производящий статическую сборку:
for i in ${BRIDGE_LIST} ;
do eval j=\$BRIDGE_DEVS_$i 10 Modern Linux - network virtualizing if [ "1$j" != "1" ] ;
then # static device list for k in $j ;
do [ "1$k" != "1" ] && { $BRIDGE_BIN addif $i $k BRIDGE_POOL=`remove_dev $k` } done else...
Здесь все очевидно - если надо включить некоторые устройства в нужный мост, то в /etc/sysconfig/bridge записывается:
BRIDGE_DEVS_eth0="hweth0 hweth2" И два физических интерфейса с именем hweth0 и hweth2 будут добавлены командой brctl addif к мосту eth0. Этот скрипт тоже должен быть установлен тем же способом, что и предыдущий.
Подведем итоги. Цена всей доработки стартовой схемы, как и обещано, два нестандартных скрипта и небольшая правка в network. После этого все начинает функционировать по новому, так как планировалось в [1].
Отдельно опишем, как решаются проблемы с настройкой сетевых экранов. Итак, после boot.local во всех цепочках уничтожаются все пакеты кроме следующих через lo. И в промежутке от пункта 1 и вплоть до пункта 5 на рисунке 3 можно делать с сетевым экраном что угодно. Это никак не отразится на работе сервера и его служб, если все реальные сетевые устройства настроены как 0.0.0.0, а все виртуальные еще не подключены ни к одному реальному. Поэтому предлагается настройки сетевого экрана выполнять для каждого интерфейса синхронно с его включением скриптом network, а точнее, программой ifup. Как уже было написано в начале, в каждый файл параметров сетевого интерфейса добавляются дополнительные настройки, например:
POST_UP_SCRIPT='/etc/fw/fw-on-internet' POST_DOWN_SCRIPT='/etc/fw/fw-off-internet' И соответствующие скрипты будут вызваться на выполнение сразу после поднятия интерфейса и после его отключения. Сам сетевой экран надо строить по схеме, аналогичной используемой для устройства lo:
1.Создаются подцепочки для каждой основной цепочки iptables. Например inputeth0 для INPUT и устройства eth0.
2.Затем они наполняются нужными правилами, завершаемыми правилом с целью DROP.
3.И лишь после того в начало основной цепочки добавляется переход на созданный фильтр по совпадению используемого интерфейса.
Подобная схема полностью исключает возможности конфликтной или недостаточной фильтрации, так как за каждый интерфейс отвечает лишь одно правило в основной цепочке и собственная подцепочка фильтра, завершаемая правилом с целью DROP, то есть не теряющая правила ни в каком случае.. Глубже вопросы проектирования сетевых экранов в этой статье рассматриваться не будут, так как тема требует самостоятельного освещения.
11 Modern Linux - network virtualizing Два последних скрипта в стартовой последовательности (рисунок 3), network и bridgeup, можно останавливать и запускать независимо от всего остального. Но, к сожалению, нет штатного способа указать, что перед остановкой network надо остановить bridgeup, и, соответственно, после запуска network не забыть запустить и bridgeup. А так как здесь идет речь о минимальном вмешательстве в системные скрипты, то ограничимся тем, что просто запомним эту взаимосвязь.
Адаптивная коммутация.
Теперь выполнены почти все обещания из первой части [1]. Но есть еще одно важное и пока не реализованное преимущество схемы виртуализации - соединение виртуальных устройств с реальными, согласно фактически произведенной внешней коммутации. Иначе говоря, в ходе выполнения bridgeup сервер должен проверить соответствие реальных подключений планируемым. Разберем это на конкретном примере. Безусловно, пример не идеален, так как он специфичен моей практике. Но описанные методы позволят построить читателю собственную схему и для других условий работы сервера.
Рисунок 4. Сетевая разметка тестового сервера.
12 Modern Linux - network virtualizing Далее все будем описывать согласно рисунку 4. Итак, есть сервер с четырьмя сетевыми картами, названными hweth0-4. Есть три внешних кабельных подключения (розовые квадраты) - локальная сеть (LOCALNET), основной ISP, подключенный через ADSL, и дополнительное подключение к региональной сети (MAN ISP), где провайдер использует DHCP. Внутри сервера создаются тоже четыре сетевых интерфейса (голубые квадраты).
Первый - для работы с локальной сетью (LOCALNET). Второй - как виртуальная внутренняя сеть для связи виртуальных же машин (CLUSTERNET). Третий - для соединения с ADSL модемом (ADSL ISP). И четвертый для подключения к региональному провайдеру (MAN ISP).
Задача заключается в том, что операционная система, стартующая на этом сервере, не знает, как на самом деле произведена коммутация внешних соединений, но должна это выяснить на лету и, соответственно, настроить сетевые мосты. А иначе, спрашивается, зачем мы все это затевали?
Вся работа будет производиться в скрипте bridgeup в самом завершении стартовой последовательности (рисунок 3). Все интерфейсы уже подняты и сетевые экраны уже настроены (красная пунктирная линия на рисунке 4). И вот теперь вопрос - если ли способ узнать, что на кабеле в таком состоянии, когда физические интерфейсы еще не включены в мосты?
Ну, конечно же, всем известная утилита tcpdump позволит прослушать такие интерфейсы. Если вызвать ее как tcpdump -ln -i hweth0, то можно получить протокол всех пойманных там пакетов, и по характеру трафика понять, какой провод воткнут в джек сетевой карты hweth0. Точно также как это можно сделать взглядом, можно и просчитать через grep по сохраненному протоколу работы этой утилиты за некоторое контрольное время. Например, если мы знаем, что в локальной сети у нас используется сетевой коммутатор, работающий по протоколу STP, то даже в пустой сети можно поймать периодически рассылаемые сообщения, содержащие в расшифровке tcpdump подстроку л802.1d config. Такая проверка реализована в скрипта check-tcpdump [3]. Скрипт, в свою очередь, вызывается из bridgeup сразу после приведенного выше фрагмента, как альтернатива, вызываемая по условию else, если отсутствует строка статического задания сборки моста:
eval j=\$BRIDGE_TCPDUMP_$i if [ "1$j" != "1" ] ;
then # check tcpdump L="" for k in ${BRIDGE_POOL} ;
do n=`${BRIDGE_CHK_TCPDUMP} $k "$j" | grep BINGO` [ "1$n" != "1" ] && { $BRIDGE_BIN addif $i $k L="$L $k" } done for k in $L ;
do BRIDGE_POOL=`remove_dev $k` done else Как уже видно, за эту ветку настройки отвечает специальная строка конфигурации сетевого моста:
13 Modern Linux - network virtualizing BRIDGE_TCPDUMP_eth0="802.1d config" Если она определена, то скрипт перебирает все устройства, оставшиеся в списке незанятых (BRIDGE_POOL) и проверяет протокол перехваченного tcpdump трафика с помощью check-tcpdump на совпадение с заданным образцом. Безусловно, выбор правильного и эффективного образца для сравнения всецело на совести системного администратора, производящего настройку, так как бывают сети с чрезвычайно замусоренным трафиком.
Например, данная строка поиска работала до тех пор, пока в сети MAN не был подключен аналогичный коммутатор, как и в локальной сети. Тогда пришлось заменить контрольный образец на более подробный л802.1d config 8000.00:11:d8:cb:df:99.8001, содержащий MAC устройства. И именно по причине ложного совпадения или из-за отсутствия трафика вообще, или из-за того, что нельзя гарантированно во время прослушивания получить уверенный отклик, приходится прибегать к способам активного тестирования кабельных подключений.
Итак, если слушать нечего или невозможно, тогда остается спросить у сети что-то конкретное. Как принято выяснять наличие или отсутствие сетевых хостов - с помощью icmp. Но у нас еще нет скоммутированных соединений, и такое высокоуровневое средство не подходит, так как обмен посылками icmp является достаточно многоступенчатым процессом. Нам придется выбрать что-то попроще. Самое простое - arp-запрос. Только сформировать его надо будет не штатной утилитой, так как сервер еще реально не подключен к сетевым соединениям. Для таких целей прекрасно подходит утилита nemesis [5]. Можно взять готовый бинарный пакет [6] и установить его в систему. Arp-запрос позволит узнать о существовании в сетевой среде, подключенной к исследуемому интерфейсу, некоторого хоста, отвечающего по требуемому адресу. При этом запрос отправляется в стиле скажите хосту с адресом1, какой физический адрес имеет хост с адресом2. Очень удобно то, что подобный запрос практически никогда не блокируется, так как протокол ARP отвечает за самое низкоуровневое взаимодействие. В терминах утилиты nemesis это выглядит так:
nemesis arp -d устройство адрес 1 -D 2-S адрес Вызов nemesis добавляется между запуском tcpdump и отсчетом контрольного времени.
После прекращения прослушивания надо точно также профильтровать результат расшифровки трафика tcpdump. Но только образец для поиска будет иной, а именно arp reply адрес2 is-as. Все это выполняется скриптом check-arp [3].
Теперь для настройки надо лишь определить хост, наличие которого будет со всей определенностью указывать на нужную сетевую среду. Например, если сетевой мост eth должен быть подключен к модему ADSL с адресом 10.0.0.2 (рисунок 4, зеленая стрелка), то в настройку добавляем строку:
BRIDGE_ARP_eth2="10.0.0.1 10.0.0.2" Очередной фрагмент кода bridgeup, отвечающий за следующую альтернативу else, вызываемую в отсутствие других настроек, проверит существование переменной BRIDGE_ARP_устройство и произведет соответствующую проверку оставшихся в списке BRIDGE_POOL устройств в поиске подходящего:
eval j=\$BRIDGE_ARP_$i if [ "1$j" != "1" ] ;
then 14 Modern Linux - network virtualizing # check arp L="" for k in ${BRIDGE_POOL} ;
do n=`${BRIDGE_CHK_ARP} $k $j | grep BINGO` [ "1$n" != "1" ] && { $BRIDGE_BIN addif $i $k L="$L $k" } done for k in $L ;
do BRIDGE_POOL=`remove_dev $k` done fi fi fi В нашем случае других проверок не предусматривается и приведенный ваше блок закрывает все операторы if, но пытливый и изобретательный читатель может продолжить настройки, если так будет требоваться конкретной задачей.
Вот как это действует на практике. После старта системы, если кабельные подключения соединены согласно рисунку 4, то запрос статуса соединения виртуальных устройств покажет следующее (листинг приведен с сокращениями):
# rcbridgeup status Checking for service bridge running bridge name bridge id STP enabled interfaces eth0 8000.000479661b70 no hweth eth1 8000.000000000000 no eth2 8000.001022ff43d7 no hweth eth3 8000.00xxxxxxxxxx no hweth eth0 Link encap:Ethernet HWaddr 00:04:79:66:1B: inet addr:192.168.0.9 Bcast:192.168.0.255 Mask:255.255.255....
eth1 Link encap:Ethernet HWaddr 00:00:00:00:00: inet addr:192.168.254.1 Bcast:192.168.254.255 Mask:255.255.255....
eth2 Link encap:Ethernet HWaddr 00:10:22:FF:43:D inet addr:10.0.0.1 Bcast:10.0.0.255 Mask:255.255.255....
eth3 Link encap:Ethernet HWaddr 00:XX:XX:XX:XX:XX inet addr:172.16.YY.YY Bcast:172.16.255.255 Mask:255.255.0....
Интерфейс eth3 уже успел получить адрес через DHCP (и адрес, и MAC скрыты, так как их можно легко использовать для спуфинга в MAN). Затем изменяем коммутацию случайным образом. Чтобы система почувствовала это изменение, в нашем случае надо перезапустить bridgeup вручную, поскольку нам достаточно правильно настроить сеть после включения устройства. Но можно настроить периодический контроль, если персонал в серверной имеет привычку к спонтанной перекоммутации кроссов.
# rcbridgeup stop Disassemble bridgies bridge name bridge id STP enabled interfaces eth0 8000.000000000000 no eth1 8000.000000000000 no eth2 8000.000000000000 no eth3 8000.000000000000 no done # rcbridgeup start 15 Modern Linux - network virtualizing Assemble bridgies eth0 eth1 eth2 eth Hardware pool hweth0 hweth1 hweth2 hweth bridge name bridge id STP enabled interfaces eth0 8000.00xxxxxxxxxx no hweth eth1 8000.000000000000 no eth2 8000.0011d8956c2e no hweth eth3 8000.000000000000 no Free NICs hweth0 hweth done # Процесс отключения происходит совершенно безупречно, а вот при подключении не обнаруживается соединение с MAN, так как в используемой сети применяется контроль по MAC, а разрешенное к применению устройство hweth3 было уже задействовано для связи с локальной сетью. И теперь уже две сетевые карты остались не задействованными. Таким образом, система предотвратила недопустимое соединение.
После того, как только кабели снова были возвращены на старые места и перезапущен bridgeup, все пришло в норму:
# rcbridgeup stop...
# rcbridgeup start Assemble bridgies eth0 eth1 eth2 eth Hardware pool hweth0 hweth1 hweth2 hweth bridge name bridge id STP enabled interfaces eth0 8000.000479661b70 no hweth eth1 8000.000000000000 no eth2 8000.001022ff43d7 no hweth eth3 8000.00xxxxxxxxxx no hweth Free NICs hweth done # Заключение.
Вот и все - запланированные цели достигнуты. Но полученное несколько больше простого технического приема. Как уже было сказано, таким образом создается независимая сетевая архитектура сервера. Или, говоря иначе, создается дополнительный слой виртуализации, что позволяет унифицировать так же и все настройки, основанные на сетевых параметрах, для систем, расположенных выше данного слоя. Максимальный выигрыш получается при использовании виртуальных серверов. Так как миграция, а также и создание, и резервирование, и восстановление их требует единства внутрисервернной среды. Но подробнее тема сетевой архитектуры будет рассмотрена в статьях, посвященных виртуальным серверам.
Несколько неожиданным может показаться еще один вывод. Описанное адаптивное поведение сетевой системы по отношению к внешней коммутации, кроме простого сохранения правильных настроек сетевого экрана, может привести и к философским выводам. Если считать линии коммуникации для компьютера эквивалентом органов осязания, то управление этими органами является необходимым минимумом интеллектуальности. Если бы сетевые розетки были снабжены манипуляторами, ничего бы не удивляло. Но здесь получается, что и без механического приспособления компьютер под управлением Linux может сам выбрать правильное подключение. Иначе говоря, может скомпенсировать ошибку оператора или техника. То есть мы сделали компьютер немного 16 Modern Linux - network virtualizing умнее, научив его видеть и менять интерфейсное подключение программным способом.
Использованные ссылки:
1. Алексей Барабанов. Современный Linux-сервер: виртуализируем сетевые устройства.
Системный администратор, №6(43) июнь 2006.
2. Daniel Drake. Writing udev rules.
3. Архив исходных текстов к статье.
4.Advanced Bash-Scripting Guide. Искусство программирования на языке сценариев командной оболочки Версия 2.5 (15 февраля 2004) Автор: Mendel Cooper (thegrendel at theriver dot com) Перевод: Андрей Киселев (kis_an at mail dot ru) 5. Nemesis packet injection utility.
6. Бинарная сборка nemesis для SUSE.