АЛЕКСЕЙ БАРАБАНОВ
Современный Linux-сервер: виртуализируем сетевые устройства
В серверном эксплуатационном цикле часто возникает потребность во всевозможных манипуляциях над сетевыми подключениями в режиме on-line. Переведя последние в категорию виртуальных, можно значительно упростить их обслуживание и предусмотреть многие внештатные ситуации.
Сетевые интерфейсы в серверах обычно ассоциируются с чем-то неизменным и предопределенным. Их число, топология подключения и настройки, как правило, заранее определяются в проектном задании и не меняются в процессе штатной эксплуатации. Но даже в таком идеализированном рассуждении статичность интерфейсов весьма условна. Возможен и иной взгляд на подсистему, отвечающую за сетевые интерфейсы. Безусловно, это всего лишь некий технический прием или рекомендация, и каждый из вас волен следовать ей или предпочесть консервативный вариант настроек. Более того, и не откровение, так как элементы такого подхода вы можете встретить во многих системах, например, во встроенных Linux или при создании виртуальных серверов. Рассмотрим предпосылки, идею и реализацию перевода сетевых интерфейсов в виртуальный режим. В качестве платформы будем использовать openSUSE Linux версий 10.0 и 10.1 [1]. Все несоответствия между ними я отмечу особо. Итак, начнем с перечисления возможных проблем, не решаемых в рамках традиционной настройки сети.
Исходные предпосылки
Рассмотрим «редкий» вариант, когда у сервера всего лишь один сетевой интерфейс. Что тут сложного, спрашивается, – от «рождения» и до «смерти» будет eth0. А вот и нет, как бы того не хотелось многим наивным скриптописателям, eth0 будет лишь в идеальном случае при установке системы прямо на данный системный блок. Если же, что более реально, сервер не устанавливается каждый раз с нуля, а копируется через образ системы или всего жесткого диска, то, скорее всего, даже единственный интерфейс будет иметь иное название. Eth1 и далее, в том случае, если полагаться на автоматическую систему наименования устройств, использованную в udev, что для современных систем является стандартом.
Но в серверах, как правило, более одного интерфейса. И если их названия в некоторый момент упорядочены и устраивают системного администратора и используемые им настроечные скрипты, то представим, что может произойти в случае отказа одного из интерфейсов. Например, произошел отказ в процессе старта. В старых системах, без использования udev, происходило смещение нумерации устройств или фактическая перекоммутация соединений. Интерфейсы, предназначенные для внутрисетевых соединений, могли переключиться на внешние линии. Практически это означало крах всей системы защиты. Современные скрипты, ориентированные на udev, привязывают имена сетевых интерфейсов к их физическим адресам или PCI-идентификаторам. Можно быть уверенным, что катастрофического «выворачивания» сервера внутренними интерфейсами наружу не произойдет. Но есть иная беда. Сетевая конфигурация, созданная автоматическим образом, может потерять свое сбалансированное состояние, и очередная перезагрузка может привести к новому изменению имен интерфейсов из-за отказа одной из сетевых карт. Впрочем, и при добавлении сетевой карты в систему с udev такие случаи нередки. Итогом подобного процесса может стать частичный или полный отказ стартовых сетевых скриптов, то есть изоляция сервера от сети. Для противодействия этому в системах с udev рекомендуется применять постоянные, или персистентные (от persistent – постоянный) сетевые имена. Увы, в такой схеме главное – исключить столкновение переименований. Другими словами, сетевые интерфейсы можно называть как угодно, но только не по схеме ethN, принятой по умолчанию.
Выход из строя сетевой карты непосредственно в ходе эксплуатации потребует замены физического интерфейса, то есть в процессе очередного старта произойдет добавление следующего номера в базу udev при обнаружении нового устройства, что сведется к ранее описанной проблеме привязки имен. Но можно и рассмотреть существование в серверном блоке сетевой карты, рассчитанной на горячую замену. Тут мы сталкиваемся с иной сложностью. Простое отключение интерфейса через ifdown (как вариант через ip link set) может привести к нарушению работы сетевых служб, использующих (прослушивающих) адрес данного интерфейса. Это в том благоприятном случае, если вы уверены, что созданная «на лету» аварийная схема при следующем рестарте не сломается.
Еще один редкий сейчас крэш-сценарий: сервер или поставляется в виде «черного ящика», или обслуживается на удаленной площадке привлеченным техником. Здесь возможна ошибка кабельной коммутации, что может привести к смене назначений сетевых соединений – внутрисетевые на внешние и наоборот. При традиционной разметке сетевых интерфейсов не существует возможности автоматически исправить ошибку внешнего кабельного подключения.
Теперь пример из смежной области. Давайте задумаемся о том, как производится включение сетевого экрана в процессе старта сервера. Самый грамотный из мною наблюдаемых – это способ, использованный в дистрибутивах SuSE. Безусловно, в качестве серьезного решения это совершенно неприемлемо (практически бытовой вариант). Отмечу лишь одно важное качество: в этом сетевом экране выделена отдельная стадия предварительной настройки – сначала срабатывает SuSEfirefall2_init после старта boot.localnet, а потом уже SuSEfirewall2_setup после $network. Не буду обсуждать, что эти скрипты делают конкретно, меня ни один из них не устраивает, но главное – здесь продемонстрирована необходимость нулевой фазы настройки сетевого экрана, которая переводит сервер в защищенное состояние. А вот иная схема, используемая в Fedora Core и RHEL, совершенно неприемлема. Кстати, как и всякое применение iptables-save/restore. Дело в том, что, как уже указано, вовсе не обязательно сетевые интерфейсы стартуют нужным образом. Поэтому не факт, что iptables-restore создаст те правила, которые требуются из-за изменившейся схемы наименования интерфейсов. В схеме SuSE есть скрипт, который теоретически может проанализировать реальное состояние сети и адаптировать сетевой экран нужным образом. Минус только в том, что сетевой экран должен подниматься не отдельным скриптом по расписанию SysVinit, а синхронно с интерфейсом, на который он настраивается! Увы, хотя разработчики Red Hat, Inc. далеки от этой простой мысли, но и в SuSE не лучше. Не может система безопасности стартовать, во-первых, после поднятия сетевых интерфейсов (и ведь никто не нормирует данное «после») и, во-вторых, вне всякой связи с настройкой динамических сетевых интерфейсов (wifi, ppp и прочие ADSL).
Возникновение базовой идеи
Причины для определенного рода озабоченности несовершенством сетевых настроек перечислены. Но путей решения указанных проблем множество. Например, можно проследить, как развивались (если не сказать – метались!) предложения разработчиков SUSE по настройке персистентных сетевых имен от версии дистрибутивов 9.х, где эта проблема возникла, до используемых сейчас 10.х. Аналогично и в отношении применяемого в упомянутом дистрибутиве сетевого экрана. Ведь неспроста в его названии использован индекс 2. Можно, например, пойти вообще радикальным путем, как это предлагается в ALT Linux, и использовать иную схему запуска сети [2]. Так что практика размножения сущностей и версий может продолжаться бесконечно. Для меня выбор способа, который, как кажется сейчас, может решить все проблемы разом, произошел почти историческим путем.
Несколько лет назад, как только код сетевого моста [3] был включен в очередной дистрибутив SUSE, мне показалось очень интересным использовать сетевой мост в качестве встроенного в сервер коммутатора, подключенного к локальной сети. Во-первых, это позволяло сократить объем дополнительного активного сетевого оборудования, использованного в формировании топологии сети, что было очень выгодным в маленьких офисах или при организации сетевых рабочих групп. И во-вторых, выделяло вакантные интерфейсы для горячей замены в случае отказа оборудования, что очень важно, если сервер обслуживается удаленно, поскольку переключить на запасной интерфейс и быстрей и дешевле, чем дожидаться приезда сисадмина, кроме того, что можно и сам сетевой интерфейс использовать как своего рода файловер. И теперь, по прошествии более 4 лет со времени внедрения подобного способа разметки локальной сети, можно сказать, что эта технология была испытана многолетней практикой и ни разу не подвела.
Но сейчас появились дополнительные аргументы в пользу использования именно сетевых мостов в качестве основы виртуализации. Логика развития серверных систем неизбежно приводит к необходимости внутренней виртуализации ресурсов и компонентов. Причин этому очень много. Их рассмотрение заслуживает отдельной статьи. Как один из последних опубликованных аргументов приведу мнение Эндрю С. Таненбаума (Andrew S. Tanenbaum) о необходимости изоляции подсистем для увеличения надежности и безопасности [4]. Лучший способ добиться изоляции – поместить нужные подсистемы в отдельную виртуальную машину. К слову сказать, статья по ссылке [4] весьма спорная, о чем свидетельствует и сопутствовавшая ей сетевая дискуссия. Здесь предлагается принять на веру, что рано или поздно, но использование вложенных виртуальных машин внутри серверов станет нормой. Практически во всех видах подобной виртуализации ресурсов внутренний, или зависимый, уровень получает непосредственный доступ к сети путем присоединения к виртуальному сетевому мосту (bridge) [5, 6], если исключить всякого рода роутинг. Поэтому будем считать абсолютно обоснованным создание сетевого окружения, приспособленного для подключения виртуальных систем, заранее в расчете на развитие.
Технология изнутри
Итак, выбор сделан – сетевой мост. Статей, описывающих настройку сетевых мостов в Linux и сопутствующие этому вопросы, огромное число, например [7]. Не буду дублировать очевидное. Обращаю ваше внимание лишь на самое важное в контексте предлагаемой идеи. Сам сетевой мост является полностью виртуальным устройством. Реальный трафик принимается только физическими сетевыми устройствами. Чтобы трафик попал на сетевой мост с некоторого физического устройства или, напротив, с сетевого моста поступил в реальный интерфейс, нужное сетевое устройство должно быть подключено к мосту командой:
brctl addif
Иначе трафик не будет проходить. Здесь внимательный читатель уже должен все понять – таким путем реализуется внутренняя коммутация. Это основная идея. То есть изначально сетевой мост предназначен для объединения нескольких реальных сетевых интерфейсов в общий коммутатор. Но ничего не мешает использовать выделенные сетевые мосты для каждого реального интерфейса. Собственно, как и наоборот – для каждого (внимание: здесь еще одно усложнение) реального, но виртуализованного путем назначения на выделенный специальный сетевой мост использовать один или несколько аппаратных сетевых интерфейсов из массива имеющихся в составе оборудования сервера. И вот эту привязку можно производить динамически. Все сказанное иллюстрируется схемой (см. рис. 1).
Рисунок 1. Сравнение традиционного способа настройки с виртуализованным интерфейсом
В левой части традиционная схема подключения, далее – то же самое, но с сетевым мостом, а в правой – новая. Согласно схеме (рис. 1, правая часть) сетевой мост переименовывается, также как и заменяемый интерфейс, а имя физической сетевой карты заменяется на служебное название. Причем все сетевые настройки выполняются вне зависимости от текущего состояния коммутации между ними. Сама же коммутация из стадии настройки переводится в категорию состояния комплексного сетевого соединения, включающего сетевой мост и предназначенные к подключению физические сетевые интерфейсы.
Если сетевая карта вышла из строя, то подключается предназначенная на замену. Если в «джек» сетевой карты вставлен «плуг» другого кабеля, то она подключается на правильный виртуальный интерфейс согласно произведенной кабельной коммутации после проверки трафика на данном кабельном подключении. Если нужно отключить сервер временно от некоторой сети, то можно не вынимать кабель из сетевой карты, не останавливать сетевые сервисы, не запирать firewall, а просто отсоединить физический интерфейс от используемого моста. Но на этом преимущества не кончаются. Все почти так же, как и с вытянутым носом киплинговского слоненка.
«Физика» работы сетевого моста требует, чтобы используемое аппаратное устройство принимало из сети пакеты для адреса сетевого моста, в который это устройство включено. Такое возможно, если в сетевой карте отключить аппаратную фильтрацию пакетов и перевести ее в promiscuous-режим или, как будем называть это далее, перевести в режим прослушивания. Самый простой способ добиться этого – поднять интерфейс с адресом 0.0.0.0 или, что более верно, вовсе без адреса. Собственно, в ином состоянии утилита brctl откажется подключать интерфейс к мосту. И в процессе подключения интерфейс будет переведен в режим прослушивания автоматически. Но в этом тезисе ничего не сказано про наличие действительного адреса у самого сетевого моста. Он может иметь реальный адрес, может иметь несколько адресов как алиасы (синонимы), может иметь адрес, немарштуризируемый в используемой сети, или вовсе не иметь адреса. Ну и, наконец, сетевой мост может вообще не соединяться с физическим интерфейсом. Тогда он образует полностью виртуальный коммутатор внутренней сети, к которому могут подключаться виртуальные машины через виртуальные же интерфейсы, и использовать его как внутреннюю сеть кластера. Рассмотрим эти варианты подробнее и проверим их экспериментально.
Сетевой мост с реальным адресом
Эту часть экспериментов проведем в системе, установленной внутрь виртуальной машины с полной и достаточно тщательной эмуляцией, а именно внутрь VMWare. Но, уверяю вас, все будет полностью работоспособно и на реальном оборудовании. Более того, такие условия позволят сделать некоторые дополнительные выводы.
Виртуальный интерфейс с реальным адресом подходит для использования в самом распространенном и одновременно тривиальном случае. Это фактически эквивалентная замена традиционного интерфейса на пару, состоящую из виртуального и реального устройства. К мосту с актуальным адресом подключается безадресный физический интерфейс, который принимает все пакеты в данной сети, а фильтрацию производит IP-стек, работающий поверх сетевого моста. То есть сетевой экран тоже настраивается на сетевом мосту. И все используемые сетевые сервисы также «слушают» адрес сетевого моста.
Итак, произведем подобную настройку. Прежде всего отключим имеющийся сетевой интерфейс:
# ifdown eth0
eth0 device: Advanced Micro Devices [AMD] 79c970 [PCnet32 LANCE] (rev 10)
eth0 configuration: eth-id-00:0c:29:89:80:c2
|
# ip link sh
1: lo: mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:89:80:c2 brd ff:ff:ff:ff:ff:ff
3: sit0: mtu 1480 qdisc noop
link/sit 0.0.0.0 brd 0.0.0.0
|
Затем переименуем его, так как «реальное» имя нам понадобится далее для создаваемого виртуального интерфейса:
# ip link set dev eth0 name hweth0
# ip link sh
1: lo: mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: hweth0: mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:89:80:c2 brd ff:ff:ff:ff:ff:ff
3: sit0: mtu 1480 qdisc noop
link/sit 0.0.0.0 brd 0.0.0.0
|
Теперь переименованный интерфейс снова можно включить. Впредь он будет играть роль простого элемента коммутации без собственного IP-адреса:
# ip link set dev hweth0 up
# ifconfig hweth0
hweth0 Link encap:Ethernet HWaddr 00:0C:29:89:80:C2
inet6 addr: fe80::20c:29ff:fe89:80c2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:15 errors:0 dropped:0 overruns:0 frame:0
TX packets:31 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:2629 (2.5 Kb) TX bytes:3211 (3.1 Kb)
Interrupt:177 Base address:0x1400
|
Создадим сетевой мост с предпочтительным именем, в нашем случае это eth0, так как виртуализацию этого устройства мы и производим:
# brctl addbr eth0
# brctl show
bridge name bridge id STP enabled interfaces
eth0 8000.000000000000 no
|
# ip link set dev eth0 up
# ip link sh
1: lo: mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: hweth0: mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:89:80:c2 brd ff:ff:ff:ff:ff:ff
3: sit0: mtu 1480 qdisc noop
link/sit 0.0.0.0 brd 0.0.0.0
4: eth0: mtu 1500 qdisc noqueue
link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
|
В приведенном выше протоколе обратите внимание на то, что виртуальный интерфейс eth0 даже после включения командой ip link set up не имеет канального адреса (MAC). Даже присвоение ему IP-адреса ничего не меняет:
# ip addr add 192.168.0.111/32 dev eth0
# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:00:00:00:00:00
inet addr:192.168.0.111 Bcast:0.0.0.0 Mask:255.255.255.255
inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:17 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:1536 (1.5 Kb)
|
И это объяснимо, так как виртуальный интерфейс пока не скоммутирован ни с одним реальным физическим интерфейсом, то есть не подключен ни к одной сети. Проверим это:
# ip route add 192.168.0.0/24 dev eth0
# ping -c 1 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
From 192.168.0.111: icmp_seq=1 Destination Host Unreachable
--- 192.168.0.1 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 1ms
|
Как видите, трафик не идет. Точно такая же картина и при попытке «достучаться» до адреса 192.168.0.111 с других хостов сети. Теперь соединим eth0 с физическим интерфейсом:
# brctl show
bridge name bridge id STP enabled interfaces
eth0 8000.000000000000 no
|
# brctl addif eth0 hweth0
// здесь происходит автоматический перевод hweth0 в режим прослушивания |
# ip link sh
1: lo: mtu 16436 qdisc noqueue
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: hweth0: mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:89:80:c2 brd ff:ff:ff:ff:ff:ff
3: sit0: mtu 1480 qdisc noop
link/sit 0.0.0.0 brd 0.0.0.0
4: eth0: mtu 1500 qdisc noqueue
link/ether 00:0c:29:89:80:c2 brd ff:ff:ff:ff:ff:ff
|
# brctl show
bridge name bridge id STP enabled interfaces
eth0 8000.000c298980c2 no hweth0
|
# ping -c 1 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=29.9 ms
--- 192.168.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 29.959/29.959/29.959/0.000 ms
|
Разберем описанное в протоколе. Во-первых, в точке, выделенной ремаркой, произошел автоматический перевод физического интерфейса в режим прослушивания. Эмулятор это «поймал» и выдал запрос оператору (см. рис. 2).
Рисунок 2. Запрос на перевод интерфейса в режим прослушивания
Во-вторых, несмотря на предупреждение о невозможности данной операции, тем не менее все работает, как следовало. Из чего можно сделать вывод, что реальный интерфейс компьютера, в котором запущен эмулятор VMWare, и так уже работает в состоянии promiscuous (кстати, это не отображается системными флагами), что подтверждает тезис о неизбежности перехода к виртуализованным сетевым интерфейсам в перспективе. В-третьих, сетевой мост получает канальный адрес, такой же как адрес первого присоединенного интерфейса. Ну и, наконец, трафик – пошел! Обратите внимание на задержки. Они велики. Но это лишь следствие инерционности работы сетевого моста. И немного спустя все приходит в норму. Например, так выглядит «пингование» с удаленного хоста спустя некоторое время:
server:~ # ping -c 1 192.168.0.111
PING 192.168.0.111 (192.168.0.111) 56(84) bytes of data.
64 bytes from 192.168.0.111: icmp_seq=1 ttl=64 time=0.498 ms
--- 192.168.0.111 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.498/0.498/0.498/0.000 ms
|
То есть на рабочей производительности это не сказывается. Более того, подобное подключение полностью прозрачно для всех сетевых служб даже канального уровня. Например, переключим виртуальный интерфейс в режим DHCP:
# ip addr del 192.168.0.111/32 dev eth0
# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:0C:29:89:80:C2
inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:41 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:860 (860.0 b) TX bytes:3502 (3.4 Kb)
|
# brctl show
Internet Systems Consortium DHCP Client V3.0.3
Copyright 2004-2005 Internet Systems Consortium.
All rights reserved.
For info, please visit http://www.isc.org/products/DHCP
Listening on LPF/eth0/00:0c:29:89:80:c2
Sending on LPF/eth0/00:0c:29:89:80:c2
Sending on Socket/fallback
DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 5
DHCPOFFER from 192.168.0.1
DHCPREQUEST on eth0 to 255.255.255.255 port 67
DHCPACK from 192.168.0.1
bound to 192.168.0.178 -- renewal in 1429 seconds.
|
# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 00:0C:29:89:80:C2
inet addr:192.168.0.178 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:25 errors:0 dropped:0 overruns:0 frame:0
TX packets:56 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:2651 (2.5 Kb) TX bytes:5370 (5.2 Kb)
|
Как видите, все работает. Собственно, другого и не стоило бы ожидать, поскольку уже было сказано, что на подобной технологии давно и устойчиво работают такие способы виртуализации серверов, как UML, XEN и даже VMWare.
Мост, объединяющий виртуальные устройства
Но наиболее интересные способы использования сетевых мостов как основы виртуализации получаются, если в них включать псевдоустройства, которые сами являются виртуальными. Например, виртуальные устройства, создаваемые в качестве туннельных. Но ведь самое главное преимущество сетевого моста в том, что он позволяет работать с включенными в него интерфейсами на уровне L2 модели ISO OSI. Поэтому наиболее уместным кандидатом на такую интеграцию можно считать виртуальный интерфейс гостевого сервера. Итак, рассмотрим, как можно манипулировать сетевыми подключениями вложенного сервера UML (user mode linux), подключенного к специально созданным сетевым мостам. Этот пример, к сожалению, недоступен на openSUSE Linux 10.1 из-за прекращения поддержки UML в указанной версии. Утилиты для настройки специальных виртуальных устройств tun/tap можно найти только в версии 10.0 этого дистрибутива. И хотя альтернативный вариант виртуализации Xen, который работает с сетью точно так же, присутствует и там и там, мы будем демонстрировать все приемы на UML, поскольку эта технология более «прозрачна» для анализа.
Предположим, что все необходимые элементы для запуска виртуальной ОС уже созданы. Подробно данная тема будет затронута в следующих статьях цикла. Пока прошу поверить «на слово». Структура тестовой системы изображена на рис. 3.
Рисунок 3. Тестовая система с виртуальными интерфейсами и сетями
Внутри хостовой, той, которая служит основой, машины создан ряд сетевых мостов по схеме, описанной выше:
# ifconfig eth0
bridge name bridge id STP enabled interfaces
eth0 8000.000479661b70 no hweth3
hweth5
eth1 8000.000000000000 no
|
В мосту eth0 два реальных порта, а в мосту eth1 вообще нет физических интерфейсов, и он поэтому полностью виртуальный. Но даже в том виде, как он существует, без канального адреса, мост вполне работоспособен:
server:~ # ifconfig eth1
eth1 Link encap:Ethernet HWaddr 00:00:00:00:00:00
inet addr:192.168.254.1 Bcast:192.168.254.255 Mask:255.255.255.0
inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:28 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:2166 (2.1 Kb)
|
И через него легко идет реальный трафик:
server:~ # ping -c 1 192.168.254.1
PING 192.168.254.1 (192.168.254.1) 56(84) bytes of data.
64 bytes from 192.168.254.1: icmp_seq=1 ttl=64 time=0.049 ms
--- 192.168.254.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.049/0.049/0.049/0.000 ms
|
После запуска виртуальной машины в системе создаются два псевдоустройства tap0 и tap1. Эти устройства передают трафик, который на них маршрутизируется, обработчику из пользовательского пространства, в данном случае виртуальной машине UML. Их создание производится командой «tunctl» непосредственно перед запуском UML, которому названия полученных устройств передаются в командной строке. И ОС, запущенная внутри виртуальной машины (в случае UML, сама модифицированная ОС и является такой виртуальной машиной) использует эти псевдоустройства в качестве эмулятора аппаратных сетевых интерфейсов. На стороне хостовой машины устройства tun/tap подключаются внутрь сетевых мостов, если предполагается разрешить на них работу на уровне L2. Мы так и поступим:
server:~ # brctl show
bridge name bridge id STP enabled interfaces
eth0 8000.000479661b70 no hweth3
hweth5
tap1
eth1 8000.2e48adefd363 no tap0
|
Сразу после подключения к мосту eth1 первого, как ему «представляется» реального устройства, мост получает и собственный канальный адрес:
server:~ # ifconfig eth1
eth1 Link encap:Ethernet HWaddr 2E:48:AD:EF:D3:63
inet addr:192.168.254.1 Bcast:192.168.254.255 Mask:255.255.255.0
inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:7 errors:0 dropped:0 overruns:0 frame:0
TX packets:44 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:372 (372.0 b) TX bytes:3628 (3.5 Kb)
|
server:~ # ifconfig tap0
tap0 Link encap:Ethernet HWaddr 2E:48:AD:EF:D3:63
inet6 addr: fe80::2c48:adff:feef:d363/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:13 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:560 (560.0 b) TX bytes:548 (548.0 b)
|
Этот адрес случайным образом был установлен для tap0 при его создании. В процессе дальнейшей работы данный MAC более нигде не будет фигурировать, так как устройство tun/tap представляет собой туннель, в котором реальным является лишь пользовательский конец, находящийся внутри виртуальной машины UML. И если внутри UML «поднять» интерфейс eth0, который будет привязан к виртуальному устройству tap0, с некоторым адресом, то внутри виртуальной сети появится новый хост с указанным адресом:
server:~ # ping -c 1 192.168.254.2
PING 192.168.254.2 (192.168.254.2) 56(84) bytes of data.
64 bytes from 192.168.254.2: icmp_seq=1 ttl=64 time=0.125 ms
--- 192.168.254.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.125/0.125/0.125/0.000 ms
|
server:~ # arp –n
Address HWtype HWaddress Flags Mask Iface
192.168.254.2 ether FE:FD:C0:A8:01:00 C eth1
|
Все это в точности совпадает с настройками интерфейса eth0 в UML:
uml:~ # ifconfig eth0
eth0 Link encap:Ethernet HWaddr FE:FD:C0:A8:01:00
inet addr:192.168.254.2 Bcast:192.168.254.255 Mask:255.255.255.0
inet6 addr: fe80::fcfd:c0ff:fea8:100/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:103 errors:0 dropped:0 overruns:0 frame:0
TX packets:7 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:18261 (17.8 Kb) TX bytes:518 (518.0 b)
Interrupt:5
|
Таким образом, продемонстрирована работоспособность полностью виртуальной сети. В данном случае необходимость такой сети вызвана тем, что нужно было создать средство «общения» запускаемых виртуальных машин с хостовым сервером.
Например, им всем через внутреннюю сеть «раздается» рабочий дистрибутив:
server:~ # showmount –e
Export list for server:
/srv/susedvd/SU1000_001 192.168.254.0/255.255.255.0,localhost
|
Эта связь может стать особенно важной, если сам сервер не будет иметь иного способа подключиться к гостевой машине. Спросите, как это может быть? Очень просто! Для этого рассмотрим второй виртуальный интерфейс на рис. 3, который подключает и сервер, и его гостевую ОС к локальной сети. Для этого используется мост eth0:
server:~ # brctl show
bridge name bridge id STP enabled interfaces
eth0 8000.000479661b70 no hweth3
hweth5
tap1
eth1 8000.2e48adefd363 no tap0
|
Псевдоустройство tap1 точно так же, как и tap0, безадресное, а вот сам мост имеет адрес:
server:~ # ifconfuigeth0
eth0 Link encap:Ethernet HWaddr 00:04:79:66:1B:70
inet addr:192.168.0.9 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:14968 errors:0 dropped:0 overruns:0 frame:0
TX packets:499 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:792910 (774.3 Kb) TX bytes:77724 (75.9 Kb)
|
Внутри UML интерфейс, соответствующий tap1, настроен для работы в локальной сети:
uml:~ # ifconfig eth1
eth1 Link encap:Ethernet HWaddr FE:FD:C0:A8:01:01
inet addr:192.168.0.2 Bcast:192.168.0.255 Mask:255.255.255.0
inet6 addr: fe80::fcfd:c0ff:fea8:101/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:92240 errors:0 dropped:0 overruns:0 frame:0
TX packets:314 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:4576001 (4.3 Mb) TX bytes:13468 (13.1 Kb)
Interrupt:5
|
При попытке «пропинговать» адрес UML получаем ответы:
server:~ # ping -c 1 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=7.14 ms
--- 192.168.0.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 7.142/7.142/7.142/0.000 ms
|
server:~ # arp –n
Address HWtype HWaddress Flags Mask Iface
192.168.0.2 ether FE:FD:C0:A8:01:01 C eth0
192.168.254.2 ether FE:FD:C0:A8:01:00 C eth1
|
Может, это вызвано неким локальным феноменом? Проверим видимость адреса UML с еще двух компьютеров в сети. После отправки ICMP-запросов внутри arp-хеша появились новые записи:
server:~ # arp –n
Address HWtype HWaddress Flags Mask Iface
192.168.0.2 ether FE:FD:C0:A8:01:01 C eth0
192.168.0.10 ether 00:05:5D:E7:86:39 C eth0
192.168.0.11 ether 00:05:5D:74:DD:5D C eth0
|
Значит, все произошло успешно. Теперь сбросим адрес у самого сетевого моста. Это приведет к невозможности работы с локальной сетью внутренних процессов сервера. На рисунке 3 красная пунктирная линия обозначает прерванные связи.
server:~ # ip addr sh dev eth0
7: eth0: mtu 1500 qdisc noqueue
link/ether 00:04:79:66:1b:70 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.9/24 brd 192.168.0.255 scope global eth0
inet6 fe80::200:ff:fe00:0/64 scope link
valid_lft forever preferred_lft forever
|
server:~ # ip addr del 192.168.0.9/24 dev eth0
server:~ # ip route sh
10.0.0.0/24 dev eth2 proto kernel scope link src 10.0.0.1
192.168.254.0/24 dev eth1 proto kernel scope link src 192.168.254.1
127.0.0.0/8 dev lo scope link
|
Все! Теперь тот самый случай – никакая сеть, кроме виртуальной, хостовому серверу не доступна:
server:~ # ping -c 1 192.168.0.2
connect: Network is unreachable |
А вот с других хостов сети «пинги» проходят вполне успешно. Адрес внутреннего виртуального UML-сервера присутствует в локальной сети и нормально работает. Если к данному мосту подключить следующую виртуальную систему с помощью соответствующего tun/tap устройства, на пользовательской стороне которого поднят иной адрес, то и он станет виден со стороны сети, подключенной к физическому интерфейсу сетевого моста. И так далее.
Получился безадресный сетевой мост, который работает как arp-прокси и транслирует трафик с виртуальных хостов. В чем его ценность? Такая схема позволяет защитить хостовую ОС от враждебного трафика сети, если вместо приватной сети используется, например Интернет. Еще один распространенный случай использования, когда безадресный хостер служит платформой для запуска виртуальных гостевых ОС, каждой со своим собственным адресом. Ну и, наконец, ничего не мешает применять на таком несущем сетевом мосте собственную сетевую разметку, не маршрутизируемую общим порядком, создавая скрытую служебную сеть. Есть, правда, и тут некоторые «подводные камни». Дело в том, что процедура взаимодействия сетевых узлов на уровне L2 является слабо защищенной. И в некоторых сетях используются самодельные службы и другие нестандартные настройки, контролирующие канальное взаимодействие. И хотя описанный выше прием с безадресным мостом прекрасно работает (не говоря уже, что такой классический способ настройки моста описан в документации), но бывали случаи, когда такая настройка действовала как DoS-атака. Точно так же стоит очень внимательно отнестись к множеству физических интерфейсов, объединенных в мост, – их неверное подключение может создать неконтролируемые пути доступа или утечки трафика и тоже стать источником проблем L2.
Предварительные итоги
Итак, подмена сетевых устройств виртуальными сетевыми мостами создает массу возможностей для манипуляции. В случае использования виртуальных серверов вообще такая настройка является единственно возможной. Но нельзя же каждый раз выполнять приведенные в статье команды, даже если их записать в отдельный скрипт. О том, как встроить предлагаемую схему разметки сетевых устройств в структуру реальных стартовых скриптов без каких-нибудь конфликтов для работы остальных подсистем, поговорим во второй части статьи.
Ссылки:
- Сайт проект Open SUSE – http://en.opensuse.org/Welcome_to_openSUSE.org и область загрузки дистрибутива openSUSE 10.0 – http://ftp.opensuse.org/pub/opensuse/distribution/SL-10.0-OSS.
- Проект альтернативных сетевых скриптов – http://etcnet.org.
- Страница wiki, на которую переадресует бывший «домашний» сайт проекта IEEE 802.1d ethernet bridging (http://bridge.sourceforge.net) – http://linux-net.osdl.org/index.php/Bridge.
- Andrew S. Tanenbaum, Jorrit N. Herden, and Herbert Bos, Vrije Universiteit, Amsterdam, May 2006, «Can We Make Operating System Reliable and Secure?» –http://www.computer.org/portal/site/computer/menuitem.5d61c1d591162e4b0ef1bd108bcd45f3/index.jsp?&pName=computer_level1_article&.
- User Mode Linux. Set up the network – http://user-mode-linux.sourceforge.net/networking.html.
- openSUSE. Xen and Virtual Network – http://en.opensuse.org/Xen3_and_a_Virtual_Network.
- Закляков П. Разводной мост на Linux (bridging firewalls). – Журнал «Системный администратор», №4, 2003 г. – 42-55 с. – http://www.samag.ru/cgi-bin/go.pl?q=articles;n=04.2003;a=07.