Дата и время публикации:
Проблемы и решение
1. Суть проблем
В случае, если узел виртуальной машины имеет два подключения к физической сети центрального узла гипервизора через устанавливаемые эмулятором Qemu связки сетевых интерфейсов eth0 → tap1 и eth1 → tap2, которые образуют две подсети с ip:192.168.7.0/24 и ip:192.168.8.0/24 соответственно, может иметь место путиница при передачи и приеме пакетов , как показано в дапме 1.1
Дамп 1.1
user@kvm-host:~$ sudo tcpdump -i any host 192.168.7.1 or host 192.168.7.2 tcpdump: data link type LINUX_SLL2 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes 19:50:56.246215 tap2 In IP 192.168.7.2 > 192.168.2.10: ICMP echo request, id 36866, seq 0, length 64 19:50:56.246279 tap1 Out IP 192.168.7.1 > 192.168.7.2: ICMP 192.168.2.10 protocol 1 port 40584 unreachable, length 92
В дампе 1.1 показано, что при попытке передать ICMP-пакет на узел с ip:192.168.2.10, указанный пакет был передан по связке eth0 → tap2, а ответ о не найденом адресе (или порта) узла пришел на связку tap1 → eth1 c соответствующими ip:192.168.7.1 / 192.168.7.2
Маршрутизация и связка сетевых интерфейсов eth0 → tap1 и eth1 → tap2 эмулятором Qemu/KVM, конфигурация которой устанавливалась в соответствие с рецептом мультиподжерки сетевых устройств виртуальной машины "ВМ ОС Yocto/poky". Кроме того, настройка подсети с "ip:192.168.2.10", где живет узел "ВМ ОС Debian-10", осуществлялась с помощью программной надстройки libvirt, которая устанавливает свои правила пакетной фильтрации (Packet filtering) и управляет продвижением пакетов (Packet forwarding) межсетевого экрана iptables / netfilter в операционной системе Linux на центральном узле гипервизора.
Движение трафика между узлами "ВМ ОС Debian-10" и "ВМ ОС Yocto/poky" по выше перечисленным интерфейсам функционально можно представить, как показано на рисунке 1.2
Рисунок 1.2
Как показано на рисунке 1.2, фактическое движение трафика от узла "ВМ ОС Debian-10" к узлу "ВМ ОС Yocto/poky" идет через интерфейсы eth0 → tap1, tap2 → eth1, virbr1, и программный межсетевой экран, который реализуется подсистемой netfilter ядра ОС GNU/Linux центрального узла гипервизора.
Конфигурация сетевых интерфейсов tap1, tap2, и virbr1 центрального узла гипервизора показана в дампе 1.3
Дамп 1.3
user@kvm-host:~$ ip a ... 5: virbr1:mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 52:54:00:77:1d:d5 brd ff:ff:ff:ff:ff:ff inet 192.168.2.2/24 brd 192.168.2.255 scope global virbr1 valid_lft forever preferred_lft forever 6: tap0: mtu 1500 qdisc pfifo_fast master virbr1 state UNKNOWN group default qlen 1000 link/ether fe:8b:32:5d:3c:5c brd ff:ff:ff:ff:ff:ff inet6 fe80::fc8b:32ff:fe5d:3c5c/64 scope link valid_lft forever preferred_lft forever 7: tap1: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether ca:07:44:b6:06:7a brd ff:ff:ff:ff:ff:ff inet 192.168.7.1/32 brd 192.168.7.255 scope global tap1 valid_lft forever preferred_lft forever inet6 fe80::c807:44ff:feb6:67a/64 scope link valid_lft forever preferred_lft forever 8: tap2: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether b2:35:63:60:f0:33 brd ff:ff:ff:ff:ff:ff inet 192.168.8.1/32 brd 192.168.8.255 scope global tap2 valid_lft forever preferred_lft forever inet6 fe80::b035:63ff:fe60:f033/64 scope link valid_lft forever preferred_lft forever
При этом показанная далее в дампе 1.4 маршрутизация на центральном узле гипревизора кажется на первый взгляд не совсем верной из-за путница в интерфейсах с "ВМ ОС Yocto/poky".
Дамп 1.4
$ ip route show ... 192.168.2.0/24 dev virbr1 proto kernel scope link src 192.168.2.2 192.168.7.2 dev tap1 scope link 192.168.8.2 dev tap2 scope link
И не вяжется с маршрутизацией виртуального узла "ВМ ОС Yocto/poky", показанной в дампе 1.5
Дамп 1.5
$ ip route show default via 192.168.7.1 dev eth0 192.168.7.0/24 dev eth0 scope link src 192.168.7.2 192.168.8.0/24 dev eth1 scope link src 192.168.8.2
При этом нельзя утверждать, что связка tap1 → eth0 и tap2 → eth1 со стороны центрального узла гипервизора KVM не правильно установлена, потому что подтверждается отправкой ответных ICMP пакетов (выделено жирным) от узла "ВМ ОС Yocto/poky" к узлу центрального гипервизора через ip:192.168.7.1 / 192.168.7.2, интерфейсы tap1 / eth0 и ip:192.168.8.1 / 192.168.8.1, интерфейс tap2 / eth1 соответственно. При этом отправка начального ICMP-пакета производилась на стороне "ВМ ОС Yocto/poky" путем последовательного ввода команд ping -c1 192.168.8.1 и ping -c1 192.168.7.1 соответственно.
Дамп 1.6
user@kvm-host:~$ sudo tcpdump -i any host 192.168.7.1 or host 192.168.8.1 ... 15:12:29.026694 tap1 In IP 192.168.8.2 > 192.168.8.1: ICMP echo request, id 36354, seq 0, length 64 15:12:29.026750 tap2 Out IP 192.168.8.1 > 192.168.8.2: ICMP echo reply, id 36354, seq 0, length 64 ... 15:12:34.567164 tap2 In IP 192.168.7.2 > 192.168.7.1: ICMP echo request, id 36610, seq 0, length 64 15:12:34.567230 tap1 Out IP 192.168.7.1 > 192.168.7.2: ICMP echo reply, id 36610, seq 0, length 64 ...
Поэтому, как показано в дампе 1.6, такая путаница затрудняет прохождение пакетов от узла "ВМ ОС Yocto/poky" до "ВМ ОС Debian-10" и завершается с отсечкой любых ICMP-пакетов, потому что в работу маршрутизации вмешивается механизм фильтрации пакетов межсетевого экрана в ядре центрального узла гипервизора, который реализован в подсистемы netfilter ядра ОС GNU/Linux и настроен на свой лад (для подсети c ip:192.168.2.0/24) программной прослойкой libvirt с помощью правил фильтрации через утилиту командной строки iptables
2. Решение
2.1 Реализация прямого канала
Производилась путем дополнения цепочек YP_FWI, YP_FWO к уже созданными ранее программной прослойкой libvirt и состоящим из последовательности цепочек LIBVIRT-{FWO,FWX,FWI, INP, OUT} таблицы filter пакетной фильтрации [3.4]. Вместе с тем, также дополняется цепочка YP_PRT, вставляемой пред уже активированной цепочки LIBVIRT_PRT в POSTROUTE таблицы nat, функционально обеспечивающие движение трафика, как показано на рисунке 2.1
Рисунок 2.1
Как показано на рисунке 2.1, для перенаправления исходящего пакета c узла "ВМ ОС Yocto/poky" на узел "ВМ ОС Debian-10" необходимо воспользоваться цепочками FORWARD и MASQUERADE бэкенда iptables, которые реализуют соответствующие политики.
Политика FORWARD через одноименную цепочку обеспечивает фильтрацию пакетов, получаемых с tap2 и с разу же перенаправляет их на virbr1 при условии, что ip-адрес источника является 192.168.7.2/32, а ip-адрес приемника находится в пуле адресов подсети, имеющей ip: 192.168.2.0/24
Это значить, что нужно определить дополнительно пользовательские цепочки YP_FWI, YP_FWO и YP_PRT к уже имеющимся и определенным libvirt в таблицах filter и nat соответственно.
Для прямого канала в цепочке YP_FWI нужно определить переход на цепочку YP_FWO, которая будет иметь необходимые правила для организации прямого канала передачи исходящего сообщения в виде ICMP-пакета. Их нужно добавить, а вернее вставить, в цепочку FORWARD, в той последовательности в какой они следуют на рисунке 2.1 и применить через фронтенд iptables, как показано в дампе 2.2
Дамп 2.2
user@kvm-host:~$sudo iptables -N YP_FWI user@kvm-host:~$ sudo iptables -I FORWARD 2 -j YP_FWI user@kvm-host:~$ sudo iptables -N YP_FWO user@kvm-host:~$ sudo iptables -I FORWARD 5 -j YP_FWO
В результате , пользовательские цепочки в FORWARD должны идти в той последовательности и нумерацией в какой они показаны в листинге 2.3 и на рисунке 2.1
Дамп 2.3
... FORWARD: 1: -A FORWARD -j LIBVIRT_FWX 2: -A FORWARD -j YP_FWI 3: -A FORWARD -j LIBVIRT_FWI 4: -A FORWARD -j LIBVIRT_FWO 5: -A FORWARD -j YP_FWO ...
Далее, нужно было определить правила для перехода по условию, когда приходит ICMP-пакет на вход tap2 , имеющего IP-адрес источника 192.168.7.2/32 и направленного к узлу-приемника в подсети c ip:192.168.2.0/24, как показано в листинге 2.4
Листинг 2.4
YP_FWI: 1: -A YP_FWI -s 192.168.7.0/24 -d 192.168.2.0/24 -i tap2 -j YP_FWO ...
Затем, в первой позиции цепочки YP_FWO изменил (установил) правило перенаправления ICMP-пакета на нужный интерфейс, как показано в листинге 2.5
Листинг 2.5
YP_FWO: 1: -A YP_FWO -s 192.168.7.2/32 -i tap2 -o virbr1 -j ACCEPT ...
После чего, с узла "ВМ ОС Yocto/poky" послал ICMP-пакет и наблюдал в дампе 2.6 изменение количество зарегистрированных пакетов по сравнению с дампом 1.1
Дамп 2.6
user@kvm-host:~$ sudo tcpdump -i any host 192.168.7.1 or host 192.168.7.2 ... 12:45:00.742026 tap2 In IP 192.168.7.2 > 192.168.2.10: ICMP echo request, id 36610, seq 0, length 64 12:45:00.742070 virbr1 Out IP 192.168.7.2 > 192.168.2.10: ICMP echo request, id 36610, seq 0, length 64 12:45:00.742078 tap0 Out IP 192.168.7.2 > 192.168.2.10: ICMP echo request, id 36610, seq 0, length 64 12:45:00.742326 tap0 P IP 192.168.2.10 > 192.168.7.2: ICMP echo reply, id 36610, seq 0, length 64 12:45:00.742326 virbr1 In IP 192.168.2.10 > 192.168.7.2: ICMP echo reply, id 36610, seq 0, length 64 12:45:00.742373 virbr1 Out IP 192.168.2.2 > 192.168.2.10: ICMP 192.168.7.2 protocol 1 port 42706 unreachable, length 92 ...
Казалось бы все хорошо и прекрасно, потому что канал связи наконец-то заработал в ожидаемом направлении и ICMP-пакет доставлен конечному адресату, как показано в дампе 2.6 . Но, так как речь идет об изоляции двух узлов виртуальных машин друг от друга , поэтому адресату назначения необязательно знать кто на самом деле является отправителем до того момента, когда они попали на шлюз с ip:192.168.2.2, как показано в дампе 2.7
Дамп 2.7
user@debian10-uni:~$ ... 13:01:03.214124 IP 192.168.2.2 > 192.168.2.10: ICMP echo request, id 37890, seq 0, length 64 13:01:03.214165 IP 192.168.2.10 > 192.168.2.2: ICMP echo reply, id 37890, seq 0, length 64 13:01:03.214343 IP 192.168.2.2 > 192.168.2.10: ICMP 192.168.7.2 protocol 1 port 7282 unreachable, length 92 ...
Поэтому, чтобы на узле "ВМ Debain-10" был лишь виден IP-адрес шлюза с ip:192.168.2.2, а не IP-адресом узла-инициатора "ВМ ОС Yocto/poky" с ip:192.168.7.2, мне пришлось использовать политику MASQUERADE для интерфейса virbr1
Политика MASQUERADE делает то, что она означает – скрывает все возможное, что находится за интерфейсом virbr1, являющийся шлюзом, IP-адрес которого показан в дампе 2.8, и обеспечивающий одним обратным IP-адресом последующие за ним интерфейсы узлов подсети [3.2], в данном случае "ВМ Debain-10".
Дамп 2.8
user@debian10-uni:~$ ip route show default via 192.168.2.2 dev ens1 proto dhcp metric 100 192.168.2.0/24 dev ens1 proto kernel scope link src 192.168.2.10 metric 100
Поэтому изюминкой нашего десерта будет цепочка YP_PRT в POSTROUTE таблицы nat, которая создается с помощью iptables, как показано в дампе 2.9
Дамп 2.8
sudo iptables -t nat -N YP_PRT sudo iptables -t nat -I POSTROUTING 1 -j YP_PRT sudo iptables -t nat -I YP_PRT 1 ! -s 192.168.7.2/32 -i tap2 -j RETURN sudo iptables -t nat -I YP_PRT 2 -d 192.168.2.0/24 -o virbr1 -j MASQUERADE
Как показано в дампе 2.9, цепочка YP_PRT содержит правила разрешения ретрансляции адреса для политики MASQUERADE или ее запрета, если полученный пакет из tap2 не содержит IP-адрес источника 192.168.7.2
2.2 Реализация обратного канала
Необходима, чтобы исключить сообщение о недостяжимых эфимерных портах на узле отправителе, которое было получено ранее в дампе 2.7 (строчка выделена жирным).
Поэтому нужно реализовать пакетную фильтрацию ответного ICMP-пакета от узла "ВМ ОС Debian-10" к узлу "ВМ ОС Debian-10" через имеющийся шлюз virbr1 с использованием ранее созданных цепочек YP_ FWI и YP_ FWO, как показано на рисунке 2.9
Рисунок 2.9
Как показано на риcунке 2.9, ответный ICMP-пакет от узла "ВМ ОС Debain-10" к "ВМ ОС Yocto/poky" приходит на virbr1, который перенаправляется на tap1 в цепочке YP_ FWI с завершающий всю эту картину безусловный переход на конечную цепочку YP_FWO, правило которого показано в листинге 2.11
Листинг 2.11
YP_ FWI: 1: -A YP_FWI -s 192.168.2.10/32 -i virbr1 -o tap1 -j ACCEPT 2: -A YP_FWI -s 192.168.2.10/32 -i virbr1 -j YP_FWO 3: -A YP_FWI -s 192.168.7.0/24 -d 192.168.2.0/24 -i tap2 -j YP_FWO
Таким образом, для реализации обратного канала, чтобы восстановить обмен между узлами "ВМ ОС Debain-10" (ip:192.168.2.10) и "ВМ ОС Yocto/poky" (ip:192.168.7.2), по сути мне нужно было лишь произвести добавление (вставку) правил в цепочку YP_ FWI, обеспечивающую нужную фильтрацию (коммутацию) пакета на нужный интерфейс центрального узла гипервизора и выполнить переход на конечную цепочку YP_FWO
Дамп 2.12
user@home2:~$ sudo tcpdump -i any host 192.168.7.1 or host 192.168.7.2 or host 192.168.2.10 ... 13:07:02.960013 tap2 In IP 192.168.7.2 > 192.168.2.10: ICMP echo request, id 38914, seq 0, length 64 13:07:02.960060 virbr1 Out IP 192.168.2.2 > 192.168.2.10: ICMP echo request, id 38914, seq 0, length 64 13:07:02.960067 tap0 Out IP 192.168.2.2 > 192.168.2.10: ICMP echo request, id 38914, seq 0, length 64 13:07:02.960284 tap0 P IP 192.168.2.10 > 192.168.2.2: ICMP echo reply, id 38914, seq 0, length 64 13:07:02.960284 virbr1 In IP 192.168.2.10 > 192.168.2.2: ICMP echo reply, id 38914, seq 0, length 64 13:07:02.960298 tap1 Out IP 192.168.2.10 > 192.168.7.2: ICMP echo reply, id 38914, seq 0, length 64
Прохождение в оба конца ICMP пакетов с id 39938 в дампе 2.12 показывает, что набор правил, добавленный в цепочках YP_FWI, YP_FWO и YP_PRT, полностью обеспечивает обмен при существующих ошибках маршрутизации из-за путаницы эмулятором Qemu/KVM связки интерфесов eth0, eth1 на узле "ВМ ОС Yocto/poky" с интерфейсами tap1, tap2 на центральном узле гипервизора. При этом, в случае использования иных протоколов, основанных на UDP или TCP и прочих, потребуются цепочки PREROUTE и POSTROUTE таблицы nat, а вместе с ними политики DNAT и SNAT для ретрансляции сетевых адресов и портов соответственно. А политика MASQUERADE будет лишь маскировать IP-адрес источника на стороне центрального узла так, что будет казаться узел "ВМ Debain-10" общается только со своим шлюзом и более ни с кем, как показано в дампе 2.13
Дамп 2.13
... user@debian10-uni:~$ sudo tcpdump -n -i any host 192.168.7.1 or host 192.168.7.2 or 192.168.2.10 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes ... 13:07:02.236150 IP 192.168.2.2 > 192.168.2.10: ICMP echo request, id 38914, seq 0, length 64 13:07:02.236181 IP 192.168.2.10 > 192.168.2.2: ICMP echo reply, id 38914, seq 0, length 64
$ ∞ $
3. Библиография
3.1 Superuser.What is MASQUERADE made for?
3.2 Stackoverflow.How target RETURN for iptables work?
3.3 eSecureData Inc. Add a Static Route on CentOS
3.4 [libvirt] [PATCH RFC] network: Delay creating private chains until starting network