OpenStack Neutron Floating Ip: различия между версиями

Материал из noname.com.ua
Перейти к навигацииПерейти к поиску
Строка 2679: Строка 2679:
 
Сама программа также может отправлять пакеты через TUN/TAP устройство выполняя запись в тот же файловый дескриптор. <BR>
 
Сама программа также может отправлять пакеты через TUN/TAP устройство выполняя запись в тот же файловый дескриптор. <BR>
 
В таком случае TUN/TAP устройство доставляет (или «внедряет») такой пакет в сетевой стек операционной системы, эмулируя тем самым доставку пакета с внешнего устройства.<BR>
 
В таком случае TUN/TAP устройство доставляет (или «внедряет») такой пакет в сетевой стек операционной системы, эмулируя тем самым доставку пакета с внешнего устройства.<BR>
  +
}}
 
 
=Ссылки=
 
=Ссылки=
 
* https://arthurchiao.art/blog/ovs-deep-dive-6-internal-port/
 
* https://arthurchiao.art/blog/ovs-deep-dive-6-internal-port/

Версия 16:54, 23 апреля 2023


TODO

Этот документ похоже слишком длинный, его не удобно писать, возможно разбить на более мелкие части (?)

TL;DR

Краткий список команд


Note: "ovs-tcpdump":

Утилита ovs-tcpdump не показывает трафик на виртуальных патчкордах, требуется исслдедовать почему и как настроить показ, но предположу что требуется настроить mirroring на какой-то порт с другим типом

Как трафик попадает из внешнего мира на VM в OpenStack

Этот документ описывает как трафик из внешнего мира доходит до виртуальных машин в OpenStack (один из возможных вариантов реализации).

Описание окружения

Окружение с установленным OpenStack состоит из 3 Control Nodes и они же совмещают роль Network Node, и одной Compute Node (минимально-возможное количество)
Для получения трафика из-вне используется интерфейс с именем floating (имя может быть выбрано произвольно)


Исследование пути трафика

В окружении (для простоты) запущен один единственный инстанс, у которого Floating IP 10.72.10.124 os server list

+--------------------------------------+--------------------------+--------+----------------------------------------+------------+--------------------+
| ID                                   | Name                     | Status | Networks                               | Image      | Flavor             |
+--------------------------------------+--------------------------+--------+----------------------------------------+------------+--------------------+
| 795dc7f8-15b4-4b5d-9f8d-c40f178467f7 | test-cirros-vm-on-nvme-1 | ACTIVE | lb-mgmt-net=10.255.4.184, 10.72.10.124 | Cirros-5.1 | m1.nano.vm-on-nvme |
+--------------------------------------+--------------------------+--------+----------------------------------------+------------+--------------------+

Сеть для floating Ips (10.72.10.0/24) настроена на маршрутизаторе в Vlan 729, и этот Vlan приходит в интерфейс с именем floating на каждой из Controller/Network nodes
Шлюзом для Floating сети выступает внешний по отношению к OpenStack маршрутизатор.

С тестового ноутбука до виртуальной машины маршрут выглядит так:

Keys:  Help   Display mode   Restart statistics   Order of fields   quit
Packets               Pings
 Host             Loss%   Snt   Last   Avg  Best  Wrst StDev
 1. 192.168.33.1  0.0%     6    2.0   1.7   1.5   2.0   0.2
 2. 10.72.10.124  0.0%     5    2.2   2.1   1.7   2.4   0.3

Определени через какую Network (Control/Network) ноду пойдет трафик

Из маршрута можно сделать следующие выводы:

  • так как 192.168.33.1 -это адрес шлюза по-умолчанию для ноутбука с которого делается трассировка маршрута, а адрес VM 10.72.10.124 уже следующий в трассировке, то можно предположить что этот маршрутизатор и выступает шлюзом для floating сеть 10.72.10.0/24
  • Если это утверждение верно, то изучив таблицу arp-записей на маршрутизаторе, возможно узнать мак-адрес VM (точнее, мак-адрес с которого уходит трафик от этой VM, этот адрес будет отличаться от того который можно посмотреть командой ip link show на самой VM.



В реальной жизни в окружении клиента доступа на роутеры и коммутаторы может не быть и тогда единственный способ понять через какую Control/Network ноду идет трафик - это использовать tcpdump

Просмотр таблицы arp на маршрутизаторе

В качестве маршрутизатора в этом (тестовом!) окружении выступает устройство Mikrotik RB4011iGS+5HacQ2HnD (RouterOS 7.4)
(это не типичная инсталляция, для других случаев маршрутизатор скорее всего окажется другого производителя)



Просматриваем таблицу arp-записей (с поиском по ip=10.72.10.124)

[admin@RB4011iGS+5HacQ2HnD] > /ip/arp/print where address=10.72.10.124

Flags: D, P - PUBLISHED; C - COMPLETE
Columns: ADDRESS, MAC-ADDRESS, INTERFACE
#    ADDRESS       MAC-ADDRESS        INTERFACE
0 DC 10.72.10.124  FA:16:3E:40:32:D5  bridge-mosk-vlan-729-ch-os-fl

Из этого вывода можно видеть что

  • мак-адрес хоста <cod>10.72.10.124 известен роутеру: FA:16:3E:40:32:D5
  • этот адрес изучен на физическом интерфейсе bridge-mosk-vlan-729-ch-os-fl
  • этот интерфейс (bridge-mosk-vlan-729-ch-os-fl) и является шлюзом для floating сети, на нем назначен первый адрес 10.72.10.1/24 из этого ( 10.72.10.0/24) диапазона

/ip/address/print where interface=bridge-mosk-vlan-729-ch-os-fl

Columns: ADDRESS, NETWORK, INTERFACE
# ADDRESS        NETWORK     INTERFACE
0 10.72.10.1/24  10.72.10.0  bridge-mosk-vlan-729-ch-os-fl



  • Проверить какие еще адреса имеют мак такой же как и у адреса с которого отвечает VM:



[admin@RB4011iGS+5HacQ2HnD] > /ip/arp/print where mac-address=FA:16:3E:40:32:D5

Flags: D, P - PUBLISHED; C - COMPLETE
Columns: ADDRESS, MAC-ADDRESS, INTERFACE
#    ADDRESS       MAC-ADDRESS        INTERFACE
0 DC 10.72.10.124  FA:16:3E:40:32:D5  bridge-mosk-vlan-729-ch-os-fl
1 DC 10.72.10.27   FA:16:3E:40:32:D5  bridge-mosk-vlan-729-ch-os-fl

Тут видно что есть еще один IP с таким же маком - можно предположить что это или еще одна виртуальная машина, которая выходит через тот же роутер, или собственно адрес самого роутера.
В нашем простейшем случае, очевидно что это не может быть адресом виртуальной машины, так как в клауде запущена всего одна виртуальная машина с адресом 10.72.10.124

Note: "О роутерах в OpenStack":

Просмотреть роутеры можно командой openstack router list

+--------------------------------------+------+--------+-------+----------------------------------+-------------+-------+
| ID                                   | Name | Status | State | Project                          | Distributed | HA    |
+--------------------------------------+------+--------+-------+----------------------------------+-------------+-------+
| 54550846-33f2-4954-8649-3233a49cedb4 | r2   | ACTIVE | UP    | 93900840d7804fd2adfe9bfd452263c7 | False       | False |
| 6cf2a3cc-5242-42b5-8c57-3ee78b0d0983 | r1   | ACTIVE | UP    | 10510c89d63d490b967f90511cdc902d | True        | False |
+--------------------------------------+------+--------+-------+----------------------------------+-------------+-------+

А подробно информацию о роутере: openstack router show 54550846-33f2-4954-8649-3233a49cedb4 (вывод команды немного отформатирован для лучшей читаемости)

+-------------------------+-------------------------------------------------------------------
| Field                   | Value                                                             
+-------------------------+-------------------------------------------------------------------
| admin_state_up          | UP                                                                
| availability_zone_hints |                                                                   
| availability_zones      | nova                                                              
| created_at              | 2023-02-27T14:15:39Z                                              
| description             | router_for_nat                                                    
| distributed             | False                                                             
| external_gateway_info   | {"network_id": "5b1a5b04-b5ed-4ced-a097-585f40b0a5fd",
|                            "external_fixed_ips": [
|                               {"subnet_id": "32d92f06-a3c7-42f1-9dbc-0eee02aa47c1", "ip_address": "10.72.10.27"}
|                             ], 
|                             "enable_snat": false} 
| flavor_id               | None                                                              
| ha                      | False                                                             
| id                      | 54550846-33f2-4954-8649-3233a49cedb4                              
| interfaces_info         | [
|                              {"port_id": "e02600f1-cd39-4f1d-b1bc-c619602087b0", 
|                                "ip_address": "10.255.0.1", 
|                                "subnet_id": "4383ea14-91b6-49f9-98fa-8d6993012da2"}
|                             ]
| name                    | r2                                                                
| project_id              | 93900840d7804fd2adfe9bfd452263c7                                  
| revision_number         | 10                                                                
| routes                  |                                                                   
| status                  | ACTIVE                                                            
| tags                    |                                                                   
| updated_at              | 2023-02-28T13:10:28Z                                              
+-------------------------+---------------------------------------

Сам по себе роутер в OpenStack это НЕ виртуальная машина, а network namespace (сущность ядра Linux на Network Node)


Просмотр таблицы коммутации на top-of-rack switch

Зная мак-адрес целевой виртуальной машины, можно определить с какого порта коммутатора "виден" мак (правильно говорить: на каком порту коммутатора был изучен этот мак, но тут и далее я пользуюсь чаще сленговыми терминами)


В этой инсталляции в качестве коммутатора для всех нод используется Cisco Сatalyst WS-C3750E-48PD
Просмотреть с какого порта был изучен мак-адрес можно следующей командой: c3750e-lab#show mac address-table address fa16.3e40.32d5
(Обратите внимания, что формат показа мак-адресов у различных вендоров может отличаться, и следует это учитывать.)

          Mac Address Table
-------------------------------------------

Vlan    Mac Address       Type        Ports
----    -----------       --------    -----
 729    fa16.3e40.32d5    DYNAMIC     Gi1/0/7

Из вывода команды можно видеть что

  • Мак-адрес изучен с 7-го порта (Gi1/0/7)
  • Мак-адерс находится в 729 Vlan (это было понятно еще на роутере по имени интерфейса bridge-mosk-vlan-729-ch-os-fl, так как в его имени содержится информация vlan: 729 os: OpenStack, fl: floatiung network, но в общем случае такой информации не будет, и этот сетап - исключение, так как при настройке заведомо предполагалось упрощение дебага.



Зная номер порта, можно обратится к документации по клауду, и определить какой сервер подключен в какой порт.
(если документация недоступна то можно попробовать определить по тому какие еще мак-адреса присутствуют на порту, в других Vlan)
В этой инсталляции (где документация есть) определяем что в порт подключен к Control/Network Node c адресом 10.72.5.11

Прохождение тарфика внутри Control/Network Node

В этом сетапе - это нода 10.72.5.11 kaas-node-b4d3a72a-abe6-4dba-9547-30915084c409

Vlan 729 заходит в интерфейс с именем floating

  • это имя дано специально, в общем случае имя может отличаться
  • на этом интерфейсе уже нет никаких тегов - они снимаются раньше, другими словами tcpdump -i floating -n -ee не покажет тегированного трафика,

для данного случая не принципиально как именно снимается тег

  • на этом интерфейсе нет ip адреса
<code>ip addr  show dev floating</code>
7: floating: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master ovs-system state UP group default qlen 1000
    link/ether 52:54:15:ac:ac:16 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::5054:15ff:feac:ac16/64 scope link
       valid_lft forever preferred_lft forever
Note: "floating":

Изначально на этом интерфейсе присутствовал ip адрес - это в целом не мешало работе виртуальных машин, однако они не были доступны с этой ноды (но были доступны из сети).
Это была ошибка конфигурации, в целом назначать адреса на порты бриджа не правильно.


  • Детальная информация по интерфейсу:

ip -d link show dev floating

7: floating: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master ovs-system state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:15:ac:ac:16 brd ff:ff:ff:ff:ff:ff promiscuity 1 minmtu 68 maxmtu 65535
    openvswitch_slave addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

Тут важно что этот интерфейс включен как порт OpenVSwitch (что видно из строки openvswitch_slave ) и, соответвенно трафик попавший в этот интерфейс попадает в openvswitch

Прохождение трафика внутри OpenVSwitch

OpenVSwitch представляет собой программную реализацию OpenFlow коммутатора, (точнее в пределах одной Linux ноды можно создать несколько независимо работающих OpenFlow коммутаторов)

Note: "OpenVswitch in Docker":

В этой инсталляции процессы openvswitch работают внутри docker-контейнера, по этой причине все команды для работы с openvswitch нужно запускать оттуда:
docker exec -ti 1d8e520565ba ovs-vsctl show
однако для простоты в дальнейшем я буду опускать docker exec ...

Выяснить в какой виртуальный свитч попадает трафик

Выша было выяснено, что трафик уходит в недра OpenVSwitch, однако теперь предстоит выяснить в какой именно виртуальный коммутатора попадает трафик
Для этого просмотрим все виртуальные коммутаторы: ovs-vsctl show Часть вывода команды скрыта, полный вывод см ниже, тут оставлена только значимая часть

    Bridge br-ch-os-fl
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port br-ch-os-fl
            Interface br-ch-os-fl
                type: internal
        Port phy-br-ch-os-fl
            Interface phy-br-ch-os-fl
                type: patch
                options: {peer=int-br-ch-os-fl}
        Port floating
            Interface floating

Тут ключевые моменты такие

  • Виртуальный коммутатор br-ch-os-fl, одним из портов (Port floating) которого и является интерфейс Interface floating является тем коммутатором куда попадает трафик
  • Этот коммутатор подключен к контроллеру, is_connected: true
  • Кроме порта через который в него попадает внешний трафик (floating), у него есть еще 2 порта:
    • Port phy-br-ch-os-flЭто порт имеет тип type: internal что означает что он виден в системе и на него можно назначить адрес (прочитать подробнее можно тут: https://arthurchiao.art/blog/ovs-deep-dive-6-internal-port/
    • Port br-ch-os-fl Этот порт имеет тип type: patch и предназначен для соединения нескольких виртуальных коммутаторов между собой, второй конец этого виртуального патч-корда имеет имя int-br-ch-os-fl. В целом, это напоминает работу интерфейса veth

Прохождение трафика через виртуальный коммутатор br-ch-os-fl

Итак, трафик попал в виртуальный коммутатор br-ch-os-fl через физический интерфейс floating
(он же порт коммутатора с таким же именем floating)
В отличии от "классических" коммутаторов, которые осуществляют пересылку пакетов основываясь на изученной таблице мак-адресов,
OpenFlow-коммутаторы (которыми и является виртуальный коммутатор br-ch-os-fl) используют для определения порта назначения OpenFlow-правила
Для просмотра правил в случае OpenVSwitch можно использовать команду ovs-ofctl dump-flows br-ch-os-fl

  • br-ch-os-fl - это имя виртуального коммутатора, которых может быть более чем один на одной Linux-ноде.


Результат работы команды

 cookie=0x2d19cd3395437eb3, duration=691002.184s, table=0, n_packets=106295, n_bytes=10732118, priority=2,in_port="phy-br-ch-os-fl"           actions=resubmit(,1)
 cookie=0x2d19cd3395437eb3, duration=691222.458s, table=0, n_packets=186,    n_bytes=11672,    priority=0                                     actions=NORMAL
 cookie=0x2d19cd3395437eb3, duration=691002.076s, table=0, n_packets=777807, n_bytes=48652600, priority=1                                     actions=resubmit(,3)
 cookie=0x2d19cd3395437eb3, duration=691001.975s, table=1, n_packets=106295, n_bytes=10732118, priority=0                                     actions=resubmit(,2)
 cookie=0x2d19cd3395437eb3, duration=690851.627s, table=2, n_packets=105853, n_bytes=10694426, priority=4,in_port="phy-br-ch-os-fl",dl_vlan=2 actions=strip_vlan,NORMAL
 cookie=0x2d19cd3395437eb3, duration=691001.628s, table=2, n_packets=442,    n_bytes=37692,    priority=2,in_port="phy-br-ch-os-fl"           actions=drop
 cookie=0x2d19cd3395437eb3, duration=690999.407s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:0e:d7:45            actions=output:"phy-br-ch-os-fl"
 cookie=0x2d19cd3395437eb3, duration=690999.335s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:64:4e:1f            actions=output:"phy-br-ch-os-fl"
 cookie=0x2d19cd3395437eb3, duration=690999.291s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:96:cd:a5            actions=output:"phy-br-ch-os-fl"
 cookie=0x2d19cd3395437eb3, duration=690999.111s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:a0:1e:1b            actions=output:"phy-br-ch-os-fl"
 cookie=0x2d19cd3395437eb3, duration=690998.887s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:f9:d4:00            actions=output:"phy-br-ch-os-fl"
 cookie=0x2d19cd3395437eb3, duration=691001.561s, table=3, n_packets=777807, n_bytes=48652600, priority=1                                     actions=NORMAL

Правила просамтриваются в следующем порядке

  • таблицы по номерам, первая таблица - с номером 0, если трафик не соответвует ни одному правилу то он отбрасывается
  • Правила внутри таблицы просматриваются в порядке приоритетов, от большего к меньшему, первое правило с номером 65535
  • При равном приортете правила просматриваются в том порядке в котором добавлены (c верху вниз)


Для лучшего понимания, отсортируем правила в то порядке в котором они будут работать (с верху вниз, верхнее - первое)

Таблица 0

 cookie=0x2d19cd3395437eb3, duration=691002.184s, table=0, n_packets=106295, n_bytes=10732118, priority=2,in_port="phy-br-ch-os-fl"           actions=resubmit(,1)
 cookie=0x2d19cd3395437eb3, duration=691002.076s, table=0, n_packets=777807, n_bytes=48652600, priority=1                                     actions=resubmit(,3)
 cookie=0x2d19cd3395437eb3, duration=691222.458s, table=0, n_packets=186,    n_bytes=11672,    priority=0                                     actions=NORMAL

Таблица 1

 cookie=0x2d19cd3395437eb3, duration=691001.975s, table=1, n_packets=106295, n_bytes=10732118, priority=0                                     actions=resubmit(,2)

Таблица 2

 cookie=0x2d19cd3395437eb3, duration=690851.627s, table=2, n_packets=105853, n_bytes=10694426, priority=4,in_port="phy-br-ch-os-fl",dl_vlan=2 actions=strip_vlan,NORMAL
 cookie=0x2d19cd3395437eb3, duration=691001.628s, table=2, n_packets=442,    n_bytes=37692,    priority=2,in_port="phy-br-ch-os-fl"           actions=drop

Таблица 3

 cookie=0x2d19cd3395437eb3, duration=690999.407s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:0e:d7:45            actions=output:"phy-br-ch-os-fl"
 cookie=0x2d19cd3395437eb3, duration=690999.335s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:64:4e:1f            actions=output:"phy-br-ch-os-fl"
 cookie=0x2d19cd3395437eb3, duration=690999.291s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:96:cd:a5            actions=output:"phy-br-ch-os-fl"
 cookie=0x2d19cd3395437eb3, duration=690999.111s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:a0:1e:1b            actions=output:"phy-br-ch-os-fl"
 cookie=0x2d19cd3395437eb3, duration=690998.887s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:f9:d4:00            actions=output:"phy-br-ch-os-fl"
 cookie=0x2d19cd3395437eb3, duration=691001.561s, table=3, n_packets=777807, n_bytes=48652600, priority=1                                     actions=NORMAL

table 0 коммутатора br-ch-os-fl

Правила table 0

table=0, priority=2,in_port="phy-br-ch-os-fl" actions=resubmit(,1)


  • Правило означает: все пакеты с порта phy-br-ch-os-fl, исследуемый трафик в это правило не попадает так как приходит с порта floating
 cookie=0x2d19cd3395437eb3, duration=691002.184s, table=0, n_packets=106295, n_bytes=10732118, priority=2,in_port="phy-br-ch-os-fl"           actions=resubmit(,1)

table=0, priority=1 actions=resubmit(,3)

  • Правило означает: все пакеты (так как не указано никакое условие) и именно оно будет применено к исследуемому трафику, действие в нет - перейти в таблицу 3, это действие описывает actions=resubmit(,3)
cookie=0x2d19cd3395437eb3, duration=691002.076s, table=0, n_packets=777807, n_bytes=48652600, priority=1                                     actions=resubmit(,3)

table=0, priority=0 actions=NORMAL

  • Это правило никогда не сработает, но если по какой-то причине предыдущее правило будет отсутствовать, то это правило заставит сделать коммутацию как классический свитч, используя таблицу ма-адресов.
 cookie=0x2d19cd3395437eb3, duration=691222.458s, table=0, n_packets=186,    n_bytes=11672,    priority=0                                     actions=NORMAL

table 3 коммутатора br-ch-os-fl

Согласно правилу в таблице 0, исследуемый трафик отправлен в таблицу 3
В этой таблице много похожих правил, все они означают что при совпадении мак-адресов отправить пакет в порт phy-br-ch-os-fl
и ни один из мак-адресов не является мак-адресом трафика адресованного виртуальной машине

В случае когда трафик к виртуальной машине идет из-за пределов OpenStack (это и есть рассматриваемый случай), то мак-адресом отправителя таких пакетов будет мак-адрес маршрутизатора,
того интерфейса который смотрит в floating сеть (в этом сетапе это VLAN729)
Просмотреть мак-адрес в случае микротика можно в настройках интерфейса

/interface/bridge/print where name=bridge-mosk-vlan-729-ch-os-fl
Flags: X - disabled, R - running
 9 R name="bridge-mosk-vlan-729-ch-os-fl" mtu=auto actual-mtu=1500 l2mtu=1588 arp=enabled arp-timeout=auto mac-address=00:02:2D:AA:BB:02 protocol-mode=none fast-forward=yes igmp-snooping=no auto-mac=no admin-mac=00:02:2D:AA:BB:02 ageing-time=5m
     vlan-filtering=no dhcp-snooping=no

и тут видно что admin-mac=00:02:2D:AA:BB:02 не совпадает ни с одним из правил (все кроме первого правила спрятаны под спойлер), из чего делаем вывод что отрабатывает только последнее правило.

 cookie=0x2d19cd3395437eb3, duration=690999.407s, table=3, n_packets=0,      n_bytes=0,        priority=2,dl_src=fa:16:3f:0e:d7:45            actions=output:"phy-br-ch-os-fl"

Последнее правило имеет actions=NORMAL, это значит что дальнейшая коммутация будет осуществляться классическим способом, используя таблицу коммутации по мак-адресам,
и значит нужно проверить содержание этой таблицы.

 cookie=0x2d19cd3395437eb3, duration=691001.561s, table=3, n_packets=777807, n_bytes=48652600, priority=1                                     actions=NORMAL

Forwarding Database(таблица мак-адресов) коммутатора br-ch-os-fl

Таблицу Forwarding Database (FDB, таблица мак-адресов) на OpenVSwitch можно просмотреть командой:
ovs-appctl fdb/show br-ch-os-fl

 port  VLAN  MAC                Age
    1     0  6a:7a:ba:bc:8b:26  219
    1     0  d4:ca:6d:7c:a6:5c  199
    1     0  52:54:15:aa:aa:16   24
    1     0  52:54:15:ff:ff:16   23
    1     0  00:02:2d:aa:bb:02    1
    2     0  fa:16:3e:40:32:d5    1

Из этой таблицы видно, что целевой мак (fa:16:3e:40:32:d5) в ней присутствует и изучен с порта номер 2,
а все остальные маки изучены с порта номер 1 (в том числе и мак маршрутизатора, 00:02:2d:aa:bb:02 )

Однако, вывод ovs-vsctl show не дает информации о номерах портов, а только их имена:

    Bridge br-ch-os-fl
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port br-ch-os-fl
            Interface br-ch-os-fl
                type: internal
        Port phy-br-ch-os-fl
            Interface phy-br-ch-os-fl
                type: patch
                options: {peer=int-br-ch-os-fl}
        Port floating
            Interface floating

Далее требуется сопоставить номера портов виртуального коммутатора br-ch-os-fl и их имена

Сопоставление номеров и имен портов для виртуального коммутатора br-ch-os-fl

Просмотреть нумерацию портов (внутри одного из многих коммутаторов!) можно командой
ovs-ofctl -OOpenFlow13 show br-ch-os-fl

  • br-ch-os-fl - имя коммутатора, для которого хочется посмотреть порты
OFPT_FEATURES_REPLY (OF1.3) (xid=0x2): dpid:0000ba2f0edece4e
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS GROUP_STATS QUEUE_STATS
OFPST_PORT_DESC reply (OF1.3) (xid=0x3):
 1(floating): addr:52:54:15:ac:ac:16
     config:     0
     state:      LIVE
     speed: 0 Mbps now, 0 Mbps max
 2(phy-br-ch-os-fl): addr:96:e8:e7:04:ec:d9
     config:     0
     state:      LIVE
     speed: 0 Mbps now, 0 Mbps max
 LOCAL(br-ch-os-fl): addr:ba:2f:0e:de:ce:4e
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
OFPT_GET_CONFIG_REPLY (OF1.3) (xid=0x9): frags=normal miss_send_len=0

Из вывода команды видно, что порт номер 2 это порт phy-br-ch-os-fl, который имеет состояние LIVE


из вывода команды ovs-vsctl show (оставлена только значимая часть) можно видеть что порт 2, он же phy-br-ch-os-fl,
имеет тип type: patch, и второй конец "виртуального патч-корда" имеет имя int-br-ch-os-fl (options: {peer=int-br-ch-os-fl})

...
        Port phy-br-ch-os-fl
            Interface phy-br-ch-os-fl
                type: patch
                options: {peer=int-br-ch-os-fl}
...

TODO (открытые вопросы):
Пока не ясно для чего служит

 LOCAL(br-ch-os-fl): addr:ba:2f:0e:de:ce:4e
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max

Этот же интерфейс, с таким же маком (ba:2f:0e:de:ce:4e) и состоянием ( LINK_DOWN) виден в системе:
ip link show dev br-ch-os-fl

77: br-ch-os-fl: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether ba:2f:0e:de:ce:4e brd ff:ff:ff:ff:ff:ff

Определение куда попадает трафик через виртуальный патчк-корд phy-br-ch-os-fl

Для того что бы понть куда трафик попадает через виртуальный патч-корд phy-br-ch-os-fl, нужно вернуться назад к выводу команды ovs-vsctl show
В части вывода, касающейся коммутатора br-int можно видеть, что вторая часть патч-корда (peer=int-br-ch-os-fl) является портом коммутатора br-int

   Bridge br-int
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}
        Port qr-e02600f1-cd
            tag: 1
            Interface qr-e02600f1-cd
                type: internal
        Port int-br-ch-os-fl
            Interface int-br-ch-os-fl
                type: patch
                options: {peer=phy-br-ch-os-fl}
        Port qg-aefc8a2e-98
            tag: 2
            Interface qg-aefc8a2e-98
                type: internal
        Port br-int
            Interface br-int
                type: internal
        Port qg-932d2f98-d2
            tag: 2
            Interface qg-932d2f98-d2
                type: internal
        Port o-hm0
            tag: 1
            Interface o-hm0
                type: internal
        Port fg-ecf7bdaf-ff
            tag: 2
            Interface fg-ecf7bdaf-ff
                type: internal

Вот описание порта через который трафик попадет в коммутатор br-int - это (ожидаемо) виртуальный патч-корд, второй конец которого - phy-br-ch-os-fl включен в коммутатор br-ch-os-fl

        Port int-br-ch-os-fl
            Interface int-br-ch-os-fl
                type: patch
                options: {peer=phy-br-ch-os-fl}

Прохождение трафика через виртуальный коммутатор br-int

Как выяснили, трафик попадает в коммутатор br-int через порт int-br-ch-os-fl

        Port int-br-ch-os-fl
            Interface int-br-ch-os-fl
                type: patch
                options: {peer=phy-br-ch-os-fl}

Так-как коммутатор br-int так же являетя OpenFlow коммутатором, то для понимания пути прохождения трафика нужно просмотреть правила OpenFlow
Просмотреть правила можно командой:
ovs-ofctl dump-flows br-int

Полный вывод достаточно большой, скрыт под спойлер, для анализа используем сокращенный вывод.

В целом логика такая же как и при прохождении трафика br-ch-os-fl

Напомню, чтот правила просматриваются по таблицам, сначала таблица 0, потом 1 и т.д, а правила внутри таблицы просматриваются в порядке приоритета, от большего к меньшему
Далее везде используется вывод уже отсортированный для удобства просмотра.
Поля cookie, duration, n_packets n_bytes удалены для удобства, они не влияют на результат работы правила.

table 0 коммутатора br-int

table=0 priority=65535,dl_vlan=4095

Этому правилу (ниже) соответствует трафик с VLAN_ID=4095, рассматриваемый сейчас трафик тэга не имеет, значит правило не срабатывает на него.

table=0   priority=65535,dl_vlan=4095                                                                                                  actions=drop

table=0 priority=200,reg3=0

Это правило (ниже) вызывает у меня затруднение, reg3=0 это обращение к 3-му регистру, но я пока не могу точно сказать как работает это расширение протокола OpenFlow
Судя по всему, это правило следует читать так

Если у пакета регистр3=0 (что касается всех пакетов, так как этот регистр - это внутреннее свойство пакета, а не поле по какому-либо смещению, и не существует за пределами OpenVSwitch), то:

  • set_queue:0,load Установить очередь 0 (TODO: Добавить пояснения)
  • 0x1->NXM_NX_REG3[0] Установить (загрузить в регистр) значение 0x1 в 0 байт регистра 3
  • resubmit(,0) Перенаправить пакет на вход таблицы 0

При повторном прохождении пакета это правило более не срабатывает, так как reg3 не равен более 0, а равен 0x1

table=0   priority=200,reg3=0                                                                                                          actions=set_queue:0,load:0x1->NXM_NX_REG3[0],resubmit(,0)



table=0 priority=5,in_port="int-br-ch-os-fl",dl_dst=fa:16:3f:e0:b7:4b actions=resubmit(,4)

Это правило (ниже) не срабатывает так-как не совпадает мак получателя (мак получателя FA:16:3E:40:32:D5не совпадает с маком fa:16:3f:e0:b7:4b из правила)

table=0   priority=5,in_port="int-br-ch-os-fl",dl_dst=fa:16:3f:e0:b7:4b                                                                actions=resubmit(,4)

Остальные не сработавшие но полностью аналогичные правила спрятаны под спойлер, мак-адреса тоже не совпадают.

table=0 priority=3,in_port="int-br-ch-os-fl",vlan_tci=0x0000/0x1fff actions=mod_vlan_vid:2,resubmit(,60)

Это правило следует читать как

  • Пакеты с порта int-br-ch-os-fl
  • У которых не установлен VlanID

Чуть подробнее остановлюсь на условии vlan_tci=0x0000/0x1fff
Прочитать можно тут: ovs-fields.7.txt


Правило читается так:

  • взять поле vlan_tci (это внутренняя структура у OpenVSwitchи других OpenFlow свитчей, а не поле в пакете!)
  • наложить на него битовую маску 0x1fff (что в двоичной форме соответствует 0001111111111111
  • результат сравнить с 0x0000


Накладывание битовой маски - по сути операция умножения, другим словами это означает что первые три бита в исходном пакет могут быть любыми (после умножения на 0 они будут равны 0),
а остальные должны уже быть равны нулю в исходном пакете (умножение на 1 не изменяет их значения)

В описании поля vlan_tci видно, что первые 3 бита отведены под PCP (Priority Control Point)

        NXM_VLAN_TCI
        <---------->
         3   1   12
       +----+--+----+
       |PCP |P |VID |
       +----+--+----+




Другими словами, это правило означает что

  • Бит присутствия VLAN (CFI, Canonical Format Indicator) должен быть выставлен в 0
  • Поле VLAN ID выставлено в 0 (все биты установлены в 0)
  • значение поля приоритета игнорируетс

В сумме это правило значит "нетэгированный пакет с порта int-br-ch-os-fl" куда и попадает искомый трафик - и он далее передается в таблицу 60 после того как на него будет добавлен тег 2 (mod_vlan_vid:2)

table=0   priority=3,in_port="int-br-ch-os-fl",vlan_tci=0x0000/0x1fff                                                                  actions=mod_vlan_vid:2,resubmit(,60)

table=0 priority=2,in_port="int-br-ch-os-fl" actions=drop

Это правило значит что все что не попало под предыдущее правило (другими словами пакеты с тегом) нужно дропнуть

table=0   priority=2,in_port="int-br-ch-os-fl"                                                                                         actions=drop


table=0 priority=0 actions=resubmit(,60)

Это правило безусловно все остальные пакеты передаст в таблицу 60, не модифицируя их

table=0   priority=0                                                                                                                   actions=resubmit(,60)

table 60 коммутатора br-int

Полный список правил (вывод не содержит не значазих полей)

table=60  priority=100,in_port="o-hm0"                    actions=load:0x3->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],resubmit(,71)
table=60  priority=100,in_port="qg-aefc8a2e-98"           actions=load:0x6->NXM_NX_REG5[],load:0x2->NXM_NX_REG6[],resubmit(,73)
table=60  priority=100,in_port="qg-932d2f98-d2"           actions=load:0x7->NXM_NX_REG5[],load:0x2->NXM_NX_REG6[],resubmit(,73)
table=60  priority=100,in_port="fg-ecf7bdaf-ff"           actions=load:0x5->NXM_NX_REG5[],load:0x2->NXM_NX_REG6[],resubmit(,73)
table=60  priority=100,in_port="qr-e02600f1-cd"           actions=load:0x4->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],resubmit(,73)
table=60  priority=90,dl_vlan=1,dl_dst=fa:16:3e:0c:e4:73  actions=load:0x3->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],strip_vlan,resubmit(,81)
table=60  priority=3                                      actions=NORMAL

Тут видно что все правила, кроме последнего, имеют условия не соответствующие исследуемому трафику - отличия в поле in_port, vlan или мак
Соответственно срабатывает третье правило (actions=NORMAL) и далее для коммутации трафика используется классическаая таблица мак-адресов

Таблица мак-адресов коммутатора br-int

Далее для проверки куда отправить пакет с маком назначения fa:16:3e:40:32:d5 нужно проверить содержимое таблицы коммутации (FDB, Forwarding Database)

ovs-appctl fdb/show br-int

 port  VLAN  MAC                Age
    1     2  52:54:15:aa:aa:16  119
    1     2  52:54:15:ff:ff:16   38
    1     2  00:02:2d:aa:bb:02    0
    5     2  fa:16:3e:40:32:d5    0

Тут видно что мак fa:16:3e:40:32:d5 изучен с порта номер 5
Для того что бы выяснить какой именно порт имеет номер 5, просмотреть номера портов

ovs-ofctl -OOpenFlow13 show br-int

OFPT_FEATURES_REPLY (OF1.3) (xid=0x2): dpid:0000ae845ebd9a43
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS GROUP_STATS QUEUE_STATS
OFPST_PORT_DESC reply (OF1.3) (xid=0x3):
 1(int-br-ch-os-fl): addr:f6:69:e1:c0:39:3c
     config:     0
     state:      LIVE
     speed: 0 Mbps now, 0 Mbps max
 2(patch-tun): addr:16:6a:e5:70:65:5c
     config:     0
     state:      LIVE
     speed: 0 Mbps now, 0 Mbps max
 3(o-hm0): addr:fa:16:3e:42:68:31
     config:     0
     state:      LIVE
     speed: 0 Mbps now, 0 Mbps max
 4(qr-e02600f1-cd): addr:00:00:00:00:00:00
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
 5(qg-aefc8a2e-98): addr:00:00:00:00:00:00
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
 6(fg-deee2f23-09): addr:00:00:00:00:00:00
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
 7(qg-932d2f98-d2): addr:00:00:00:00:00:00
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
 8(tapb9b0014b-e1): addr:00:00:00:00:00:00
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
 LOCAL(br-int): addr:ae:84:5e:bd:9a:43
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
OFPT_GET_CONFIG_REPLY (OF1.3) (xid=0x9): frags=normal miss_send_len=0

Тут видно что номеру 5 соответствует интерфейс qg-aefc8a2e-98

 5(qg-aefc8a2e-98): addr:00:00:00:00:00:00
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max

ovs-vsctl show показывает что

  • это интерфейс типа internal и это означает что он должен быть виден в системе командой ip link show ...
  • этот интерфейс имеет VLAN (tag: 2), что (предположительно) соответствует access port 2, при отправки на этот порт будет снят тег, а при получении - добавлен тег 2
  • тег 2 совпадает с тегом которым тегирован исследуемый трафик
        Port qg-932d2f98-d2
            tag: 2
            Interface qg-932d2f98-d2
                type: internal
</code>
Однако "в лоб" интерфейс в системе не виден <BR>
<code>ip link show dev qg-aefc8a2e-98</code>
<PRE>
Device "qg-aefc8a2e-98" does not exist.

Это связано с тем что он находится внутри network namespace
Дальнейшим прохождением трафика занимается уже не OpenVSwitch, а сетевая подсистема Linux

Note: "Почему Port Down?":

Можно было увидеть: config: PORT_DOWN, state: LINK_DOWN

 5(qg-aefc8a2e-98): addr:00:00:00:00:00:00
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max

Могу предположить что OpenVSWitch не может определить статус интерфейса, если он находится в network namespace отличном от "умолчального" (в терминах Cisco это называется VRF Global)

Прохождение трафика внутри Network Node

Первым делом следует определить в какой именно network namespace помещен интерфейс (qg-aefc8a2e-98 - имя интерфейса и оно же имя порта openvswitch)

Определение Network Namespace в который попадает трафик

ip netns list

qdhcp-bd7e52d9-e09c-4d43-8816-db1be731a761 (id: 50)
fip-5b1a5b04-b5ed-4ced-a097-585f40b0a5fd (id: 29)
snat-6cf2a3cc-5242-42b5-8c57-3ee78b0d0983 (id: 49)
qrouter-6cf2a3cc-5242-42b5-8c57-3ee78b0d0983
qrouter-54550846-33f2-4954-8649-3233a49cedb4 (id: 14)

Для того что бы не перебирать все неймспейсы руками: for NET_NS_NAME in $(ip netns list | awk '{print $1}'); do ip netns exec ${NET_NS_NAME} ip -s -d link show dev qg-aefc8a2e-98 2>/dev/null && echo ${NET_NS_NAME} && break; done

83: qg-aefc8a2e-98: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether fa:16:3e:40:32:d5 brd ff:ff:ff:ff:ff:ff promiscuity 1 minmtu 68 maxmtu 65535
    openvswitch addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    RX: bytes  packets  errors  dropped overrun mcast
    373294     7318     0       0       0       0
    TX: bytes  packets  errors  dropped carrier collsns
    198624     1955     0       0       0       0

qrouter-54550846-33f2-4954-8649-3233a49cedb4


Из вывода видно, что интерфейс это порт openvswitch,
а искомый Network Namespace - qrouter-54550846-33f2-4954-8649-3233a49cedb4


Note: "В поисках неймспейса":

На самом деле можно было предположить что это какой-то роутер openstack router list

+--------------------------------------+------+--------+-------+----------------------------------+-------------+-------+
| ID                                   | Name | Status | State | Project                          | Distributed | HA    |
+--------------------------------------+------+--------+-------+----------------------------------+-------------+-------+
| 54550846-33f2-4954-8649-3233a49cedb4 | r2   | ACTIVE | UP    | 93900840d7804fd2adfe9bfd452263c7 | False       | False |
...

и тут видно что ID роутера совпадает с именем неймспейса
Конечно когда роутеров много - все может оказаться сложнее


qrouter-54550846-33f2-4954-8649-3233a49cedb4

Далее запускаем шелл (отключив буфферизацию ввода и вывода и исследуем настройки сети внутри неймспейса) ip netns exec qrouter-54550846-33f2-4954-8649-3233a49cedb4 stdbuf -i0 -o0 -e0 bash

Note: "stdbuf": Без отключение буферизации например tcpdump выдает на экран пакеты не сразу, а пачками. Что может сильно мешать дебагу

Причины такого поведения в неймспейсе я пока не исследовал

Просмотрим базовые настойки сетевого стека:

ip -d link show

ip -d link show

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 minmtu 0 maxmtu 0 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
82: qr-e02600f1-cd: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1430 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether fa:16:3e:69:cd:07 brd ff:ff:ff:ff:ff:ff promiscuity 1 minmtu 68 maxmtu 65535
    openvswitch addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
83: qg-aefc8a2e-98: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether fa:16:3e:40:32:d5 brd ff:ff:ff:ff:ff:ff promiscuity 1 minmtu 68 maxmtu 65535
    openvswitch addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

Из вывода можно увидеть, что тут присутвуют 2 интерфейса, оба являются портами OpenVSwitch:

  • qg-aefc8a2e-98 - интерфейс через который приходит трафик
  • qr-e02600f1-cd - еще один интерфейс, назначение которого пока не известно (но можно предположить что он смотрит во внутреннюю сеть тенанта)

Роутер выглядит как классический, с 1 портом WAN - qg-aefc8a2e-98 и одним портом LAN - qr-e02600f1-cd

ip -d addr show

ip -d addr show

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 promiscuity 0 minmtu 0 maxmtu 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
82: qr-e02600f1-cd: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1430 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether fa:16:3e:69:cd:07 brd ff:ff:ff:ff:ff:ff promiscuity 1 minmtu 68 maxmtu 65535
    openvswitch numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 10.255.0.1/16 brd 10.255.255.255 scope global qr-e02600f1-cd
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe69:cd07/64 scope link
       valid_lft forever preferred_lft forever
83: qg-aefc8a2e-98: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether fa:16:3e:40:32:d5 brd ff:ff:ff:ff:ff:ff promiscuity 1 minmtu 68 maxmtu 65535
    openvswitch numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
    inet 10.72.10.27/24 brd 10.72.10.255 scope global qg-aefc8a2e-98
       valid_lft forever preferred_lft forever
    inet 10.72.10.124/32 brd 10.72.10.124 scope global qg-aefc8a2e-98
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fe40:32d5/64 scope link
       valid_lft forever preferred_lft forever


У роутера 3 IP адреса (за исклбючением lo который я нигде отдельно не описываю)

  • на интерфейсе qg-aefc8a2e-98
    • 10.255.0.1/16 - это адрес на интерфейсе в тенантную сеть и он выступает шлюзом для ВМ в этой сети
  • на интерфейсе qg-aefc8a2e-98
    • 10.72.10.27/24 - Floating IP роутера (кстати зачем он ему?)
    • 10.72.10.124/32 - Floating IP назначеный на ВМ

Так как Floating адрес виртуальной машины назначин на интерфейс qg-aefc8a2e-98, который как рассмотрели выше, средствами OpenVSwitch,
через виртуальные коммутаторы соединен с сетью floating (Vlan729 на физическом коммутаторе), то становится понятно как происходит изучение ARP -
сетевой стек линукса Network Node отвечает на ARP-запросы на адрес 10.72.10.124, что является поведением по-умолчанию.

Никакая "магия" proxy_arp здесь не используется

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

ip route

ip route

default via 10.72.10.1 dev qg-aefc8a2e-98 proto static
10.72.10.0/24 dev qg-aefc8a2e-98 proto kernel scope link src 10.72.10.27
10.255.0.0/16 dev qr-e02600f1-cd proto kernel scope link src 10.255.0.1

Таблица маршрутизации соответствует ожиданиям и максимально проста - по одному маршруту в каждый из сегментов сети, и маршрут по-умолчанию.

arp -n

arp -n

arp -n
Address                  HWtype  HWaddress           Flags Mask            Iface
10.255.4.184             ether   fa:16:3e:ea:49:b7   C                     qr-e02600f1-cd
10.72.10.1               ether   00:02:2d:aa:bb:02   C                     qg-aefc8a2e-98

Таблица ARP тоже полностью соответвует ожиданиям - 2 записи, шлюза во внешний мир и виртуальной машины.

ip route get 10.72.10.124

ip route get 10.72.10.124

ip route get 10.72.10.124
local 10.72.10.124 dev lo src 10.72.10.124 uid 0
    cache <local>

Результат соответвует ожиданиям - так как адрес 10.72.10.124 назначен на один из интерфейса маршрутизатора, то трафик адресованный этому адресу (тавтология?) будет обрабатываться как локальный.

iptables-save

iptables-save

*raw

# Generated by iptables-save v1.8.4 on Mon Apr 17 11:54:58 2023
*raw
:PREROUTING ACCEPT [2580:169942]
:OUTPUT ACCEPT [2537:233396]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-l3-agent-OUTPUT
COMMIT
# Completed on Mon Apr 17 11:54:58 2023
# Generated by iptables-save v1.8.4 on Mon Apr 17 11:54:58 2023

*nat

*nat
:PREROUTING ACCEPT [11:2391]
:INPUT ACCEPT [11:2391]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [2:128]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-POSTROUTING - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
:neutron-l3-agent-float-snat - [0:0]
:neutron-l3-agent-snat - [0:0]
:neutron-postrouting-bottom - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-l3-agent-POSTROUTING
-A POSTROUTING -j neutron-postrouting-bottom
-A neutron-l3-agent-OUTPUT -d 10.72.10.124/32 -j DNAT --to-destination 10.255.4.184
-A neutron-l3-agent-POSTROUTING ! -o qg-aefc8a2e-98 -m conntrack ! --ctstate DNAT -j ACCEPT
-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -i qr-+ -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 9697
-A neutron-l3-agent-PREROUTING -d 10.72.10.124/32 -j DNAT --to-destination 10.255.4.184
-A neutron-l3-agent-float-snat -s 10.255.4.184/32 -j SNAT --to-source 10.72.10.124 --random-fully
-A neutron-l3-agent-snat -j neutron-l3-agent-float-snat
-A neutron-l3-agent-snat -m mark ! --mark 0x2/0xffff -m conntrack --ctstate DNAT -j SNAT --to-source 10.72.10.27 --random-fully
-A neutron-postrouting-bottom -m comment --comment "Perform source NAT on outgoing traffic." -j neutron-l3-agent-snat
COMMIT
# Completed on Mon Apr 17 11:54:58 2023
# Generated by iptables-save v1.8.4 on Mon Apr 17 11:54:58 2023

*mangle

