Libvirt виртуализируем сетевые устройства

Материал из support.qbpro.ru
Версия от 14:02, 31 марта 2021; imported>Vix (Новая страница: «В серверном эксплуатационном цикле часто возникает потребность во всевозможных манипу...»)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)

В серверном эксплуатационном цикле часто возникает потребность во всевозможных манипуляциях над сетевыми подключениями в режиме 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. Сравнение традиционного способа настройки с виртуализованным интерфейсом

В левой части традиционная схема подключения, далее – то же самое, но с сетевым мостом, а в правой – новая. Согласно схеме (рис. 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.000      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.000      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. Запрос на перевод интерфейса в режим прослушивания

Рисунок 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. Тестовая система с виртуальными интерфейсами и сетями

Рисунок 3. Тестовая система с виртуальными интерфейсами и сетями

Внутри хостовой, той, которая служит основой, машины создан ряд сетевых мостов по схеме, описанной выше:

# ifconfig eth0

bridge name     bridge id               STP enabled     interfaces

eth0            8000.000.479661b70       no              hweth3

                                                        hweth5

eth1            8000.000       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.000.479661b70       no              hweth3

                                                        hweth5

                                                        tap1

eth1            8000.2e4.8adefd363       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.000.479661b70       no              hweth3

                                                        hweth5

                                                        tap1

eth1            8000.2e4.8adefd363       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.

Предварительные итоги

Итак, подмена сетевых устройств виртуальными сетевыми мостами создает массу возможностей для манипуляции. В случае использования виртуальных серверов вообще такая настройка является единственно возможной. Но нельзя же каждый раз выполнять приведенные в статье команды, даже если их записать в отдельный скрипт. О том, как встроить предлагаемую схему разметки сетевых устройств в структуру реальных стартовых скриптов без каких-нибудь конфликтов для работы остальных подсистем, поговорим во второй части статьи. ИСТОЧНИК: