diff --git a/src/config_parser.h b/src/config_parser.h index f0c03dc3..dfaeaf72 100644 --- a/src/config_parser.h +++ b/src/config_parser.h @@ -184,7 +184,8 @@ struct global_config { int allowed_keys_count; int allowed_keys_allow_all; - // NAT configuration ([nat] section) + // Опции [nat] включают NAT; forward=proto:node_id_hex:IPv4:internal_port:external_port. + // Ошибки parse_nat отклоняют весь конфиг; диапазон портов дополнительно проверяет eim_nat_init_ctx. int nat_enabled; char nat_tun_ifname[16]; struct IP nat_tun_ip; diff --git a/src/eim_nat.h b/src/eim_nat.h index 6e38c159..b4da8b4e 100644 --- a/src/eim_nat.h +++ b/src/eim_nat.h @@ -5,7 +5,9 @@ * IPv4 UDP: отключённая checksum=0 сохраняется; вычисленный ноль записывается как 0xffff. * Перед обработкой нужен инициализированный ctx; вызовы последовательны в uasync-потоке. * gateway/internal IP — host order, internal_port — network order, внешний порт — host order. - * src_conn заимствован; срок жизни и выбор транспорта обратной отправки контролирует nat_transport. */ + * Записи живут до destroy_ctx, автоматического истечения/освобождения портов нет. + * Фрагменты и неподдерживаемые протоколы возвращаются без изменений с кодом 0. + * src_conn только сохраняется при создании динамической записи, для отправки не используется. */ #ifndef EIM_NAT_H #define EIM_NAT_H @@ -30,39 +32,53 @@ struct ETCP_CONN; #define IPPROTO_ICMP_UINT8 1 struct eim_nat_entry { - uint32_t internal_ip; - uint16_t internal_port; - uint8_t proto; - uint8_t state; - uint64_t src_node_id; // which node sent the original packet - uint64_t last_seen; - struct ETCP_CONN* src_conn; // cached connection for sendback + uint32_t internal_ip; // Внутренний IPv4, host order. + uint16_t internal_port; // Внутренний TCP/UDP-порт или ICMP echo ID, network order. + uint8_t proto; // IPPROTO_*_UINT8. + uint8_t state; // EIM_NAT_ENTRY_FREE / ACTIVE / STATIC. + uint64_t src_node_id; // Узел-владелец mapping и адресат обратной отправки. + uint64_t last_seen; // Резерв, остаётся 0; таймауты записей не реализованы. + struct ETCP_CONN* src_conn; // Заимствованная метка исходного conn, может быть NULL; модуль её не разыменовывает. }; // Состояние преобразования; TUN и транспортные ресурсы принадлежат nat_transport. struct eim_nat_ctx { - uint32_t gateway_ip; + uint32_t gateway_ip; // Внешний IPv4 NAT, host order; nat_tun_ip, иначе tun_ip. uint16_t port_start; // Начало динамического диапазона включительно; 1 <= port_start < port_end. uint16_t port_end; // Конец диапазона включительно; допускается 65535. - uint16_t next_port; - struct eim_nat_entry* table; - size_t table_size; - int initialized; + uint16_t next_port; // Следующая позиция циклического поиска свободного динамического порта. + struct eim_nat_entry* table; // Владеет table_size записями; индекс — внешний порт/echo ID для всех протоколов вместе. + size_t table_size; // EIM_NAT_TABLE_SIZE при успешной инициализации. + int initialized; // 1 — таблица создана; 0 — NAT отключён, не создан или уничтожен. }; struct global_config; +/* Создать ctx по g: IPv4 шлюза, 1 <= port_start < port_end <= 65535, статические пробросы. + * ctx не должен владеть предыдущей таблицей: перед повторным init вызвать destroy_ctx. + * 0 — успех (при !nat_enabled таблица не создаётся); -1 — ошибка, созданная таблица освобождена. */ int eim_nat_init_ctx(struct eim_nat_ctx* ctx, const struct global_config* g); +/* Освободить таблицу и обнулить созданный ctx; NULL и повторное уничтожение допустимы. */ void eim_nat_destroy_ctx(struct eim_nat_ctx* ctx); -/* 0 — обработано или пакет не подлежит NAT, -1 — ошибка; буфер принадлежит вызывающему. */ +/* Изменить source IP и порт/echo ID в ip_data (ip_len байтов IPv4), выделив/переиспользовав mapping. + * src_node_id — владелец записи; src_conn — необязательная заимствованная метка только для новой ACTIVE-записи. + * Контрольные суммы должны быть корректны до вызова: модуль их обновляет, но не проверяет. + * ctx создан, ip_data — полный доступный для записи IPv4-пакет; буфер остаётся у вызывающего. + * 0 — преобразован или пропущен без изменений; -1 — короткий заголовок/неверный IHL/исчерпан пул. */ int eim_nat_egress(struct eim_nat_ctx* ctx, uint8_t* ip_data, size_t ip_len, uint64_t src_node_id, struct ETCP_CONN* src_conn); -/* out_entry изменяется только при найденном mapping; заранее установить *out_entry=NULL. - * Запись заимствована у ctx. 0 само по себе не означает, что пакет был преобразован. */ +/* Для пакета на gateway_ip найти запись по внешнему порту/echo ID и протоколу, восстановить destination. + * ctx/ip_data/checksum — как у egress. out_entry необязателен и изменяется только при найденном mapping: + * заранее установить *out_entry=NULL. Запись заимствована у ctx до destroy_ctx. + * 0 — преобразован или не найден/не поддерживается; -1 — короткий заголовок/неверный IHL. */ int eim_nat_ingress(struct eim_nat_ctx* ctx, uint8_t* ip_data, size_t ip_len, struct eim_nat_entry** out_entry); +/* Добавить TCP/UDP-проброс на node_id != 0: internal_ip_host — IPv4 host order, + * internal_port_net — ненулевой порт network order; external_port — 1..65535 host order, + * включая порты вне динамического диапазона. Ответ владельца использует тот же внешний порт. + * 0 — добавлен; -1 — ctx не создан, неверные аргументы, занят внешний порт или уже есть такой внутренний ключ. */ int eim_nat_add_forward(struct eim_nat_ctx* ctx, uint8_t proto, uint32_t internal_ip_host, uint16_t internal_port_net, uint16_t external_port, uint64_t node_id); diff --git a/src/nat_transport.h b/src/nat_transport.h index 820294e2..57204246 100644 --- a/src/nat_transport.h +++ b/src/nat_transport.h @@ -2,7 +2,11 @@ * nat_via_node_id задаёт удалённого провайдера; при 0 узел сам выполняет eim_nat для клиентов. * Приём ограничен группой UTUN; клиент принимает ответы только от настроенного провайдера. * init создаёт TUN/обработчики по nat_enabled; destroy снимает их и освобождает ресурсы. - * Контекст принадлежит UTUN_INSTANCE; очереди и callbacks работают в uasync. */ + * TUN-очередь содержит [prefix:1][IP]; tun_write принимает только IP без prefix. + * Отправка роутеру: [svc_id:1][IP], владение entry/dgram передаётся ему даже при ошибке. + * Приём использует общий ROUTER_SVC_HDR_SIZE=26: [svc][src][dst][flags][group][IP]. + * Клиент не создаёт таблицу eim_nat: inst->nat.initialized служит флагом готовности транспорта. + * Контекст принадлежит UTUN_INSTANCE; все вызовы и callbacks работают в его uasync-потоке. */ #ifndef NAT_TRANSPORT_H #define NAT_TRANSPORT_H @@ -14,18 +18,23 @@ extern "C" { #include struct tun_if; -struct ETCP_CONN; struct nat_transport_ctx { - uint64_t self_node_id; - uint64_t nat_via_node_id; - struct tun_if* nat_tun; - int initialized; + uint64_t self_node_id; // Копия локального ID, установленная при init. + uint64_t nat_via_node_id; // 0 — провайдер; иначе ID единственного NAT-провайдера клиента. + struct tun_if* nat_tun; // Отдельный принадлежащий модулю TUN, MTU=TUN_MTU_DEFAULT. + int initialized; // TUN, binding и callback созданы; 0 при отключённом NAT/после destroy. }; struct UTUN_INSTANCE; +/* inst и inst->config обязательны, ядро/роутер уже созданы; перед повторным init вызвать destroy. + * Создать отдельный TUN и binding ETCP_RT_ID_NAT; провайдер также создаёт eim_nat. + * 0 — успех, включая выключенный NAT; -1 — ошибка, созданные NAT-ресурсы освобождены. + * Настройка внешних маршрутов/доступа в интернет выполняется отдельно от этого API. */ int nat_transport_init(struct UTUN_INSTANCE* inst); +/* Снять binding, закрыть NAT TUN и уничтожить eim_nat; NULL и повторный вызов допустимы. + * Вызвать до освобождения роутера/uasync, вне выполняемого NAT callback. */ void nat_transport_destroy(struct UTUN_INSTANCE* inst);