:PREROUTING ACCEPT [2580:169942]
:INPUT ACCEPT [12:2739]
:FORWARD ACCEPT [32:4899]
:OUTPUT ACCEPT [2537:233396]
:POSTROUTING ACCEPT [2569:238295]
:neutron-l3-agent-FORWARD - [0:0]
:neutron-l3-agent-INPUT - [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-POSTROUTING - [0:0]
:neutron-l3-agent-PREROUTING - [0:0]
:neutron-l3-agent-float-snat - [0:0]
:neutron-l3-agent-floatingip - [0:0]
:neutron-l3-agent-mark - [0:0]
:neutron-l3-agent-scope - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING
-A INPUT -j neutron-l3-agent-INPUT
-A FORWARD -j neutron-l3-agent-FORWARD
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A POSTROUTING -j neutron-l3-agent-POSTROUTING
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-mark
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-scope
-A neutron-l3-agent-PREROUTING -m connmark ! --mark 0x0/0xffff0000 -j CONNMARK --restore-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-floatingip
-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -i qr-+ -p tcp -m tcp --dport 80 -j MARK --set-xmark 0x1/0xffff
-A neutron-l3-agent-float-snat -m connmark --mark 0x0/0xffff0000 -j CONNMARK --save-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-mark -i qg-aefc8a2e-98 -j MARK --set-xmark 0x2/0xffff
-A neutron-l3-agent-scope -i qr-e02600f1-cd -j MARK --set-xmark 0x4000000/0xffff0000
-A neutron-l3-agent-scope -i qg-aefc8a2e-98 -j MARK --set-xmark 0x4000000/0xffff0000
COMMIT
# Completed on Mon Apr 17 11:54:58 2023
# Generated by iptables-save v1.8.4 on Mon Apr 17 11:54:58 2023

*filter

:INPUT ACCEPT [12:2739]
:FORWARD ACCEPT [32:4899]
:OUTPUT ACCEPT [2537:233396]
:neutron-filter-top - [0:0]
:neutron-l3-agent-FORWARD - [0:0]
:neutron-l3-agent-INPUT - [0:0]
:neutron-l3-agent-OUTPUT - [0:0]
:neutron-l3-agent-local - [0:0]
:neutron-l3-agent-scope - [0:0]
-A INPUT -j neutron-l3-agent-INPUT
-A FORWARD -j neutron-filter-top
-A FORWARD -j neutron-l3-agent-FORWARD
-A OUTPUT -j neutron-filter-top
-A OUTPUT -j neutron-l3-agent-OUTPUT
-A neutron-filter-top -j neutron-l3-agent-local
-A neutron-l3-agent-FORWARD -j neutron-l3-agent-scope
-A neutron-l3-agent-INPUT -m mark --mark 0x1/0xffff -j ACCEPT
-A neutron-l3-agent-INPUT -p tcp -m tcp --dport 9697 -j DROP
-A neutron-l3-agent-scope -o qr-e02600f1-cd -m mark ! --mark 0x4000000/0xffff0000 -j DROP
-A neutron-l3-agent-scope -o qg-aefc8a2e-98 -m mark ! --mark 0x4000000/0xffff0000 -j DROP
COMMIT
# Completed on Mon Apr 17 11:54:58 2023


Разбор правил IPTABLES

На первый взгляд правил довольно много
Для понимания в каком порядке происходит прохождения пакета - прикрепляю классическую 'iptables-big-picture' (так она и назодится гуглом)
Netfilter-packet-flow.svg.png]

Из этой схемы становится более понятно в каком порядке какие таблицы просматривать.
eBPF/qdisc (шейпера) пропускаем. Считаем что интерфейс не является бриджом (в классическом смысле - Linux Bridge, проверить brctl show, которая показывает что бриджей нет)


Порядок прохождения пакетов

Порядок прохождения пакетов
Имя таблицы Имя Цепочки Примечание Примечание 2
raw PREROUTING
*raw
:PREROUTING ACCEPT [2580:169942]
:neutron-l3-agent-PREROUTING - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING

В таблице raw в цепочке PREROUTING только одно правило, пересылает пакеты в цепочку neutron-l3-agent-PREROUTING,
в которой нет ни одного правила, и как следствие к пакетам применяется правило по-умолчанию для
цепочки :PREROUTING - ACCEPT

mangle PREROUTING
:PREROUTING ACCEPT [2580:169942]
:neutron-l3-agent-PREROUTING - [0:0]
:neutron-l3-agent-floatingip - [0:0]
-A PREROUTING -j neutron-l3-agent-PREROUTING

-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-mark
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-scope

-A neutron-l3-agent-PREROUTING -m connmark ! --mark 0x0/0xffff0000 -j CONNMARK --restore-mark --nfmask 0xffff0000 --ctmask 0xffff0000
-A neutron-l3-agent-PREROUTING -j neutron-l3-agent-floatingip

-A neutron-l3-agent-PREROUTING -d 169.254.169.254/32 -i qr-+ -p tcp -m tcp --dport 80 -j MARK --set-xmark 0x1/0xffff
111


nat PREROUTING
Принятие решения о маршрутизации
mangle FORWARD
filter FORWARD
mangle POSTROUTING
nat POSTROUTING


 !!!!Раздел не закончен!!!!!
-A neutron-l3-agent-PREROUTING -d 10.72.10.124/32 -j DNAT --to-destination 10.255.4.184

Вот это правило - меняет в заголовке назначение пакета с адреса 10.72.10.124 (это и есть адрес Floating IP) на адрес сервера 10.255.4.184
После чего пакет передается процессу маршрутизации

Маршрутизация

Пакет дальше маршрутизируется с новым адресаом получателя - 10.255.4.184, соответвенно можно проверить таблицу маршрутизации для этого адреса назначения:
ip ro get 10.255.4.184

10.255.4.184 dev qr-e02600f1-cd src 10.255.0.1 uid 0
    cache

Отсюда видно что пакет будет отправлен через интерфейс dev qr-e02600f1-cd

Подробнее информация об интерфейсе qr-e02600f1-cd:
ip -d link show dev qr-e02600f1-cd

82: qr-e02600f1-cd: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1430 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether fa:16:3e:69:cd:07 brd ff:ff:ff:ff:ff:ff promiscuity 1 minmtu 68 maxmtu 65535
    openvswitch addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

Тут видно что этот интерфейс - порт openvswitch, другими словами пакет после процесса маршрутизации снова попадает в виртуальный коммутатор.


ВАЖНО: Пакет уже модифицирован - он имеет другой адрес получателя и другой МАК-адрес:
arp -n 10.255.4.184

Address                  HWtype  HWaddress           Flags Mask            Iface
10.255.4.184             ether   fa:16:3e:ea:49:b7   C                     qr-e02600f1-cd

НОВЫЙ Мак-адрес: fa:16:3e:ea:49:b7

Прохождение трафика внутри OpenVSwitch (после процесса маршрутизации)

Повторно пакет в OpenVSwitch попадает через интерфейс qr-e02600f1-cd

Просмотрев командой ovs-vsctl show конфигурацию, можно увидеть порт свитча соответвующий интерфейсу:

Port qr-e02600f1-cd
  tag: 1
  Interface qr-e02600f1-cd
    type: internal


Этот порт является портом свитча br-int.

    Bridge br-int
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port qg-932d2f98-d2
            tag: 2
            Interface qg-932d2f98-d2
                type: internal
        Port br-int
            Interface br-int
                type: internal
        Port int-br-ch-os-fl
            Interface int-br-ch-os-fl
                type: patch
                options: {peer=phy-br-ch-os-fl}
        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}
        Port fg-deee2f23-09
            tag: 2
            Interface fg-deee2f23-09
                type: internal
        Port qr-e02600f1-cd
            tag: 1
            Interface qr-e02600f1-cd
                type: internal
        Port o-hm0
            tag: 1
            Interface o-hm0
                type: internal
        Port tapb9b0014b-e1
            tag: 1
            Interface tapb9b0014b-e1
                type: internal
        Port qg-aefc8a2e-98
            tag: 2
            Interface qg-aefc8a2e-98
                type: internal

Далее снова следует вернуться к OpenFlow правилам коммутатора br-int

Таблицы коммутатора br-int

Для простоты просмотра вырежем из вывода счетчики и прочие поля которые не влияют на коммутацию пакета:
ovs-ofctl dump-flows br-int | awk '{print $3" "$6" "$7}' | column -t
Вывод выглядит так (2 строки для примера)

table=0,   priority=65535,dl_vlan=4095                                                                                                  actions=drop
table=0,   priority=200,reg3=0                                                                                                          actions=set_queue:0,load:0x1->NXM_NX_REG3[0],resubmit(,0)

Для сравнения полный вывод без форматирования:

 cookie=0xe00566add900f16d, duration=4675.255s, table=0, n_packets=0, n_bytes=0, priority=65535,dl_vlan=4095 actions=drop
 cookie=0x8777a5187aa1b046, duration=4673.509s, table=0, n_packets=26045, n_bytes=2256052, priority=200,reg3=0 actions=set_queue:0,load:0x1->NXM_NX_REG3[0],resubmit(,0)

Поля,которые не интересуют для анализа прохождения пакета и вырезаны:

  • cookie
  • duration
  • n_packets
  • n_bytes

Далее проджолжаем анализ по-таблично, от таблицы с номером 0, внутри таблицы правила идут в порядке приоритетов, от большего к меньшему

table0 коммутатора br-int (второй проход)

table 0

table=0,   priority=65535,dl_vlan=4095                                                                                                  actions=drop
table=0,   priority=200,reg3=0                                                                                                          actions=set_queue:0,load:0x1->NXM_NX_REG3[0],resubmit(,0)
table=0,   priority=5,in_port="int-br-ch-os-fl",dl_dst=fa:16:3f:e0:b7:4b                                                                actions=resubmit(,4)
table=0,   priority=5,in_port="patch-tun",dl_dst=fa:16:3f:e0:b7:4b                                                                      actions=resubmit(,3)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:0e:d7:45                                                                actions=resubmit(,2)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:38:c0:62                                                                actions=resubmit(,2)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:64:4e:1f                                                                actions=resubmit(,2)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:96:cd:a5                                                                actions=resubmit(,2)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:a0:1e:1b                                                                actions=resubmit(,2)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:f9:d4:00                                                                actions=resubmit(,2)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:0e:d7:45                                                                      actions=resubmit(,1)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:38:c0:62                                                                      actions=resubmit(,1)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:64:4e:1f                                                                      actions=resubmit(,1)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:96:cd:a5                                                                      actions=resubmit(,1)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:a0:1e:1b                                                                      actions=resubmit(,1)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:f9:d4:00                                                                      actions=resubmit(,1)
table=0,   priority=3,in_port="int-br-ch-os-fl",vlan_tci=0x0000/0x1fff                                                                  actions=mod_vlan_vid:2,resubmit(,60)
table=0,   priority=2,in_port="int-br-ch-os-fl"                                                                                         actions=drop
table=0,   priority=0                                                                                                                   actions=resubmit(,60)



Тут можно видеть, что ни одно правило не имеет fl_dst=fa:16:3e:ea:49:b7, где мак fa:16:3e:ea:49:b7 это мак виртуальной машины (отличный от оригинального мака, так как пакет был отмаршрутизирован, и соответвенно мак получателя изменен)


Остальные правила кроме table=0, priority=200,reg3=0 actions=set_queue:0,load:0x1->NXM_NX_REG3[0],resubmit(,0)
которое устанавливает значение регистра reg3 (ВНУТРЕННИЙ регистр, модификация пакета НЕ происходит!)
не соответвуют пакету, и последним правилом table=0, priority=0 actions=resubmit(,60) пакет отправляется в таблицу 60

table 60 коммутатора br-int (второй проход)

table 60

table=60,  priority=100,in_port="o-hm0"                                                                                                 actions=load:0x3->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],resubmit(,71)
table=60,  priority=100,in_port="qg-aefc8a2e-98"                                                                                        actions=load:0x6->NXM_NX_REG5[],load:0x2->NXM_NX_REG6[],resubmit(,73)
table=60,  priority=100,in_port="qr-e02600f1-cd"                                                                                        actions=load:0x5->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],resubmit(,73)
table=60,  priority=100,in_port="tap0692f9f3-7f"                                                                                        actions=load:0x4->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],resubmit(,73)
table=60,  priority=90,dl_vlan=1,dl_dst=fa:16:3e:0c:e4:73                                                                               actions=load:0x3->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],strip_vlan,resubmit(,81)
table=60,  priority=3                                                                                                                   actions=NORMAL

