Наткнулся на днях на довольно забавный артефакт, и решил поделиться. Потому что в гугле по этому поводу — чуть лучше "белого шума".

В общем, довелось поднимать "playground" на платформе Cisco UCS. Немного б/у и не без странностей, но речь сейчас не об этом. Для запуска всякой телематики и управляющего ядра openStack в наших краях используется "греческий" (в свете последних новостей про linux, кавычки здесь могут исчезнуть) GANETI. Proxmox и прочие XCP в своё время были для этой цели забракованы как чересчур сложные. А команда собралась достаточно сильная для того чтобы добавить желаемые "мелочи".

Схемотехника подобрана так чтобы было практически нечему падать. Всё основное запускается в нативном (растегированном) vlan. (Обычный 802.1q для максимизации производительности. L3+ — между датацентрами.) Ещё пара vlan — для приоритизации вспомогательного трафика. Никаких network manager'ов/openvswitch'ей. (Openvswitch — на следующем уровне абстракции, и у меня именно про него нет интересных историй. Что, согласитесь, определённым образом продукт характеризует.)

Ключевые виртуальные машины (ВМ) также едут в нативном vlan. Решение не без изъянов (на прикрученные к обычному linux-бриджу tap-интерфейсы ВМ в таком режиме попадают тегированные пакеты с разведённых на том же бридже vlan). Но за полтора десятка лет эксплуатации сюрпризов именно там не случалось.

Теперь, обрисовав общую картину, перехожу к делу. Выпускаю первую ВМ. По сети не пингуется. Локально с гипервизора — всё ок. Лезу на консоль, запускаю что-то типа "tcpdump -nvvvi any" (по-умолчанию выставляется promisc-режим) — ping побежал. Выключаю tcpdump — опять глухо. При запуске с ключом "-p" (запретить неразборчивый режим) tcpdump вообще ничего не видит.

Причина, очевидно, локализуется в (virtio-)network.

Как говорил один киногерой в полосатом свитере, "посмотрим поглубже". При внимательном рассмотрении видно, что "правый хвост длиннее левого". "Туда" улетают 42 байта, а обратно тот же ARP-кадр (who-has и ответ имеют одинаковый размер) возвращается в пакете 54 байта. Потому что на него навешен дополнительно 802.1q заголовок, только с vlan id = 0 (изначально его резервировали для служебных целей). Беглое гугление показывает, что так работает относительно свежая реализация протокола 802.1p (способ таскать информацию о приоритетах трафика). "Давайте мы будем отправлять кадр без метки с меткой 0", как-то так.

Вот так оно смотрится в tcpdump на входе в гипервизор:

# tcpdump -nvvvi bond0 -e dst host 172.x.x.x | head
15:07:32.847072 00:3a:9c:57:51:fc > ae:9b:f1:f1:90:83, ethertype 802.1Q (0x8100), length 70: vlan 0, p 0, ethertype IPv4, (tos 0x0, ttl 62, id 44387, offset 0, flags [DF], proto

Дальше в подробности лезть не стал, т.к. это — "не мой хлеб". Предложение коллегам переключить политику на UCS с "Platinum" на что-нибудь попроще также энтузиазма не вызвало (оно в целом понятно, новая же игрушка). Ну ладно, "мы и сами с усами"…

QEMU у нас достаточно свежий (из Oracle appStream), и с этой стороны ловить особо нечего. Или потом на досуге поразбираться, или тупо сдать багу. (Понятно что в промышленной эксплуатации именно такой usecase не собирал примерно никто, поэтому до сих пор и осталось незамеченным.) Поэтому, сразу переходим к "плану Б".

Пытаться снять нулевой тег "в лоб" представляется довольно дурной затеей. Даже если удастся пропатчить модуль для работы с vlan, это будет лютый эксклюзив. Сразу смотрим в сторону "ip" ("ip filter" etc.), т.к. он (в принципе) позволяет проделать большинство из тех фокусов, для которых в своё время во freeBSD придумали "netmap"netgraph.

Есть у меня пакетик "network-scripts-extra", дополняющий функциональность "network-scripts" всякими macvlan'ами, veth'ами и прочими приятными вещами. Там же лежит сценарий "ifup-eth-bond" для автоматизации настройки буферов и очередей на "физике". Ну и QoS, "до кучи". В принципе, ничего секретного, постараюсь как-нибудь выложить. (Но, скорее всего, ближе ко взятию на сопровождение объявленного устаревшим "network-scripts". Потому что замены с приемлемыми ТТХ тупо не предложено, а бороться с "фатальным недостатком" — не в моих привычках.)

Для работы со входящими "очередями" есть в "tc qdisc" такая специальная дисциплина "ingress". Полагаю, даже не слишком подкованный в сетевых кунштюках читатель сообразит, что это не совсем "очередь" (пакет-то уже́ принят!). Обычно эта штука используется чтобы "заполисить" входящий трафик (грубо откинуть входящие пакеты сверх какого-то лимита). Здесь мы её задействуем для более интересных манипуляций.

Этот фокус по какой-то причине работает только на "физических" интерфейсах.

У бондинга, у которого "вход", по идее, с той же стороны — не работает. Если кто-то лазал вглубь вопроса, буду признателен за комментарии.

  • На "тапках" "tapNN" очередь будет уже "исходящей", и туда особо не воткнуться, т.к. по-умолчанию выставляю туда примитивную бесклассовую pfifo_fast.

Дополненный кусок "ifup-eth-bond" теперь выглядит так:

# tc -d -s qdisc show dev em1
tc qdisc del dev ${DEVICE} root # >> net.core.default_qdisc=fq_codel
tc qdisc replace dev ${DEVICE} handle ffff: ingress
tc qdisc replace dev ${DEVICE} root handle 1: htb default 0
# tc -d -s class show dev em1
tc class add dev ${DEVICE} parent 1:1 classid 1:10 htb prio 1 rate ${SPEED}gbit
tc class add dev ${DEVICE} parent 1:1 classid 1:20 htb prio 2 rate ${SPEED}gbit
tc class add dev ${DEVICE} parent 1:1 classid 1:30 htb prio 3 rate ${SPEED}gbit
tc qdisc add dev ${DEVICE} parent 1:10 handle 10: pfifo_fast

# Multi-band pfifo_fast for VM's like the default
# Since UEKR7 kernels, multiq needs "kernel-uek-modules-extra" to be installed.
if modprobe sch_multiq; then
  # Try to use hardware queues
  tc qdisc add dev ${DEVICE} parent 1:20 handle 20: multiq || tc qdisc add dev ${DEVICE} parent 1:20 handle 20: fq_codel
else
  tc qdisc add dev ${DEVICE} parent 1:20 handle 20: fq_codel
fi

tc qdisc add dev ${DEVICE} parent 1:30 handle 30: pfifo_fast
# tc -d -s filter show dev em1
tc filter add dev ${DEVICE} protocol all prio 10 basic match "meta(vlan mask 0xfff eq ${VLAN_LOW_PRIO})" flowid 1:30
tc filter add dev ${DEVICE} protocol all prio 10 basic match "meta(vlan mask 0xfff eq ${VLAN_HIGH_PRIO})" flowid 1:10
tc filter add dev ${DEVICE} protocol all prio 100 u32 match u32 0 0 flowid 1:20               # default, neutral prio
# "Untag" vlan 0 from 802.1p in native vlan (ingress)
#tc filter add dev ${DEVICE} parent ffff: protocol 802.1q basic match 'meta(vlan eq 0)' action vlan pop continue
tc filter add dev ${DEVICE} parent ffff: protocol all basic match "meta(vlan mask 0xfff eq 0)" action vlan pop continue

○ Скорость на интерфейсе обычно можно подглядеть в "/sys/class/net/", хотя бывают нюансы.

  • Создаём пару qdisc ("оттуда" ingress и "туда" htb). ○ HTB даёт минимальный оверхед из протестированных. Гипервизору есть чем заняться, кроме как таскать сетевой трафик.

  • Расписываем по классам 3 приоритета: управляющий канал, общая толпа (её, по возможности, оффлоадим на multiq) и "что осталось".

  • Загоняем фильтрами исходящий трафик по классам, в зависимости от метки vlan.

  • И, наконец, во входящем трафике ищем пакеты с заголовком 802.1Q (0x8100) и меткой vlan 0. И командуем растегировать. ○ Со стороны маршрутизатора срабатывает только "meta(vlan mask 0xfff eq 0)". Потому что https://en.wikipedia.org/wiki/IEEE_P802.1p, а lunux "meta(vlan)" зачем-то захватывает и верхние биты тоже.

¿ Почему я вместо "all" не указал "802.1q"? Просто побоялся ещё какой-нибудь глюк поймать. Приоритизация вылизана за последний десяток лет, влияние на производительность — на уровне стат. погрешности. Пускай остаётся "all". (Но "802.1q" — лучше читается, да. )

Всё. Надеюсь, эта заметка кому-нибудь пригодится.

Спасибо

Камрадам @ajijiadduhи @Vadimioза внимательность.

Комментарии (2)


  1. poige
    24.10.2024 12:26

    для которых в своё время во freeBSD придумали "netmap"

    Netgraph наверное всё же


    1. PnDx Автор
      24.10.2024 12:26

      Да, разумеется. Память подвела, однако. Больше 12 лет прошло с нашей последней встречи с "Фрюхой".
      * И я слегка польстил tc. Netgraph, ЕМНИП, даёт возможность состыковать вообще любого "ежа" с любым "ужом". А не только Ethernet манипулировать.