Protect NAT mappings referenced by pending TUN transmissions
This commit is contained in:
1 parent
80b481827c
commit
824c663ebd
3 files changed
+35
-1
No files matched your search
+28
-1
@@ -388,6 +388,10 @@ int eim_nat_remove(struct eim_nat_ctx* ctx, uint16_t external_port) {
|
||||
}
|
||||
struct eim_nat_entry* e = &ctx->table[external_port];
|
||||
if (e->state == EIM_NAT_ENTRY_FREE) return 0;
|
||||
if (e->holds) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT mapping is held by pending transmission: port=%u holds=%u", external_port, e->holds);
|
||||
return -1;
|
||||
}
|
||||
uint16_t* link = &ctx->buckets[eim_nat_bucket(e->proto, e->internal_ip, e->internal_port, e->src_node_id)];
|
||||
while (*link && *link != external_port) link = &ctx->table[*link].hash_next;
|
||||
if (!*link) {
|
||||
@@ -415,7 +419,7 @@ size_t eim_nat_expire(struct eim_nat_ctx* ctx, uint64_t now_tb, size_t budget) {
|
||||
uint32_t port = ctx->gc_cursor++;
|
||||
if (ctx->gc_cursor == EIM_NAT_TABLE_SIZE) ctx->gc_cursor = 1;
|
||||
struct eim_nat_entry* e = &ctx->table[port];
|
||||
if (e->state != EIM_NAT_ENTRY_ACTIVE) continue;
|
||||
if (e->state != EIM_NAT_ENTRY_ACTIVE || e->holds) continue;
|
||||
uint64_t timeout = e->proto == IPPROTO_TCP_UINT8 ? EIM_NAT_TCP_IDLE_TB :
|
||||
e->proto == IPPROTO_UDP_UINT8 ? EIM_NAT_UDP_IDLE_TB : EIM_NAT_ICMP_IDLE_TB;
|
||||
if (now_tb >= e->last_seen && now_tb - e->last_seen >= timeout && eim_nat_remove(ctx, port) == 1) removed++;
|
||||
@@ -423,3 +427,26 @@ size_t eim_nat_expire(struct eim_nat_ctx* ctx, uint64_t now_tb, size_t budget) {
|
||||
if (removed) DEBUG_DEBUG(DEBUG_CATEGORY_NAT, "NAT expired mappings=%zu free=%u", removed, ctx->free_count);
|
||||
return removed;
|
||||
}
|
||||
|
||||
// Зафиксировать внешний порт, уже записанный в IP-пакет, до завершения асинхронной передачи.
|
||||
int eim_nat_hold(struct eim_nat_ctx* ctx, uint16_t external_port) {
|
||||
if (!ctx || !ctx->initialized || !ctx->table || !external_port ||
|
||||
ctx->table[external_port].state == EIM_NAT_ENTRY_FREE || ctx->table[external_port].holds == UINT16_MAX) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Invalid NAT hold: ctx=%p port=%u", (void*)ctx, external_port);
|
||||
return -1;
|
||||
}
|
||||
ctx->table[external_port].holds++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Освободить удержание; время простоя после отправки отсчитывается от фактической записи в TUN.
|
||||
int eim_nat_release(struct eim_nat_ctx* ctx, uint16_t external_port, int refresh) {
|
||||
if (!ctx || !ctx->initialized || !ctx->table || !external_port || !ctx->table[external_port].holds) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Invalid NAT release: ctx=%p port=%u", (void*)ctx, external_port);
|
||||
return -1;
|
||||
}
|
||||
struct eim_nat_entry* entry = &ctx->table[external_port];
|
||||
entry->holds--;
|
||||
if (refresh) entry->last_seen = get_time_tb();
|
||||
return 0;
|
||||
}
|
||||
@@ -47,6 +47,7 @@ struct eim_nat_entry {
|
||||
struct ETCP_CONN* src_conn; // Заимствованная метка исходного conn, может быть NULL; модуль её не разыменовывает.
|
||||
uint16_t hash_next; // Внутренняя цепочка корзины; 0 — конец. Ключи и состояние извне не менять.
|
||||
uint16_t free_prev, free_next; // Внутренний список свободных динамических портов; 0 — конец.
|
||||
uint16_t holds; // Временные удержания уже преобразованных исходящих пакетов до записи в TUN.
|
||||
};
|
||||
|
||||
// Состояние преобразования; TUN и транспортные ресурсы принадлежат nat_transport.
|
||||
@@ -107,6 +108,11 @@ int eim_nat_remove(struct eim_nat_ctx* ctx, uint16_t external_port);
|
||||
* Возврат — число удалённых ACTIVE-записей; STATIC не истекают. Очистка не выполняется из обработки пакета.
|
||||
* budget ограничивается 65535. NULL/неинициализированный ctx — 0. */
|
||||
size_t eim_nat_expire(struct eim_nat_ctx* ctx, uint64_t now_tb, size_t budget);
|
||||
/* Удержать существующий mapping на время асинхронной отправки; удаление/expiry запрещены до release.
|
||||
* release с refresh != 0 обновляет last_seen при фактической отправке; при отбрасывании refresh=0.
|
||||
* 0 — успех, -1 — неверная/свободная запись, переполнение holds или непарный release. */
|
||||
int eim_nat_hold(struct eim_nat_ctx* ctx, uint16_t external_port);
|
||||
int eim_nat_release(struct eim_nat_ctx* ctx, uint16_t external_port, int refresh);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -34,6 +34,7 @@ struct nat_transport_ctx {
|
||||
uint64_t blocked_destination; // Зафиксирован до ожидания/истечения mapping.
|
||||
size_t pending_count; // Пакеты в FIFO получателей и их активных ожиданиях, <= NAT_PENDING_TOTAL.
|
||||
struct ll_entry* tun_pending; // Единственный принятый IP-пакет, ожидающий доступности TUN для записи.
|
||||
uint16_t tun_pending_port; // Удержанный mapping провайдера при EAGAIN; 0 у клиента/без удержания.
|
||||
void* maintenance_timer; // Порционная очистка mapping, восстановление ожиданий и уборка получателей.
|
||||
void* tun_retry_timer; // Повтор записи TUN при EAGAIN; отменяется до tun_close.
|
||||
uint64_t dropped, last_drop_log; // Счётчик отбрасываний и ограничение частоты диагностики перегрузки.
|
||||
|
||||
Reference in new issue
Block a user