Как было выяснено выше, пакет к коммутатор попадает через порт qr-e02600f1-cd и имеет мак назначения fa:16:3e:ea:49:b7
Просмотрев таблицу 60 сразу видно, что единственно правило которому соответвует пакет это
table=60, priority=100,in_port="qr-e02600f1-cd" actions=load:0x5->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],resubmit(,73)
Согласно этому правилу, производятся следующие действия:

  • load:0x5->NXM_NX_REG5[] - записать значение 0x5 в регистр 5
  • load:0x1->NXM_NX_REG6[] - записать значение 0x1 в регистр 6
  • resubmit(,73) - передать пакет в таблицу 73

table 73 коммутатора br-int

table 73 Пакет имеет мак назначения fa:16:3e:ea:49:b7, reg5=0x5, reg6=0x1

table=73,  priority=100,reg6=0x1,dl_dst=fa:16:3e:0c:e4:73                                                                               actions=load:0x3->NXM_NX_REG5[],resubmit(,81)
table=73,  priority=90,ct_state=+new-est,ip,reg5=0x3                                                                                    actions=ct(commit,zone=NXM_NX_REG6[0..15]),resubmit(,91)
table=73,  priority=90,ct_state=+new-est,ipv6,reg5=0x3                                                                                  actions=ct(commit,zone=NXM_NX_REG6[0..15]),resubmit(,91)
table=73,  priority=80,reg5=0x3                                                                                                         actions=resubmit(,94)
table=73,  priority=80,reg5=0x6                                                                                                         actions=resubmit(,94)
table=73,  priority=80,reg5=0x5                                                                                                         actions=resubmit(,94)
table=73,  priority=80,reg5=0x4                                                                                                         actions=resubmit(,94)
table=73,  priority=0                                                                                                                   actions=drop

Единсственное правило, которому соответвует пакет - это table=73, priority=80,reg5=0x4 actions=resubmit(,94), согласно которому пакет попадает в таблицу 94

table 94 коммутатора br-int

table 94

table=94,  priority=1                                                                                                                   actions=NORMAL

Единственное правило говорит о том, что дальнейшая коммутация осуществляется на основе классической таблицы коммутации (Forwarding database, FDB)

Таблица мак-адресов коммутатора br-int (второй проход)

Мак-адрес назначения в пакете - fa:16:3e:ea:49:b7 ovs-appctl fdb/show br-int

 port  VLAN  MAC                Age
    1     2  52:54:15:bb:bb:16  118
    2     1  fa:16:3e:ea:49:b7    1
    1     2  00:02:2d:aa:bb:02    1
    5     1  fa:16:3e:69:cd:07    1
    6     2  fa:16:3e:40:32:d5    1
    1     2  52:54:15:ff:ff:16    1
    1     2  52:54:15:aa:aa:16    1

Тут видно, что мак-адрес назначения (fa:16:3e:ea:49:b7 )встречается 1 раз - в строке

    2     1  fa:16:3e:ea:49:b7    1

Соответственно порт назначения имеет номер 2.
Для того что бы выяснить какой именно порт имеет номер 2, просмотреть номера портов ovs-ofctl -OOpenFlow13 show br-int

OFPT_FEATURES_REPLY (OF1.3) (xid=0x2): dpid:0000ae03d42a2e4e
n_tables:254, n_buffers:0
capabilities: FLOW_STATS TABLE_STATS PORT_STATS GROUP_STATS QUEUE_STATS
OFPST_PORT_DESC reply (OF1.3) (xid=0x3):
 1(int-br-ch-os-fl): addr:8a:38:2d:a7:77:0a
     config:     0
     state:      LIVE
     speed: 0 Mbps now, 0 Mbps max
 2(patch-tun): addr:6a:2a:dd:60:81:7c
     config:     0
     state:      LIVE
     speed: 0 Mbps now, 0 Mbps max
 3(o-hm0): addr:fa:16:3e:0c:e4:73
     config:     0
     state:      LIVE
     speed: 0 Mbps now, 0 Mbps max
 4(tap0692f9f3-7f): addr:00:00:00:00:00:00
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
 5(qr-e02600f1-cd): addr:00:00:00:00:00:00
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
 6(qg-aefc8a2e-98): addr:00:00:00:00:00:00
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
 LOCAL(br-int): addr:ae:03:d4:2a:2e:4e
     config:     PORT_DOWN
     state:      LINK_DOWN
     speed: 0 Mbps now, 0 Mbps max
OFPT_GET_CONFIG_REPLY (OF1.3) (xid=0x9): frags=normal miss_send_len=0

Тут видно, что порт с номером 2 это

 2(patch-tun): addr:6a:2a:dd:60:81:7c
     config:     0
     state:      LIVE
     speed: 0 Mbps now, 0 Mbps max

Что это за порт можно найти в выводе команды ovs-vsctl show


        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}

Этот порт имеет тип patch, другими словами соединяет 2 виртуальных коммутатора,
и просмотрев вывод команды

ovs-vsctl show

можно найти второй конец этого виртуального патч-корда, и увидеть что он включен в Bridge br-tun

        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}

Полностью информация о виртуальном коммутаторе br-tun

    Bridge br-tun
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port vxlan-0a48080a
            Interface vxlan-0a48080a
                type: vxlan
                options: {df_default="true", dst_port="4790", egress_pkt_mark="0", in_key=flow, local_ip="10.72.8.9", out_key=flow, remote_ip="10.72.8.10"}
        Port vxlan-0a48080b
            Interface vxlan-0a48080b
                type: vxlan
                options: {df_default="true", dst_port="4790", egress_pkt_mark="0", in_key=flow, local_ip="10.72.8.9", out_key=flow, remote_ip="10.72.8.11"}
        Port vxlan-0a48080d
            Interface vxlan-0a48080d
                type: vxlan
                options: {df_default="true", dst_port="4790", egress_pkt_mark="0", in_key=flow, local_ip="10.72.8.9", out_key=flow, remote_ip="10.72.8.13"}
        Port br-tun
            Interface br-tun
                type: internal
        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}

Дальнейшая коммутация пакета будет выполняться коммутатором br-tun

Прохождение трафика через виртуальный коммутатор br-tun

Как выяснили, трафик попадает в коммутатор br-tun через порт patch-int

Таблицы коммутатора br-tun

Для простоты просмотра вырежем из вывода счетчики и прочие поля которые не влияют на коммутацию пакета:
ovs-ofctl dump-flows br-tun | awk '{print $3" "$6" "$7}' | column -t
Вывод выглядит так (2 строки для примера)

table=0,   priority=1,in_port="patch-int"                         actions=resubmit(,1)
table=0,   priority=1,in_port="vxlan-0a48080a"                    actions=resubmit(,4)

Для сравнения полный вывод без форматирования:

 cookie=0x664e52e9b9d23ed7, duration=21138.026s, table=0, n_packets=105243, n_bytes=9417514, priority=1,in_port="patch-int" actions=resubmit(,1)
 cookie=0x664e52e9b9d23ed7, duration=21128.685s, table=0, n_packets=0, n_bytes=0, priority=1,in_port="vxlan-0a48080a" actions=resubmit(,4)

Поля,которые не интересуют для анализа прохождения пакета и вырезаны:

  • cookie
  • duration
  • n_packets
  • n_bytes

Далее проджолжаем анализ по-таблично, от таблицы с номером 0, внутри таблицы правила идут в порядке приоритетов, от большего к меньшему
Пакет попадает в коммутатор через порт patch-int, и имеет мак назначения fa:16:3e:ea:49:b7

table 0 коммутатора br-tun

table=0,   priority=1,in_port="patch-int"                         actions=resubmit(,1)
table=0,   priority=1,in_port="vxlan-0a48080a"                    actions=resubmit(,4)
table=0,   priority=1,in_port="vxlan-0a48080d"                    actions=resubmit(,4)
table=0,   priority=1,in_port="vxlan-0a480809"                    actions=resubmit(,4)
table=0,   priority=0                                             actions=drop

Тут видно, что пакеты с порта patch-int передаются в таблицу 1 (проверка по входящему порту)

table 1 коммутатора br-tun

table=1,   priority=0                                             actions=resubmit(,2)

Безусловный переход в таблицу 2 (зачем ?)

table 2 коммутатора br-tun

table=2,   priority=1,arp,dl_dst=ff:ff:ff:ff:ff:ff                actions=resubmit(,21)
table=2,   priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00  actions=resubmit(,20)
table=2,   priority=0,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00  actions=resubmit(,22)

Это правила в переводе на "язык человеков" означают:

  • unicast ethernet адреса получения передаем в таблицу 20
  • multicast ethernet адреса получения передаем в таблицу 22


Если описать более "стого" то читать так (для обоих правил):

  • взять мак-адрес получателя, надожить на него маску (01:00:00:00:00:00) (в обоих правилах маска одинаковая)
  • в результате работы может получиться 2 возможных значения (два значения, по тому что если перевести маску в двоичный вид то в полученном числе есть 1 только в одном разряде, и при поразрядном умножении в результате будет тоже не боле одной единицы (но может быть и ноль единиц, так как в исходном маке в этом разряде уже был ноль)
  • результат наложения маски даст знание 0 или 1 в младшем бите второго байта мак - адреса (например в маке который рассматриваем старший байт это fa:16:3e:ea:49:b7 (0xa) == 1010, который после наложения маски 0x1 (двоичное 0001 ) будет иместь значение 0000, так как младьший бит уже имел значение 0

В результате исследуемый пакет будет направлен в таблицу 20

table 20 коммутатора br-tun

table=20,  priority=2,dl_vlan=1,dl_dst=fa:16:3e:55:eb:a8          actions=strip_vlan,load:0x24->NXM_NX_TUN_ID[],output:"vxlan-0a48080a"
table=20,  priority=2,dl_vlan=1,dl_dst=fa:16:3e:2b:3e:99          actions=strip_vlan,load:0x24->NXM_NX_TUN_ID[],output:"vxlan-0a480809"
table=20,  priority=2,dl_vlan=1,dl_dst=fa:16:3e:42:68:31          actions=strip_vlan,load:0x24->NXM_NX_TUN_ID[],output:"vxlan-0a480809"
table=20,  priority=2,dl_vlan=1,dl_dst=fa:16:3e:ea:49:b7          actions=strip_vlan,load:0x24->NXM_NX_TUN_ID[],output:"vxlan-0a48080d"
table=20,  hard_timeout=300,                                      priority=1,vlan_tci=0x0001/0x0fff,dl_dst=fa:16:3e:ea:49:b7
table=20,  priority=0                                             actions=resubmit(,22)

В этой таблице есть совпадения по маку получателя - отработает правило

table=20,  priority=2,dl_vlan=1,dl_dst=fa:16:3e:ea:49:b7          actions=strip_vlan,load:0x24->NXM_NX_TUN_ID[],output:"vxlan-0a48080d"

Это правило:

  • strip_vlan снимет все VLAN теги
  • load:0x24->NXM_NX_TUN_ID[] это дествие определяет VNI - аналог VlanID для VXLAN, 0x24 == 36 (десятичное)
  • output:"vxlan-0a48080d" отправит пакет в порт vxlan-0a48080d

порт vxlan-0a48080d

Port vxlan-0a48080d
   Interface vxlan-0a48080d
       type: vxlan
       options: {df_default="true", dst_port="4790", egress_pkt_mark="0", in_key=flow, local_ip="10.72.8.11", out_key=flow, remote_ip="10.72.8.13"}

Пакет будет инкапсулирован в VxLAN и отправлен на адрес remote_ip="10.72.8.13" (адрес отправителя будет local_ip="10.72.8.11")


Проверка tcpdump

Так как пакет на данном этапе покинет Network/Controller node, то можно попробовать увидеть его tcpdump
Проверить через какой интерфейс будет отправле пакет для remote_ip="10.72.8.13" можно командой:
ip ro get 10.72.8.13

10.72.8.13 dev tenant src 10.72.8.11 uid 0
    cache

Искомый интерфейс имеет имя tenant

Проверив трафик tcpdump можно видеть что VXLAN трафик с VNI=36 (0x24) передается:

tcpdump -n -i tenant -ee
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tenant, link-type EN10MB (Ethernet), capture size 262144 bytes
08:52:12.889945 52:54:15:ac:ac:15 > 5e:24:8c:10:3d:20, ethertype IPv4 (0x0800), length 148: 10.72.8.11.36100 > 10.72.8.13.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol
08:52:12.890454 5e:24:8c:10:3d:20 > 52:54:15:ac:ac:15, ethertype IPv4 (0x0800), length 148: 10.72.8.13.38067 > 10.72.8.11.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol
08:52:12.990619 52:54:15:ac:ac:15 > 5e:24:8c:10:3d:20, ethertype IPv4 (0x0800), length 148: 10.72.8.11.36100 > 10.72.8.13.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol
08:52:12.991146 5e:24:8c:10:3d:20 > 52:54:15:ac:ac:15, ethertype IPv4 (0x0800), length 148: 10.72.8.13.38067 > 10.72.8.11.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol

Далее обработка на стороне Network/Control Node заканчивается, и дальнейшая счастливая жизнь пакета продолжается на Compute Node

Note: "ERROR: unknown-next-protocol":

Судя по всему tcpdump не знает что внутри так как не знает что делать со згначением "Next Protocol":
https://github.com/the-tcpdump-group/tcpdump/blob/master/print-vxlan-gpe.c

Compute Node

Compute node имеет адрес 10.72.8.13, однако это не означает что это маршрутизируемый адрес - вполне возможно что это адрес используется только для построения оверлейной сети. (как раз наш случай)
Выяснить адрес для подключения - обратится к документации по клауду, это полностью зависит от того как был развернут конкретный клауд и общих рекомендаций тут нет.
Проверить что трафик доходит до Compute Node можно tcpdump
сначала убедиться что адрес присутствует на ноде:

tenant: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.72.8.13  netmask 255.255.255.0  broadcast 10.72.8.255
        inet6 fe80::5c24:8cff:fe10:3d20  prefixlen 64  scopeid 0x20<link>
        ether 5e:24:8c:10:3d:20  txqueuelen 1000  (Ethernet)
        RX packets 878696  bytes 93808692 (93.8 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 616662  bytes 90313355 (90.3 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


Проверить что видно трафик VXLAN с VNI=36 (0x24) tcpdump -n -i tenant -ee

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tenant, link-type EN10MB (Ethernet), capture size 262144 bytes
09:19:04.142325 52:54:15:ac:ac:15 > 5e:24:8c:10:3d:20, ethertype IPv4 (0x0800), length 148: 10.72.8.11.36100 > 10.72.8.13.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol
09:19:04.142706 5e:24:8c:10:3d:20 > 52:54:15:ac:ac:15, ethertype IPv4 (0x0800), length 148: 10.72.8.13.38067 > 10.72.8.11.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol
09:19:05.145801 52:54:15:ac:ac:15 > 5e:24:8c:10:3d:20, ethertype IPv4 (0x0800), length 148: 10.72.8.11.36100 > 10.72.8.13.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol
09:19:05.146111 5e:24:8c:10:3d:20 > 52:54:15:ac:ac:15, ethertype IPv4 (0x0800), length 148: 10.72.8.13.38067 > 10.72.8.11.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol
^C
4 packets captured
4 packets received by filter
0 packets dropped by kernel

Прохождение трафика внутри OpenVSwitch (Compute Node)

Note: "Перезагрузка":

В процессе написания этого документа у меня была необходимость обновить OpenStack что повлекло за собой перезагрузку нод, и виртуальный роутер переместился на другую Network/Control ноду.
Соответственно сменился адрес с которого идет рафик - вместо 10.72.8.11 (как в дампе выше) стал 10.72.8.9

 tcpdump -n -i tenant -ee
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tenant, link-type EN10MB (Ethernet), capture size 262144 bytes
13:58:15.889411 52:54:15:ab:ab:15 > 5e:24:8c:10:3d:20, ethertype IPv4 (0x0800), length 148: 10.72.8.9.55052 > 10.72.8.13.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol
13:58:15.890550 5e:24:8c:10:3d:20 > 52:54:15:ab:ab:15, ethertype IPv4 (0x0800), length 148: 10.72.8.13.36859 > 10.72.8.9.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol
13:58:16.886610 52:54:15:ab:ab:15 > 5e:24:8c:10:3d:20, ethertype IPv4 (0x0800), length 148: 10.72.8.9.55052 > 10.72.8.13.4790: VXLAN-GPE, flags [I], vni 36: ERROR: unknown-next-protocol


Определить прт можно по ip адресу - в этом случае это remote_ip="10.72.8.9"
ovs-vsctl show

        Port vxlan-0a480809
            Interface vxlan-0a480809
                type: vxlan
                options: {df_default="true", dst_port="4790", egress_pkt_mark="0", in_key=flow, local_ip="10.72.8.13", out_key=flow, remote_ip="10.72.8.9"}

Полный вывод команды скрыт под спойлер:

В результате видно что трафик попадает в порт

vxlan-0a480809

коммутатора br-tun


Дополнительно можно убедиться в этом с помощью команды ovs-tcpdump:
ovs-tcpdump -i vxlan-0a480809

tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ovsmi645790, link-type EN10MB (Ethernet), capture size 262144 bytes
14:06:40.097042 IP6 :: > ff02::1:fff1:28c1: ICMP6, neighbor solicitation, who has fe80::4c43:b8ff:fef1:28c1, length 32
14:06:40.338960 IP 192.168.22.253 > 10.255.4.184: ICMP echo request, id 8336, seq 503, length 64
14:06:40.339240 IP 10.255.4.184 > 192.168.22.253: ICMP echo reply, id 8336, seq 503, length 64
14:06:40.649072 IP6 :: > ff02::16: HBH ICMP6, multicast listener report v2, 1 group record(s), length 28
14:06:41.129165 IP6 fe80::4c43:b8ff:fef1:28c1 > ff02::16: HBH ICMP6, multicast listener report v2, 1 group record(s), length 28
14:06:41.129181 IP6 fe80::4c43:b8ff:fef1:28c1 > ip6-allrouters: ICMP6, router solicitation, length 16
14:06:41.345654 IP 192.168.22.253 > 10.255.4.184: ICMP echo request, id 8336, seq 504, length 64
14:06:41.346027 IP 10.255.4.184 > 192.168.22.253: ICMP echo reply, id 8336, seq 504, length 64
14:06:42.025131 IP6 fe80::4c43:b8ff:fef1:28c1 > ff02::16: HBH ICMP6, multicast listener report v2, 1 group record(s), length 28
14:06:42.344460 IP 192.168.22.253 > 10.255.4.184: ICMP echo request, id 8336, seq 505, length 64
14:06:42.344682 IP 10.255.4.184 > 192.168.22.253: ICMP echo reply, id 8336, seq 505, length 64

( адрес 192.168.22.253 это адрес откуда идет пинг на ВМ )
если добавить ключ -ee то можно было бы увидеть заголовки VLAN но в данном случае их нет.

Прохождение трафика через виртуальный коммутатор br-tun (Compute Node)

Так же как и для других OpenFlow-коммутаторов, требуется изучить таблицу правил что бы понять как будет идти трафик:
ovs-ofctl dump-flows br-tun
Полный вывод скрыт, дальше анализируем используя только значимые поля.


table 0 коммутатора br-tun (Compute Node)

table=0,   priority=1,in_port="patch-int"                         actions=resubmit(,1)
table=0,   priority=1,in_port="vxlan-0a48080a"                    actions=resubmit(,4)
table=0,   priority=1,in_port="vxlan-0a48080b"                    actions=resubmit(,4)
table=0,   priority=1,in_port="vxlan-0a480809"                    actions=resubmit(,4)
table=0,   priority=0                                             actions=drop

Тут видно, что все пакеты с VxLAN-интерфейсов передаются в таблицу 4 (actions=resubmit(,4))

table 4 коммутатора br-tun (Compute Node)

table=4,   priority=1,tun_id=0x24                                 actions=mod_vlan_vid:1,resubmit(,9)
table=4,   priority=0                                             actions=drop

Тут видно что все пакеты с tun_id=0x24, которым соответствует и анализируемый трафик (0x24 = 36, и VNI=36 если смотреть в tcpdump выше) передаются далее в таблицу 9, (resubmit(,9))
после того как на них будет установлен VLAN ID=1 (mod_vlan_vid:1)

table 9 коммутатора br-tun (Compute Node)

table=9,   priority=1,dl_src=fa:16:3f:0e:d7:45                    actions=output:"patch-int"
table=9,   priority=1,dl_src=fa:16:3f:38:c0:62                    actions=output:"patch-int"
table=9,   priority=1,dl_src=fa:16:3f:64:4e:1f                    actions=output:"patch-int"
table=9,   priority=1,dl_src=fa:16:3f:96:cd:a5                    actions=output:"patch-int"
table=9,   priority=1,dl_src=fa:16:3f:a0:1e:1b                    actions=output:"patch-int"
table=9,   priority=1,dl_src=fa:16:3f:e0:b7:4b                    actions=output:"patch-int"
table=9,   priority=0                                             actions=resubmit(,10)

Напомню, что как было выяснено выше, пакет имет мак-адрес получателяfa:16:3e:ea:49:b7, а мак-адрес отправителя - fa:16:3e:69:cd:07
Мак-адрес отправителя - это мак-адрес интерфейса виртуального роутера
ни одно из правил, кроме последнего не срабатывает - и пакет передается в таблицу 10

table 10 коммутатора br-tun (Compute Node)

table=10,  priority=1 actions=learn(table=20,hard_timeout=300,priority=1,cookie=0xca3e4b7bf6ec3ebf,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:0->NXM_OF_VLAN_TCI[],load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],output:OXM_OF_IN_PORT[]),output:"patch-int"

Достаточно запутанное правило, однако из него видно что кроме всего прочего пакет покидает коммутатор через порт patch-int.

  • learn(
    • table=20
    • hard_timeout=300
    • priority=1
    • NXM_OF_VLAN_TCI[0..11]
    • NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]
    • load:0->NXM_OF_VLAN_TCI[]
    • load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[]
    • output:OXM_OF_IN_PORT[])
  • output:"patch-int"

Тут происходит 2 действия

  • пакет отправляется через порт patch-int
  • создается правило в табличке 20

Пример правила из таблицы 20

table=20,  priority=2,dl_vlan=1,dl_dst=fa:16:3e:69:cd:07          actions=strip_vlan,load:0x24->NXM_NX_TUN_ID[],output:"vxlan-0a480809"

Некоторые части более-менее понятны, и в целом назначение этого действия - создать правило для обратного трафика

  • NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[] означает что в match-части правила dl_dstбудет совпадать с маком отправителя (fa:16:3e:69:cd:07)
  • output:OXM_OF_IN_PORT[]) - выставит OUT порт таки как был IN порт пакета
  • load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[] - cохранить TUN_ID

Но по некоторым частям ясности нет (приоритет 2 а не 1, load:0->NXM_OF_VLAN_TCI[] )

Проверить к какому виртуальному коммутатору принадлежит порт можно командой ovs-vsctl show
Порт patch-int это порт типа патч-корд, второй конец которого это порт patch-tun (что видно из options: {peer=patch-tun})

        Port patch-int
            Interface patch-int
                type: patch
                options: {peer=patch-tun}

Порт patch-tun}:

        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}

Этот порт принадлежит коммутатору br-int, и далее трафик идет в него.

Прохождение трафика через виртуальный коммутатор br-int (Compute Node)

Коммутатор br-int

    Bridge br-int
        Controller "tcp:127.0.0.1:6633"
            is_connected: true
        fail_mode: secure
        datapath_type: system
        Port int-br-ch-os-fl
            Interface int-br-ch-os-fl
                type: patch
                options: {peer=phy-br-ch-os-fl}
        Port br-int
            Interface br-int
                type: internal
        Port patch-tun
            Interface patch-tun
                type: patch
                options: {peer=patch-int}
        Port tap7eed9c11-3d
            tag: 1
            Interface tap7eed9c11-3d

