Шаг 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

+21 -12
View File
@@ -272,16 +272,18 @@ table=11,priority=0 actions=drop
# Слот положил идентификатор члена в reg2. SNAT не выполняется ни в одном из
# путей: бэкенд видит реальный адрес клиента.
#
# Член в том же сегменте, что и клиент (приоритет 200) — выдача коммутацией:
# меняется только MAC назначения, TTL не уменьшается, маршрутизации нет. Для
# ВМ клиент и бэкенд остаются L2-соседями, какими и были.
# Член в том же сегменте, что и клиент (приоритет 200) — TTL не уменьшается,
# маршрутизации нет. MAC назначения здесь больше не пишется (шаг 5): следующий
# переход — таблица 21, единственное место, где живёт актуальный MAC члена
# (резолвит hcd через ARP, см. hc/neigh.go). Для ВМ клиент и бэкенд остаются
# L2-соседями, какими и были — таблица 21 сама не декрементирует TTL.
EOF
for seg_reg in "$P2_REG:$P2_MEMBERS" "$P3_REG:$P3_MEMBERS"; do
reg="${seg_reg%%:*}"
for m in ${seg_reg#*:}; do
eval "mip=\$${m}_IP; mport=\$${m}_PORT; mmac=\$${m}_MAC; mid=\$${m}_ID"
echo "table=12,priority=200,reg0=$reg,ip,reg2=$mid actions=mod_dl_dst:$mmac,ct(commit,zone=$CT_ZONE,nat(dst=$mip:$mport),table=25)"
eval "mip=\$${m}_IP; mport=\$${m}_PORT; mid=\$${m}_ID"
echo "table=12,priority=200,reg0=$reg,ip,reg2=$mid actions=ct(commit,zone=$CT_ZONE,nat(dst=$mip:$mport),table=21)"
done
done
@@ -329,15 +331,22 @@ table=20,priority=0 actions=drop
# =============================================================================
# Таблица 21 — adjacency (next-hop MAC + выходной порт)
# =============================================================================
# Члены пула прописаны статически: их MAC и порт фиксированы в topology.env.
table=21,priority=100,ip,nw_dst=$BE1_IP actions=mod_dl_dst:$BE1_MAC,output:$BE1_OFPORT
table=21,priority=100,ip,nw_dst=$BE2_IP actions=mod_dl_dst:$BE2_MAC,output:$BE2_OFPORT
table=21,priority=100,ip,nw_dst=$BE3_IP actions=mod_dl_dst:$BE3_MAC,output:$BE3_OFPORT
table=21,priority=100,ip,nw_dst=$BE4_IP actions=mod_dl_dst:$BE4_MAC,output:$BE4_OFPORT
# Ответы на health-пробы — в стек узла через internal-порты.
# Единственное место, где применяется MAC следующего перехода — и для
# маршрутизируемого пути (после таблицы 20), и для коммутируемого (напрямую
# из таблицы 12, без decrement TTL). Три источника записей, по приоритету:
#
# priority=100 члены пула — заливает и обновляет hcd (hc/neigh.go),
# резолвя MAC настоящим ARP через hcif-порты. Пусто до
# первого резолва после старта — fail-close ниже, окно то же,
# что у таблицы слотов.
# priority=90 клиенты (публичные и внутрисегментные) — действие learn из
# таблиц 0 и 1: адрес заранее не известен, изучается пассивно.
# priority=0 неизвестный next-hop — drop.
#
# Ответы на health-пробы — в стек узла через internal-порты; это собственные
# адреса узла, а не next-hop, поэтому остаются статичными.
table=21,priority=100,ip,nw_dst=$HC2_IP actions=mod_dl_dst:$HC2_MAC,output:$HC2_OFPORT
table=21,priority=100,ip,nw_dst=$HC3_IP actions=mod_dl_dst:$HC3_MAC,output:$HC3_OFPORT
# priority=90 — записи клиентов, устанавливаемые действием learn из таблиц 0 и 1.
table=21,priority=0 actions=drop
# =============================================================================