# Шаг 1 — итоги: прототип окружения hpnn_v2 **Дата:** 2026-08-16 **Статус:** выполнено, стенд поднят и проверен (30 из 30 проверок) **План:** [STEP1_IMPLEMENTATION_PLAN.md](STEP1_IMPLEMENTATION_PLAN.md) ## Что сделано Собрано контейнерное окружение из трёх сетей и трёх контейнеров, на котором работают обе подсистемы — маршрутизация и балансировка нагрузки. Всё форвардинг-решение принимается в OpenFlow: сетевой стек ядра контейнера транзитный трафик не обрабатывает. - **Контейнер 1 (`hpnn-lb`)** — Open vSwitch с kernel datapath в собственном netns, мост `br-lb` с тремя портами. Пайплайн из 42 правил и группы балансировки рендерится скриптом из `topology.env` и заливается атомарным бандлом. - **Контейнеры 2 и 3 (`hpnn-be1`, `hpnn-be2`)** — идентичный web-сервис на Go, каждый в своём приватном сегменте; отдаёт имя хоста, дату, время и адрес клиента. - **Сети** — публичный сегмент через macvlan поверх `enp3s0` (настоящий L2 клиентской сети) и два приватных bridge-сегмента. - **Инструментарий** — `make up/down/flows/verify`, `lbctl` для осмотра датапаса, `scripts/host-prereq.sh` для подготовки хоста. ## Результаты проверок | Проверка | Результат | |---|---| | Kernel datapath OVS в netns контейнера | `system@ovs-system`, поддержка recirculation, ct_state_nat, ct_zone | | macvlan-интерфейс как порт OVS | работает, `pub0` — ofport 1 | | `selection_method=hash, fields(ip_src,tcp_src)` | принят OVS 3.1 | | Балансировка 20 запросов с клиента | be1/be2 ≈ 10/10, счётчики обоих бакетов растут | | Сохранение IP клиента | бэкенд видит `192.168.5.13` — DNAT без SNAT подтверждён | | ARP- и ICMP-респондеры OVS | `ping` до узла и до VIP проходит | | Маршрутизация между приватными сегментами | `be1 → be2` через таблицы 20/21 | | Профиль A (§3.2 дизайна v1) | egress бэкенда наружу работает и на порту `p2` не появляется | | `ofproto/trace` пути клиент → VIP | DNAT, `dec_ttl`, перепись MAC, выход в порт 2 | ## Отличия от плана 1. **Публичная сеть объявлена с подсетью `192.168.55.0/24`, а не `192.168.5.0/24`.** Docker отказывается регистрировать пул, пересекающийся с уже существующей macvlan-сетью соседнего стенда `router-functest` (`Pool overlaps with other one on this address space`). Объявленная подсеть нужна только Docker IPAM: адреса узла и VIP живут исключительно в правилах OpenFlow, а macvlan включён в `enp3s0` и работает на реальном L2. 2. **Обратный путь занял таблицы 15 и 16 вместо 30 и 31.** `goto_table` в OpenFlow разрешает переход только вперёд, поэтому таблицы обратного пути обязаны стоять до общей маршрутизации (20). 3. **DNAT вынесен из бакетов группы в отдельную таблицу 11.** Бакет только кладёт номер члена пула в `reg2` и делает `resubmit`. Так вся логика трансляции лежит в одном месте и не зависит от того, какие действия допустимы внутри бакета. 4. **Обучение MAC клиентов сужено** до трафика, адресованного стенду (VIP, адрес узла, приватные сегменты). В первой редакции `learn` срабатывал на любой IP-пакет с macvlan-порта и заносил в таблицу 21 весь широковещательный шум сегмента — DHCP, mDNS, SSDP всех соседей по LAN. 5. **Проверка профиля A добавлена в `verify.sh`** — в плане она была только ручной. ## Известные ограничения Осознанные упрощения шага 1, каждое — задача следующих шагов: - **Датапас stateful.** Обратную трансляцию выполняет `ct(nat)`, а не stateless un-DNAT из §2 дизайна v1. Следствие: прямой и обратный трафик сессии обязаны проходить через один узел, то есть Active/Active ECMP в такой схеме работать не будет. - **ICMP-ошибки не генерируются.** У чисто-OpenFlow узла нет стека, который бы формировал `TTL exceeded` и `fragmentation needed`; PMTUD через стенд не работает, `traceroute` не показывает узел. - **Фрагменты IP не обрабатываются** — правила матчат L4-заголовок, которого во втором и последующих фрагментах нет. - **Один узел LB.** Ни BGP, ни BFD, ни ECMP-набора: FRR в стенде нет, место под него (internal-порты с IP) на шаге 1 не занято, поскольку маршрутизация выполняется целиком в OpenFlow. - **Нет health-check.** Бэкенды считаются живыми всегда; отказ бэкенда не выводит его из группы. У приложения есть `/healthz` как задел. - **Пул и листенер статические** — заданы в `topology.env`, control plane и OVSDB-схемы из §6 дизайна v1 нет. - ~~MAC бэкендов прописаны статически. ARP-резолвер next-hop отсутствует~~ — снято на шаге 5: `hcd` резолвит MAC членов пула через обычный ARP ядра, см. [STEP5_SUMMARY.md](STEP5_SUMMARY.md). - **Только TCP и только IPv4.** UDP- и L3-листенеров нет. ## Задел на шаг 2 1. Заменить `ct(nat)` на stateless DNAT/un-DNAT с таблицей слотов и `multipath(symmetric_l4)` — это спайк S0 из §12 дизайна v1 и предпосылка для Active/Active. 2. Проверить детерминизм выбора бэкенда: один и тот же 5-tuple должен давать один и тот же слот на разных узлах и между перезапусками (`ofproto/trace` по корпусу синтетических кортежей). 3. Подсистема health-check: пробы с уникального адреса узла, вывод члена из пула, перерасчёт таблицы слотов. 4. Control plane: модель `Load_Balancer → Listener → Pool → Member` в OVSDB вместо `topology.env`, агент-рендерер пайплайна. 5. ICMP-транслятор в userspace (packet-in) — для PMTUD и корректных ICMP-ошибок от имени VIP.