Так же как и для других OpenFlow-коммутаторов, требуется изучить таблицу правил что бы понять как будет идти трафик:
ovs-ofctl dump-flows br-int
Полный вывод скрыт, дальше анализируем используя только значимые поля.


table 0 коммутатора br-int (Compute Node)

table=0,   priority=65535,dl_vlan=4095                                                                                                           actions=drop
table=0,   priority=200,reg3=0                                                                                                                   actions=set_queue:0,load:0x1->NXM_NX_REG3[0],resubmit(,0)
table=0,   priority=5,in_port="int-br-ch-os-fl",dl_dst=fa:16:3f:f9:d4:00                                                                         actions=resubmit(,4)
table=0,   priority=5,in_port="patch-tun",dl_dst=fa:16:3f:f9:d4:00                                                                               actions=resubmit(,3)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:0e:d7:45                                                                         actions=resubmit(,2)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:38:c0:62                                                                         actions=resubmit(,2)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:64:4e:1f                                                                         actions=resubmit(,2)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:96:cd:a5                                                                         actions=resubmit(,2)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:a0:1e:1b                                                                         actions=resubmit(,2)
table=0,   priority=4,in_port="int-br-ch-os-fl",dl_src=fa:16:3f:e0:b7:4b                                                                         actions=resubmit(,2)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:0e:d7:45                                                                               actions=resubmit(,1)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:38:c0:62                                                                               actions=resubmit(,1)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:64:4e:1f                                                                               actions=resubmit(,1)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:96:cd:a5                                                                               actions=resubmit(,1)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:a0:1e:1b                                                                               actions=resubmit(,1)
table=0,   priority=2,in_port="patch-tun",dl_src=fa:16:3f:e0:b7:4b                                                                               actions=resubmit(,1)
table=0,   priority=2,in_port="int-br-ch-os-fl"                                                                                                  actions=drop
table=0,   priority=0                                                                                                                            actions=resubmit(,60)

Напомню, что анализируемый трафик:

  • in_port=patch-tun (интерфейс через который трафик попал в коммтатор)
  • dl_dst=fa:16:3e:ea:49:b7 (мак-адрес получателя)
  • dl_src=fa:16:3e:69:cd:07 (мак-адрес отправителя)

Тут видно, что

  • правило с приоритеом 200 выставляет значение reg3=1 для тех пакетов у которых reg3=0
  • правила с приоритетом 5 не совпадают (не тот мак адрес или не тот порт и не тот мак-адрес)
  • правила с приоритетом 4 - не совпадает порт
  • правила с приоритетом 2 - не совпадает мак-адрес отправителя или интерфейс


Итого, отрабатывает только последнее правило с приоритетом 0 и дальше пакет будет отработан в таблице 60

table 60 коммутатора br-int (Compute Node)

table=60,  priority=100,in_port="tap7eed9c11-3d"          actions=load:0x3->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],resubmit(,71)
table=60,  priority=90,dl_vlan=1,dl_dst=fa:16:3e:ea:49:b7 actions=load:0x3->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],strip_vlan,resubmit(,81)
table=60,  priority=3                                     actions=NORMAL

Тут срабатывает правило table=60, priority=90,dl_vlan=1,dl_dst=fa:16:3e:ea:49:b7 actions=load:0x3->NXM_NX_REG5[],load:0x1->NXM_NX_REG6[],strip_vlan,resubmit(,81) Далее пакет попадает в таблицу 81

table 81 коммутатора br-int (Compute Node)

table=81,  priority=100,arp,reg5=0x3                                                                                                             actions=output:"tap7eed9c11-3d"
table=81,  priority=100,icmp6,reg5=0x3,icmp_type=130                                                                                             actions=output:"tap7eed9c11-3d"
table=81,  priority=100,icmp6,reg5=0x3,icmp_type=135                                                                                             actions=output:"tap7eed9c11-3d"
table=81,  priority=100,icmp6,reg5=0x3,icmp_type=136                                                                                             actions=output:"tap7eed9c11-3d"
table=81,  priority=95,udp,reg5=0x3,tp_src=67,tp_dst=68                                                                                          actions=output:"tap7eed9c11-3d"
table=81,  priority=95,udp6,reg5=0x3,tp_src=547,tp_dst=546                                                                                       actions=output:"tap7eed9c11-3d"
table=81,  priority=90,ct_state=-trk,ip,reg5=0x3                                                                                                 actions=ct(table=82,zone=NXM_NX_REG6[0..15])
table=81,  priority=90,ct_state=-trk,ipv6,reg5=0x3                                                                                               actions=ct(table=82,zone=NXM_NX_REG6[0..15])
table=81,  priority=80,ct_state=+trk,reg5=0x3                                                                                                    actions=resubmit(,82)
table=81,  priority=0                                                                                                                            actions=drop

Далее начинается какой то ад, и это правило я уже не могу разобрать

table=81,  priority=90,ct_state=-trk,ip,reg5=0x3                                                                                                 actions=ct(table=82,zone=NXM_NX_REG6[0..15])

и трафик попадает в таблицу 82

table 82 коммутатора br-int (Compute Node)

table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x4000/0xc000                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x8000/0xc000                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x4000/0xc000                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x8000/0xc000                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x2000/0xe000                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xc000/0xe000                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x2000/0xe000                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xc000/0xe000                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x1000/0xf000                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xe000/0xf000                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x1000/0xf000                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xe000/0xf000                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x800/0xf800                                                                    actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xf000/0xf800                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x800/0xf800                                                                        actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xf000/0xf800                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x400/0xfc00                                                                    actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xf800/0xfc00                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x400/0xfc00                                                                        actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xf800/0xfc00                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x200/0xfe00                                                                    actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xfc00/0xfe00                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x200/0xfe00                                                                        actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xfc00/0xfe00                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x100/0xff00                                                                    actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xfe00/0xff00                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x100/0xff00                                                                        actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xfe00/0xff00                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x80/0xff80                                                                     actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xff00/0xff80                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x80/0xff80                                                                         actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xff00/0xff80                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x40/0xffc0                                                                     actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xff80/0xffc0                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x40/0xffc0                                                                         actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xff80/0xffc0                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x20/0xffe0                                                                     actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xffc0/0xffe0                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x20/0xffe0                                                                         actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xffc0/0xffe0                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x10/0xfff0                                                                     actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xffe0/0xfff0                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x10/0xfff0                                                                         actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xffe0/0xfff0                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x8/0xfff8                                                                      actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xfff0/0xfff8                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x8/0xfff8                                                                          actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xfff0/0xfff8                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x4/0xfffc                                                                      actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xfff8/0xfffc                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x4/0xfffc                                                                          actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xfff8/0xfffc                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x2/0xfffe                                                                      actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0xfffc/0xfffe                                                                   actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0x2/0xfffe                                                                          actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=0xfffc/0xfffe                                                                       actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=1                                                                               actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=65534                                                                           actions=output:"tap7eed9c11-3d"
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=1                                                                                   actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=77,ct_state=+new-est,tcp,reg5=0x3,tp_dst=65534                                                                               actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=75,ct_state=+est-rel-rpl,icmp,reg5=0x3                                                                                       actions=output:"tap7eed9c11-3d"
table=82,  priority=75,ct_state=+new-est,icmp,reg5=0x3                                                                                           actions=ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=70,ct_state=+est-rel-rpl,ip,reg5=0x3                                                                                         actions=conjunction(16,2/2)
table=82,  priority=70,ct_state=+est-rel-rpl,ipv6,reg5=0x3                                                                                       actions=conjunction(24,2/2)
table=82,  priority=70,ct_state=+new-est,ip,reg5=0x3                                                                                             actions=conjunction(17,2/2)
table=82,  priority=70,ct_state=+new-est,ipv6,reg5=0x3                                                                                           actions=conjunction(25,2/2)
table=82,  priority=70,conj_id=16,ct_state=+est-rel-rpl,ip,reg5=0x3                                                                              actions=load:0x10->NXM_NX_REG7[],output:"tap7eed9c11-3d"
table=82,  priority=70,conj_id=24,ct_state=+est-rel-rpl,ipv6,reg5=0x3                                                                            actions=load:0x18->NXM_NX_REG7[],output:"tap7eed9c11-3d"
table=82,  priority=70,conj_id=17,ct_state=+new-est,ip,reg5=0x3                                                                                  actions=load:0x11->NXM_NX_REG7[],ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=70,conj_id=25,ct_state=+new-est,ipv6,reg5=0x3                                                                                actions=load:0x19->NXM_NX_REG7[],ct(commit,zone=NXM_NX_REG6[0..15]),output:"tap7eed9c11-3d",resubmit(,92)
table=82,  priority=70,ct_state=+est-rel-rpl,ip,reg6=0x1,nw_src=10.255.4.184                                                                     actions=conjunction(16,1/2)
table=82,  priority=70,ct_state=+new-est,ip,reg6=0x1,nw_src=10.255.4.184                                                                         actions=conjunction(17,1/2)
table=82,  priority=50,ct_state=+inv+trk                                                                                                         actions=resubmit(,93)
table=82,  priority=50,ct_mark=0x1,reg5=0x3                                                                                                      actions=resubmit(,93)
table=82,  priority=50,ct_state=+est-rel+rpl,ct_zone=1,ct_mark=0,reg5=0x3                                                                        actions=output:"tap7eed9c11-3d"
table=82,  priority=50,ct_state=-new-est+rel-inv,ct_zone=1,ct_mark=0,reg5=0x3                                                                    actions=output:"tap7eed9c11-3d"
table=82,  priority=40,ct_state=-est,reg5=0x3                                                                                                    actions=resubmit(,93)
table=82,  priority=40,ct_state=+est,ip,reg5=0x3                                                                                                 actions=ct(commit,zone=NXM_NX_REG6[0..15],exec(load:0x1->NXM_NX_CT_MARK[]))
table=82,  priority=40,ct_state=+est,ipv6,reg5=0x3                                                                                               actions=ct(commit,zone=NXM_NX_REG6[0..15],exec(load:0x1->NXM_NX_CT_MARK[]))
table=82,  priority=0                                                                                                                            actions=drop

Тут сложно, а у меня лапки, и я не могу прочитать эти правила
Тут пришлось срезать угол и воспользоваться знанием того что есть только одна ВМка с активным трафиком - и то правило где меняются значение счетчиков и будет искомым:

 cookie=0xc8775018ea65cafd,  table=82, n_packets=4552, n_bytes=313378, priority=77,ct_state=+est-rel-rpl,tcp,reg5=0x3,tp_dst=0x10/0xfff0 actions=output:"tap7eed9c11-3d"

Далее трафик выходит из недр OpenVSWitch и попадает в tap интерфейс tap7eed9c11-3d

Трафик на Compute Node за пределами OpenVSwitch

После того как трафик покинул OpenVSwitch его можно наблюдать на интерфейсе:
ip -d link show dev tap7eed9c11-3d

53: tap7eed9c11-3d: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1430 qdisc fq_codel master ovs-system state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether fe:16:3e:ea:49:b7 brd ff:ff:ff:ff:ff:ff promiscuity 1 minmtu 68 maxmtu 65521
    tun type tap pi off vnet_hdr on persist off
    openvswitch_slave addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

Далее остался один шаг - связать виртуальную машину и интрерфейс tap

virsh dumpxml instance-000000b2 | grep 'tap7eed9c11-3d'
      <target dev='tap7eed9c11-3d'/>
Note: "TUN/TAP в 2 словах":

Пакет, посылаемый операционной системой через TUN/TAP устройство обрабатывается программой, которая контролирует это устройство.
Получение данных происходит через специальный файловый дескриптор, таким образом программа просто считывает данные с файлового дескриптора.
Сама программа также может отправлять пакеты через TUN/TAP устройство выполняя запись в тот же файловый дескриптор.
В таком случае TUN/TAP устройство доставляет (или «внедряет») такой пакет в сетевой стек операционной системы, эмулируя тем самым доставку пакета с внешнего устройства.

Ссылки