Files
hpnn-proto/docs/STEP5_IMPLEMENTATION_PLAN.md
ayurishchevandClaude Opus 5 fa389f119b Шаг 5: динамическое изучение MAC/ARP для бэкендов
MAC каждого члена пула больше не статическая константа в topology.env,
а резолвится демоном hcd через обычный ARP ядра — в реальном
окружении MAC бэкенда заранее не известен (сервер ещё не подключён,
NIC может замениться), топология не описывается статически, в отличие
от стенда.

hcif-порты (единственные адреса узла в ядре) уже были настоящими
L3-интерфейсами в тех же сегментах, что и бэкенды — единственное, что
мешало обычному ARP, это permanent-записи ip neigh в entrypoint.sh.
Убрав их и добавив hc/neigh.go (читает ip -json neigh show, точечно
заливает бандл в таблицу 21 на том же тикере, что и health-пробы),
получили резолвер без нового OpenFlow-контроллера.

Таблица 21 стала единственным источником MAC для обоих путей —
маршрутизируемого и коммутируемого (шаг 3): таблица 12 больше не
дублирует MAC инлайново, ct(commit) ведёт сразу в таблицу 21.

Исправлен попутно найденный баг: после apply.sh (replace-flows)
таблица 21 не восстанавливалась, поскольку syncNeighbors сравнивал
MAC с памятью демона, а не с датапасом. Добавлен force-режим по
аналогии с Agent.apply(), плюс регрессионная проверка в verify.sh.

Проверено на живом стенде: MAC всех четырёх членов резолвлен и
совпадает с реальными интерфейсами; смена MAC "железа" обнаружена и
применена без вмешательства за счёт штатного старения ARP ядра.
Регрессия: 94 из 94 проверок.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 12:33:48 +03:00

10 KiB
Raw Permalink Blame History

Шаг 5 — план внедрения: динамическое изучение MAC/ARP для бэкендов

Дата: 2026-08-18 Предыдущий шаг: STEP3_SUMMARY.md (реализован). Не путать с шагом 4 (STEP4_PUBLIC_STATELESS_ASSESSMENT.md) — это только оценка перехода публичного пути на схему без conntrack, ещё не реализована; данный шаг с ней не пересекается. Итоги: STEP5_SUMMARY.md

Задача

Сейчас MAC каждого члена пула — статическая константа (BE1_MAC…BE4_MAC в lb/topology.env), продублированная в четырёх независимых местах: правило mod_dl_dst в таблице 21 (routed-путь), инлайновый mod_dl_dst в таблице 12 (same-segment/коммутируемый путь шага 3), permanent-запись ip neigh в ядре узла (lb/entrypoint.sh, для health-проб) и реальный MAC интерфейса ВМ (scripts/attach-segment.sh). Все четыре обязаны совпадать вручную.

В реальном окружении MAC бэкенда заранее не известен: топология сети, в отличие от стенда, не описывается статически. Уже задокументированный пробел (STEP1_SUMMARY.md, известные ограничения: «MAC бэкендов прописаны статически. ARP-резолвер next-hop отсутствует»), отдельно всплывший в оценке шага 4 (раздел 8, доставка до физического сервера в EVPN-фабрике).

Нужен компонент, изучающий MAC членов пула динамически, без предварительного описания в конфигурации.

Объём: изучаются MAC только для адресов, уже известных из конфигурации (member IP объявлен в hc-config.sh); обнаружение новых, необъявленных хостов — вне рамок (отдельная задача control plane, уже отложена в MULTITENANCY.md). Логика живёт в демоне hcd — расширение существующего единственного control-plane процесса на ноде.

Ключевая находка

Internal-порты hcif-p2/hcif-p3 — единственные адреса узла, живущие в ядре Linux, — уже настоящие L3-интерфейсы в том же сегменте, что и бэкенды. Ядро уже способно резолвить их MAC обычным ARP; единственное препятствие — статические ip neigh ... nud permanent записи в lb/entrypoint.sh:99, которые ARP для этих адресов отключают навсегда. Подтверждено на живом стенде: ip -json neigh show внутри lb-router сейчас отдаёт четыре записи с "state":["PERMANENT"] — ровно то, что предстоит заменить на реальный ARP.

Дополнительно: segment_ingress()/l3_learn() в lb/pipeline.sh уже заливает действием learn записи приоритета 90 в таблицу 21 для любого IP-трафика с портов сегмента, включая ответы бэкендов на health-пробы — на живом стенде эти записи присутствуют в dump-flows table=21, но перекрыты статическими правилами приоритета 100. Задача не строит резолвер с нуля, а: (1) снимает блокировку ARP ядра, (2) делает hcd явным и наблюдаемым источником MAC вместо неявного поведения learn, (3) убирает дублирование MAC между таблицами 12 и 21.

Разделение ответственности

Кто Адрес Механизм
Клиенты заранее неизвестен, произвольный как сейчас — learn (таблица 21, приоритет 90), без изменений
Члены пула известен из конфигурации, MAC — нет новое: hcd резолвит через ядро (ARP на hcif-портах), заливает таблицу 21 приоритетом 100

Изменения по файлам

lb/entrypoint.sh

В hc_port() убрать ip neigh replace ... nud permanent для адресов членов пула (строка 99, вызовы 106-109). MAC самого internal-порта (строка 93) остаётся статичным — собственный адрес узла, не next-hop.

hc/neigh.go (новый файл)

Работает на тикере health-проб (cfg.interval), без отдельной горутины:

  • по Source члена и новому полю iface выполняет ip -json neigh show dev <iface> to <member_ip>;
  • MAC резолвлен при состоянии REACHABLE/STALE/DELAY/PROBE/PERMANENT;
  • при изменении MAC — точечный бандл ovs-ofctl bundle: flow delete table=21,ip,nw_dst=<ip> + flow add table=21,priority=100,ip,nw_dst=<ip> actions=mod_dl_dst:<mac>,output:<port>;
  • API: поле в /status, команда lbctl neigh.

lb/pipeline.sh

  • Убрать статические table=21,priority=100,ip,nw_dst=$BE{1..4}_IP actions=mod_dl_dst:$BE{n}_MAC,output:... (строки 333-336). Записи для HC2_IP/HC3_IP остаются статичными.
  • Таблица 12, ветка same-segment (приоритет 200): убрать инлайновый mod_dl_dst:$mmac (строка 284), ct(commit,...) направить в table=21 вместо table=25. Таблица 21 не декрементирует TTL — свойство «TTL не уменьшается для соседей по сегменту» сохраняется, дублирование MAC между таблицами уходит.

lb/topology.env, lb/hc-config.sh

BE1_MAC…BE4_MAC перестают быть обязательными. Добавляется поле iface на член пула.

lb/lbctl.sh

Команда neigh: «член / адрес / MAC / состояние / возраст».

scripts/verify.sh, docs/MANUAL_TEST_PLAN.md

  • Таблица 21 заполняется динамически в течение нескольких секунд после старта.
  • lbctl neigh показывает MAC, совпадающий с реальным MAC интерфейса ВМ.
  • Смена MAC у be1 обнаруживается hcd без разрыва доставки.
  • Регрессия: все существующие проверки (оба листенера, транзит, health-check, fail-close, дренаж, идемпотентность apply.sh) без изменений в поведении.
  • lbctl trace same-segment пути — обновить ожидаемую цепочку (12 → 21 вместо 12 → 25).

Документация

docs/PUBLIC_LB_DATAFLOW.md, docs/PRIVATE_LB_DATAFLOW.md — таблица 21: «статические MAC» → «резолвятся hcd через ядро». Снять пункт «MAC бэкендов прописаны статически» из ограничений STEP1_SUMMARY.md. README.md.

Порядок работ

  1. docs/STEP5_IMPLEMENTATION_PLAN.md (этот документ).
  2. Спайк ip -json neigh — выполнено, подтверждено на живом стенде.
  3. lb/entrypoint.sh — снять статические ip neigh permanent; проверить ARP вручную.
  4. hc/neigh.go + правки hc/main.go.
  5. lb/pipeline.sh — таблица 21 как единственный источник MAC.
  6. lb/hc-config.sh, lb/topology.env.
  7. lb/lbctl.sh — команда neigh.
  8. Ручная проверка, затем scripts/verify.sh и MANUAL_TEST_PLAN.md.
  9. docs/STEP5_SUMMARY.md, README, диаграммы потоков.

Критерии приёмки

  • lbctl neigh показывает реальный MAC каждого члена, совпадающий с ip link show eth0 внутри соответствующего контейнера;
  • в topology.env нет обязательных BE*_MAC;
  • смена MAC у бэкенда обнаруживается и трафик продолжает доставляться без ручного вмешательства;
  • все существующие проверки (make verify) проходят без изменений в наблюдаемом поведении.

Риски

  • Окно на холодном старте таблицы 21 для члена, пока hcd не сделал первый резолв — по порядку величины совпадает с существующим fail-close таблицы слотов, новой категории риска не вводит.
  • Обнаружение новых, необъявленных хостов вне объёма — осознанно отложено к control plane (MULTITENANCY.md).
  • Изменение цепочки таблиц same-segment пути — требует правки ожидаемых трасс в документации.