Шаг 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>
This commit is contained in:
ayurishchevandClaude Opus 5 committed 2026-08-19 12:33:48 +03:00
1 parent f34d2edefa
commit fa389f119b
17 files changed
+678 -61

No files matched your search

+13 -8
View File
@@ -22,6 +22,7 @@
| 3 | Листенер в приватном сегменте, коммутация сегментов | [план](docs/STEP3_IMPLEMENTATION_PLAN.md) | [итоги](docs/STEP3_SUMMARY.md) |
| — | Разделение документации по потокам данных | [план](docs/CHANGE_SPLIT_DATAFLOW_DOCS_PLAN.md) | [итоги](docs/CHANGE_SPLIT_DATAFLOW_DOCS_SUMMARY.md) |
| 4 | Публичная балансировка без conntrack | — | [оценка осуществимости](docs/STEP4_PUBLIC_STATELESS_ASSESSMENT.md) |
| 5 | Динамическое изучение MAC/ARP для бэкендов | [план](docs/STEP5_IMPLEMENTATION_PLAN.md) | [итоги](docs/STEP5_SUMMARY.md) |
Как устроена раскладка слотов — [docs/MAGLEV_SLOT_TABLE.md](docs/MAGLEV_SLOT_TABLE.md)
(справка по `hc/maglev.go` для разработки). Что нужно изменить для перевода ноды
@@ -239,6 +240,7 @@ make conns # таблица соединений ct
make trace SRC=192.168.5.13 # ofproto/trace сессии клиент -> VIP
make trace SRC=10.20.0.10 SEG=p2 # то же для приватного листенера
make fdb # выученные MAC сегментов и счётчики рассылки
make neigh # MAC членов пула, резолвленный hcd через ARP ядра
make flows # перечитать pipeline.sh и перезалить правила
docker compose exec lb-router lbctl flows 21 # правила конкретной таблицы
docker compose exec lb-router lbctl status # состояние пула в JSON
@@ -252,16 +254,16 @@ docker compose exec lb-router lbctl status # состояние пула в
| Таблица | Назначение |
|---|---|
| 0 | Сегмент по входному порту (`reg0`); обучение MAC (FDB) и adjacency |
| 0 | Сегмент по входному порту (`reg0`); обучение MAC (FDB) и adjacency клиентов |
| 1 | Классификация: ARP, ICMP, листенер, трафик к маршрутизатору, коммутация |
| 5 | ARP-респондер для адресов узла, VIP и источников проб; прочий ARP — в коммутацию |
| 6 | ICMP echo-респондер для адресов узла и VIP |
| 10 | Листенеры: `VIP:80` → `multipath` кладёт номер слота в `reg1`; прочий IP → маршрутизация |
| 11 | **Таблица слотов**: `reg1` → `reg2` (член пула). Ведёт демон `hcd` |
| 12 | Применение члена: DNAT и выдача — коммутацией либо через маршрутизацию |
| 12 | Применение члена: `ct(commit, nat)`, дальше — таблица 21 (свой сегмент) или 20 (чужой сегмент/публичный) |
| 15, 16 | Обратный путь L3: `ct(nat)` снимает DNAT, источник снова становится VIP |
| 20 | Маршрутизация: приоритет = длина префикса, `dec_ttl`, MAC источника |
| 21 | Adjacency: MAC next-hop и выходной порт |
| 21 | **Adjacency**: MAC next-hop и выходной порт. Единственное место с MAC членов пула — резолвит `hcd` через ARP ядра (приоритет 100, шаг 5); MAC клиентов — `learn` (приоритет 90) |
| 24 | Обратная трансляция коммутируемого трафика сегмента |
| 25 | L2-коммутация сегмента: FDB, broadcast и рассылка неизвестного unicast |
@@ -277,12 +279,15 @@ docker compose exec lb-router lbctl status # состояние пула в
Приватный листенер и публичный делят пул, таблицу слотов и хэш. Различается
только выдача:
- **член в сегменте клиента** — меняется лишь MAC назначения, кадр отдаётся в
коммутацию. `dec_ttl` не выполняется: клиент и бэкенд остаются L2-соседями,
и ответ приходит к клиенту с исходным TTL. Обратную трансляцию делает
таблица 24, когда бэкенд отвечает соседу напрямую;
- **член в сегменте клиента** — из таблицы 12 сразу в таблицу 21 (минуя 20):
MAC назначения переписывается, `dec_ttl` не выполняется — клиент и бэкенд
остаются L2-соседями, ответ приходит к клиенту с исходным TTL. Обратную
трансляцию делает таблица 24, когда бэкенд отвечает соседу напрямую;
- **член в другом сегменте либо публичный листенер** — прежний маршрутизируемый
путь через таблицы 20/21 с `dec_ttl` и обратной трансляцией в таблице 15.
путь через таблицы 20 → 21 с `dec_ttl` и обратной трансляцией в таблице 15.
Оба пути сходятся в одной и той же таблице 21 — MAC члена пула нужен ровно в
одном месте пайплайна, а не дублируется.
Для клиента оба пути неотличимы: ответ приходит от `VIP:80`.