From 1ccb266971ae23d3a71fdb755a5c76d2d08995f2 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 21 Oct 2026 23:08:31 +0200 Subject: [PATCH] Describe NAT transport backpressure, ownership and mapping holds --- src/eim_nat_doc.md | 2 +- src/nat_transport_doc.md | 125 ++++++++++++++++++++++++--------------- 2 files changed, 78 insertions(+), 49 deletions(-) diff --git a/src/eim_nat_doc.md b/src/eim_nat_doc.md index e495fc61..e003f2ad 100644 --- a/src/eim_nat_doc.md +++ b/src/eim_nat_doc.md @@ -36,7 +36,7 @@ if (eim_nat_init_ctx(&nat, &global_cfg) == 0 && nat.initialized) { `last_seen` обновляется по `get_time_tb()` в обоих направлениях. Время простоя до удаления: TCP — 7440 секунд без отслеживания TCP-состояния ([RFC 5382, REQ-5](https://www.rfc-editor.org/rfc/rfc5382#section-5)), UDP — 120 секунд ([RFC 4787, REQ-5](https://www.rfc-editor.org/rfc/rfc4787#section-4.3)), -ICMP Echo — 60 секунд ([RFC 5508](https://www.rfc-editor.org/rfc/rfc5508#section-3.1)). +ICMP Echo — 60 секунд ([RFC 5508](https://www.rfc-editor.org/rfc/rfc5508#section-3.2)). `expire` проверяет не более заданного числа слотов с сохранением курсора, статические записи не удаляет. В nat_transport проверяются 128 слотов каждые 100мс: очистка после timeout может задержаться примерно на 51.2с плюс задержки выполнения event loop. Полного обхода таблицы на пути пакета нет. diff --git a/src/nat_transport_doc.md b/src/nat_transport_doc.md index bcf5a1fe..eba2ff6a 100644 --- a/src/nat_transport_doc.md +++ b/src/nat_transport_doc.md @@ -1,60 +1,89 @@ # NAT Transport Layer (nat_transport) ## 1. Назначение -Транспортный слой NAT поверх ETCP. Обеспечивает проброс IP-трафика между клиентом и провайдером NAT через ETCP-роутер (канал `ETCP_RT_ID_NAT`). Клиент захватывает пакеты из TUN-интерфейса и отправляет провайдеру; провайдер выпускает их в интернет через свой NAT-движок, а обратные пакеты возвращает клиенту. +Передача IPv4 между отдельным TUN и удалённым NAT-провайдером через сервис `ETCP_RT_ID_NAT` +в группе `TOPO_GROUP_UTUN`. Провайдер преобразует пакеты движком `eim_nat`, клиент использует +только транспорт. Нормативный API и правила владения описаны в `nat_transport.h` и `eim_nat.h`. -## 2. Как пользоваться +## 2. Конфигурация и поток данных +- **Провайдер** (`nat_via_node_id = 0`): создаёт таблицу `eim_nat`. В `[nat]` задаются `tun_ifname`, + `tun_ip`, `port_start`, `port_end`; проброс — `forward=udp:node_id_hex:IPv4:internal_port:external_port`. +- **Клиент**: в `[nat]` задаётся `nat_via=` и параметры своего NAT TUN. + Таблица `eim_nat` не создаётся; `inst->nat.initialized=1` означает готовность транспорта. + Опции секции `[nat]` включают NAT; параметра `enabled` нет. +- Создаётся отдельный `nat_tun` с `TUN_MTU_DEFAULT`. Адрес берётся из IPv4 `nat_tun_ip`, иначе из `tun_ip`. + Маршруты ОС и доступ провайдера в интернет настраиваются отдельно. -### Конфигурация -- **Провайдер** (`nat_via_node_id = 0`): создаёт таблицу `eim_nat`. В секции `[nat]` задаются `tun_ifname`, - `tun_ip`, `port_start`, `port_end`; статический проброс — `forward=udp:node_id_hex:IPv4:internal_port:external_port`. -- **Клиент** (`nat_via_node_id != 0`): в `[nat]` задаётся `nat_via=` и параметры своего NAT TUN. - Таблица `eim_nat` не создаётся; `inst->nat.initialized=1` используется как флаг готовности транспорта. - Параметра `enabled` нет: NAT включают опции секции `[nat]`. +1. Клиент получает `[prefix:1][IP]` из `nat_tun->output_queue`. `nat_tun_output` заменяет prefix + на `ETCP_RT_ID_NAT` и передаёт исходный entry/dgram роутеру с адресатом `nat_via_node_id`. +2. Провайдер получает пакет в `nat_receive`, выполняет `eim_nat_egress` и пишет чистый IP в NAT TUN. +3. Ответ/статический проброс из TUN провайдера проходит `eim_nat_ingress` в `nat_tun_output`. + Преобразованный пакет отправляется узлу из `mapping->src_node_id`. +4. Клиент принимает ответ только от своего провайдера, проверяет IPv4 и записывает в свой NAT TUN. -### Поток данных -1. **Клиент → Провайдер**: `[prefix:1][IP]` из `nat_tun->output_queue` → callback клиента → - `[ETCP_RT_ID_NAT:1][IP]` в `etcp_route_send(inst, TOPO_GROUP_UTUN, provider_id, ...)`. - Source/destination и заголовки протокола добавляет роутер. -2. **Провайдер получает**: `nat_transport_etcp_recv_cb` (зарегистрирован через `etcp_router_bind`) → `eim_nat_egress()` (source NAT) → `tun_write()` в NAT TUN. -3. **Провайдер → Клиент (ответ/статический проброс)**: пакет из NAT TUN → `eim_nat_ingress()` → - `[ETCP_RT_ID_NAT:1][IP]` в роутер с адресатом `entry->src_node_id`. Без mapping/адресата пакет отбрасывается. -4. **Клиент получает ответ**: `nat_transport_etcp_recv_cb` → `tun_write()` в NAT TUN клиента. +Роутер добавляет source/destination и протокольные заголовки. Callback получает служебный заголовок +`[svc:1][src:8][dst:8][rx_flags:1][group:8][IP]`, размером `ROUTER_SVC_HDR_SIZE=26`. +Это формат доставки callback, а не заголовок на проводе. CLOSE/reset без IP освобождается без трансляции. +Приём ограничен группой UTUN. Повреждённые пакеты, фрагменты, неподдерживаемые протоколы и входящие пакеты +провайдера без mapping отбрасываются. Ограничения проверки и преобразования — в `eim_nat.h`. -### Ключевые нюансы -- Используется отдельный TUN (`nat_tun`), MTU=`TUN_MTU_DEFAULT`. Шлюз берётся из IPv4 `nat_tun_ip`, иначе из `tun_ip`. - Маршруты ОС и выход провайдера в интернет настраиваются отдельно; модуль их не создаёт. -- Callback'и работают через очередь `output_queue` TUN-интерфейса. По одному пакету за вызов. -- Сервисный receive callback получает общий заголовок роутера размером 26 байтов: - `[svc:1][src:8][dst:8][rx_flags:1][group:8][IP]` (`ROUTER_SVC_*` в `etcp_router.h`). - Это формат доставки callback, не заголовок пакета на проводе. Отдельного 9-байтного NAT-заголовка нет. -- Приём ограничен `TOPO_GROUP_UTUN`; клиент принимает IP только от своего `nat_via_node_id`. -- `tun_write()` получает чистый IP без служебного префикса. Роутер принимает владение отправленным entry/dgram - при любом результате; NAT не освобождает их повторно при отказе. -- Все операции выполняются в uasync-потоке ядра. `init` вызывается после создания роутера, - `destroy` — до освобождения роутера/uasync и вне NAT callback; перед повторным `init` нужен `destroy`. -- Ограничения преобразования описаны в `eim_nat.h`: фрагменты и неподдерживаемые протоколы не преобразуются. - Сейчас провайдер передаёт такие исходящие пакеты в свой TUN без NAT, а входящие без mapping отбрасывает. +## 3. Очереди, ожидания и владение +NAT не копирует IP из TUN и не создаёт для него новый пакет. Роутер синхронно копирует payload +в свою очередь и принимает владение исходным entry/dgram при любом результате отправки. +После передачи роутеру NAT больше не обращается к пакету и не освобождает его повторно. -## 3. API +Для каждого адресата создаётся FIFO и одно отменяемое ожидание готовности router send_q. +Адресаты индексируются через хеш `ll_queue`; выделения требуются при первом обращении к узлу. +Перед передачей владения проверяется место в router send_q. При заполнении ожидается `on_send_ready`. +Порядок пакетов одного адресата сохраняется; ожидания разных адресатов независимы. -### Структуры -| Структура | Назначение | -|-----------|------------| -| `nat_transport_ctx` | Контекст: `self_node_id`, `nat_via_node_id` (0=провайдер, иначе id провайдера), `nat_tun`, флаг `initialized` | +- `NAT_PENDING_PER_PEER=64` ограничивает дополнительный буфер адресата, включая пакет в активном ожидании. + Новый пакет переполненного адресата отбрасывается. Остальные адресаты продолжают обслуживаться. + Счётчик потерь обновляется всегда, предупреждения о перегрузке выводятся не чаще раза в секунду. +- `NAT_PENDING_TOTAL=1024` ограничивает сумму дополнительных буферов. При достижении лимита + текущий преобразованный пакет возвращается в голову `output_queue` TUN, callback остаётся приостановленным. + Непустая очередь останавливает чтение TUN; освобождение места возобновляет callback. +- Дополнительно существуют очереди роутера: указанные лимиты ограничивают именно NAT-буферы. +- Адресат ответа фиксируется вместе с уже преобразованным пакетом до ожидания. Истечение/повторное + использование mapping во время ожидания не меняет адресата и не запускает повторную трансляцию. -### Функции -| Функция | Назначение | -|---------|------------| -| `nat_transport_init(inst)` | Инициализация: создаёт NAT TUN, настраивает eim_nat (если провайдер), биндит `ETCP_RT_ID_NAT` через etcp_router, вешает callback'и на TUN output_queue | -| `nat_transport_destroy(inst)` | Деинициализация: анбиндит ETCP-канал, закрывает TUN, разрушает eim_nat | +При `tun_write` с `EAGAIN/EWOULDBLOCK` удерживается один принятый пакет. `nat_receive_ready` запрещает +доставку следующих: они остаются в router recv_q без ACK. Повтор записи назначается через 10мс; +maintenance также повторяет запись. После успеха или окончательной ошибки пакет освобождается, +приём NAT-каналов UTUN возобновляется. Ошибки записи диагностируются в TUN-модуле. -### Внутренние коллбэки -| Коллбэк | Срабатывание | -|---------|-------------| -| `nat_transport_client_tun_out_cb` | Пакет из TUN output_queue на клиенте — упаковывает и шлёт провайдеру | -| `nat_transport_provider_tun_out_cb` | Пакет из TUN output_queue на провайдере — делает ingress NAT lookup, шлёт обратно клиенту | -| `nat_transport_etcp_recv_cb` | Получен пакет из канала `ETCP_RT_ID_NAT` — клиент пишет в TUN, провайдер делает egress NAT | +Провайдер дополнительно вызывает `eim_nat_hold` для внешнего source-порта/echo ID пакета, +ожидающего записи в TUN. Удержанный mapping нельзя удалить или переиспользовать при очистке. +После успешной записи `eim_nat_release` обновляет время активности; при ошибке/остановке снимает +удержание без обновления. Это удержание относится к исходящему пакету провайдера; ожидающий +обратной отправки ответ уже содержит внутренний адрес и сохранённый ID клиента. -### Зависимости -`eim_nat.h`, `etcp_router.h`, `etcp_api.h`, `tun_if.h`, `utun_instance.h`, `ll_queue.h` +## 4. Обслуживание и остановка +Таймер `nat_maintenance` каждые 100мс: +- проверяет 128 слотов NAT на истечение: TCP — 7440с, UDP — 120с, ICMP Echo — 60с простоя; +- восстанавливает ожидания, отменённые при close/reset router-канала; +- удаляет пустые FIFO адресатов после 60с простоя; +- повторяет ожидающую запись TUN. + +Статические и удержанные mapping не истекают. Полный проход таблицы занимает около 51.2с +при своевременном выполнении таймеров; очистка после timeout дополнительно зависит от задержек event loop. +Полного обхода mapping на пути пакета нет. Если выделение таймера повтора TUN не удалось, +ошибка логируется и повтор остаётся на maintenance; ошибка планирования maintenance также логируется. + +Все вызовы выполняются в uasync-потоке ядра. `init` требует созданного роутера, перед повторным `init` +нужен `destroy`. Остановка выполняется вне NAT callback, до освобождения роутера/uasync: +сначала отменяются таймеры и binding, затем waiters и FIFO освобождают исходные TUN-пакеты, +закрываются NAT router-каналы, освобождаются удержанный пакет/его mapping, TUN и таблица NAT. +Освобождение пакетов предшествует уничтожению их TUN-пула. Другие router-сервисы продолжают работать. + +## 5. API и проверки +| API | Назначение | +|---|---| +| `nat_transport_ctx` | TUN, хеш адресатов, счётчик pending, глобально остановленный пакет, ожидающая запись TUN и таймеры | +| `nat_transport_init(inst)` | Создать транспорт по конфигу; 0 — успех/выключенный NAT, -1 — ошибка с освобождением созданных ресурсов | +| `nat_transport_destroy(inst)` | Отменить ожидания и освободить транспорт; NULL/повторный вызов допустимы | + +`test_nat_transport` проверяет обе роли через настоящий роутер и TUN в test mode: порядок и контрольные суммы, +отсутствие копии исходного entry, владение при ошибке, фильтрацию источника/группы, некорректные IP, +давление router send_q, оба лимита NAT, прогресс исправного клиента рядом с медленным, +истечение/повторное использование mapping во время ожидания, EAGAIN TUN и остановку с активными ожиданиями.