Про qemu и протокол 802.1p

от автора

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

В общем, довелось поднимать «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за внимательность.


ссылка на оригинал статьи https://habr.com/ru/articles/853062/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *