# Шаг 5 — план внедрения: динамическое изучение MAC/ARP для бэкендов **Дата:** 2026-08-18 **Предыдущий шаг:** [STEP3_SUMMARY.md](STEP3_SUMMARY.md) (реализован). **Не путать с шагом 4** ([STEP4_PUBLIC_STATELESS_ASSESSMENT.md](STEP4_PUBLIC_STATELESS_ASSESSMENT.md)) — это только оценка перехода публичного пути на схему без conntrack, ещё не реализована; данный шаг с ней не пересекается. **Итоги:** [STEP5_SUMMARY.md](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 to `; - MAC резолвлен при состоянии `REACHABLE`/`STALE`/`DELAY`/`PROBE`/`PERMANENT`; - при изменении MAC — точечный бандл `ovs-ofctl bundle`: `flow delete table=21,ip,nw_dst=` + `flow add table=21,priority=100,ip,nw_dst= actions=mod_dl_dst:,output:`; - 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 пути** — требует правки ожидаемых трасс в документации.