diff --git a/lib/ll_queue.c b/lib/ll_queue.c index 79348f36..bbc878f1 100644 --- a/lib/ll_queue.c +++ b/lib/ll_queue.c @@ -46,8 +46,6 @@ static inline void queue_check_thread(struct ll_queue* q) { // Предварительные объявления внутренних функций static void queue_resume_timeout_cb(void* arg); static void check_waiters(struct ll_queue* q); -static void add_to_hash(struct ll_queue* q, struct ll_entry* entry); -static void remove_from_hash(struct ll_queue* q, struct ll_entry* entry); // ==================== Управление очередью ==================== @@ -60,6 +58,8 @@ struct ll_queue* queue_new(struct UASYNC* ua, size_t hash_size, uint16_t index_o q->name = name; q->ua = ua; q->size_limit = -1; // Без ограничения по умолчанию + q->threshold_max_packets = 0; // По умолчанию: ждать пустой очереди + q->threshold_max_bytes = 0; // По умолчанию: не проверять байты q->hash_size = hash_size; q->index_offset = index_offset; q->index_size = index_size; @@ -112,6 +112,17 @@ void queue_free(struct ll_queue* q) { // ВАЖНО: Не освобождаем элементы в очереди - они должны быть извлечены отдельно // Это упрощает архитектуру и предотвращает double-u_free + // Освободить список waiters (сбросить handle->internal) + struct queue_waiter* w = q->waiter_head; + while (w) { + struct queue_waiter* next = w->next; + if (w->handle) w->handle->internal = NULL; + u_free(w); + w = next; + } + q->waiter_head = NULL; + q->waiter_tail = NULL; + // Освободить хеш-таблицу if (q->hash_table) { u_free(q->hash_table); @@ -266,24 +277,27 @@ static uint32_t make_hash(const void* data, uint16_t len) {// алгоритм F return hash; } -// Проверить и запустить ожидающие коллбэки +// Проверить и запустить первый ожидающий коллбэк (FIFO / round-robin) static void check_waiters(struct ll_queue* q) { - if (!q) return; + if (!q || !q->waiter_head) return; -// DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "check_waiters: checking waiters, count=%d, bytes=%zu", q->count, q->total_bytes); + const int cond = q->count <= q->threshold_max_packets + && (q->threshold_max_bytes == 0 || q->total_bytes <= q->threshold_max_bytes); + if (!cond) return; - struct queue_waiter* waiter = &q->waiter; + struct queue_waiter* waiter = q->waiter_head; + q->waiter_head = waiter->next; + if (!q->waiter_head) q->waiter_tail = NULL; - if (waiter->callback) { - // Проверить условие: не больше max_packets и не больше max_bytes - // max_bytes = 0 означает "не проверять байты" - if (q->count <= waiter->max_packets && (waiter->max_bytes == 0 || q->total_bytes <= waiter->max_bytes)) { - DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "check_waiters: condition met, calling callback, count=%d<=%d, bytes=%zu<=%zu (max_bytes_check=%s)", - q->count, waiter->max_packets, q->total_bytes, waiter->max_bytes, - waiter->max_bytes == 0 ? "disabled" : "enabled"); - waiter->callback(q, waiter->callback_arg); - memset(waiter, 0, sizeof(*waiter)); - } + struct queue_waiter_handle* h = waiter->handle; + if (h) h->internal = NULL; + + u_free(waiter); + + if (h) { + DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "check_waiters: waking head waiter, count=%d<=%d, bytes=%zu<=%zu", + q->count, q->threshold_max_packets, q->total_bytes, q->threshold_max_bytes); + h->callback(q, h->callback_arg); } } @@ -532,34 +546,73 @@ int queue_check_consistency(struct ll_queue* q) { return 0; } -// ==================== Асинхронное ожидание ==================== +// ==================== Пороговое ожидание (backpressure) ==================== + +void queue_set_threshold(struct ll_queue* q, int max_packets, size_t max_bytes) { + if (!q) return; + q->threshold_max_packets = max_packets; + q->threshold_max_bytes = max_bytes; +} + +void queue_waiter_handle_init(struct queue_waiter_handle* h, + queue_threshold_callback_fn callback, void* arg) { + if (!h) return; + h->internal = NULL; + h->callback = callback; + h->callback_arg = arg; +} -struct queue_waiter* queue_wait_threshold(struct ll_queue* q, int max_packets, size_t max_bytes, - queue_threshold_callback_fn callback, void* arg) { - if (!q || !callback) return NULL; +int queue_waiter_wait(struct ll_queue* q, struct queue_waiter_handle* h) { + if (!q || !h) return 1; - struct queue_waiter* waiter = &q->waiter; + if (h->internal) { + DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "[%s] queue_waiter_wait: waiter already registered", q->name); + return 1; + } // Проверить условие немедленно - if (q->count <= max_packets && (max_bytes == 0 || q->total_bytes <= max_bytes)) { - // Условие уже выполнено - вызвать коллбэк немедленно - callback(q, arg); - return NULL; + if (q->count <= q->threshold_max_packets + && (q->threshold_max_bytes == 0 || q->total_bytes <= q->threshold_max_bytes)) { + h->callback(q, h->callback_arg); + return 1; + } + + struct queue_waiter* waiter = u_malloc(sizeof(struct queue_waiter)); + if (!waiter) { + DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "[%s] queue_waiter_wait: u_malloc failed", q->name); + return -1; } + waiter->next = NULL; + waiter->handle = h; + h->internal = waiter; - // Установить waiter для отложенного вызова - waiter->max_packets = max_packets; - waiter->max_bytes = max_bytes; - waiter->callback = callback; - waiter->callback_arg = arg; + if (q->waiter_tail) q->waiter_tail->next = waiter; else q->waiter_head = waiter; + q->waiter_tail = waiter; - return waiter; + return 0; } -void queue_cancel_wait(struct ll_queue* q, struct queue_waiter* waiter) { - if (!q || !waiter || waiter != &q->waiter) return; +void queue_waiter_cancel(struct ll_queue* q, struct queue_waiter_handle* h) { + if (!q || !h || !h->internal) return; + + struct queue_waiter* waiter = h->internal; + struct queue_waiter* prev = NULL; + struct queue_waiter* curr = q->waiter_head; + while (curr && curr != waiter) { + prev = curr; + curr = curr->next; + } + if (!curr) { + DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "[%s] queue_waiter_cancel: waiter not in list", q->name); + h->internal = NULL; + return; + } + + if (prev) prev->next = waiter->next; else q->waiter_head = waiter->next; + if (waiter == q->waiter_tail) q->waiter_tail = prev; - memset(waiter, 0, sizeof(*waiter)); + h->internal = NULL; + u_free(waiter); } // ==================== Поиск и удаление по ID ==================== diff --git a/lib/ll_queue.h b/lib/ll_queue.h index c8e39366..61b3c805 100644 --- a/lib/ll_queue.h +++ b/lib/ll_queue.h @@ -26,7 +26,7 @@ * - Обязательный вызов queue_resume_callback после обработки элемента * - Поддержка пулов памяти для entry и отдельно для dgram * - Хеш-таблица для быстрого поиска по ID (опционально) - * - Встроенный одиночный waiter для ожидания освобождения очереди до заданного порога + * - Связный список waiters для backpressure c общим порогом (round-robin) * - Использует uasync для отложенного возобновления callback'ов (без рекурсии в стеке) * * @note Важные правила использования автозабора (queue_set_callback): @@ -37,13 +37,15 @@ * 5. queue_data_get() автоматически suspend'ит коллбэки для предотвращения рекурсии * * @note Память: - * - queue_free() освобождает ТОЛЬКО структуру очереди и хеш-таблицу + * - queue_free() освобождает структуру очереди, хеш-таблицу и список waiters + * (handle->internal каждого ожидающего сбрасывается в NULL) * - Все элементы должны быть извлечены через queue_data_get() и освобождены через queue_entry_free() * - dgram освобождается отдельно через queue_dgram_free() или кастомную dgram_free_fn */ -// Forward declaration +// Forward declarations struct ll_queue; +struct queue_waiter_handle; /** * @struct ll_entry @@ -84,7 +86,7 @@ typedef void (*queue_callback_fn)(struct ll_queue* q, void* arg); /** * @typedef queue_threshold_callback_fn - * @brief Одноразовый коллбэк при достижении порога очереди (queue_wait_threshold). + * @brief Одноразовый коллбэк при достижении порога очереди. * @param q указатель на очередь * @param arg пользовательский аргумент */ @@ -92,13 +94,27 @@ typedef void (*queue_threshold_callback_fn)(struct ll_queue* q, void* arg); /** * @struct queue_waiter - * @brief Описание ожидания освобождения места в очереди (встроен в ll_queue — только один). + * @brief Внутренний узел в связном списке ожидающих (очередь владеет, аллоцирует/освобождает сама). */ struct queue_waiter { - int max_packets; ///< Максимально допустимое количество элементов - size_t max_bytes; ///< Максимально допустимый объём данных (0 = не проверять) - queue_threshold_callback_fn callback; ///< Коллбэк (вызывается один раз) - void* callback_arg; ///< Аргумент коллбэка + struct queue_waiter* next; ///< Следующий в списке ожидающих + struct queue_waiter_handle* handle; ///< Обратная ссылка на публичную структуру +}; + +/** + * @struct queue_waiter_handle + * @brief Публичная управляющая структура (встраивается в структуру вызывающей стороны). + * + * Вызывающая сторона: + * 1. Объявляет поле struct queue_waiter_handle в своей структуре + * 2. Вызывает queue_waiter_handle_init() один раз при инициализации + * 3. Вызывает queue_waiter_wait() когда хочет дождаться освобождения очереди + * 4. Вызывает queue_waiter_cancel() при деинициализации для отмены ожидания + */ +struct queue_waiter_handle { + struct queue_waiter* internal; ///< NULL = не ждём; иначе — внутренний узел + queue_threshold_callback_fn callback; + void* callback_arg; }; /** @@ -120,7 +136,10 @@ struct ll_queue { void* resume_timeout_id; // ID таймера uasync для отложенного resume struct UASYNC* ua; // Экземпляр uasync (обязателен для таймеров) - struct queue_waiter waiter; // Встроенный waiter (только один) + struct queue_waiter* waiter_head; // Голова списка ожидающих + struct queue_waiter* waiter_tail; // Хвост списка ожидающих + int threshold_max_packets; // Общий порог: макс. кол-во элементов (по умолчанию 0) + size_t threshold_max_bytes; // Общий порог: макс. объём данных (0 = не проверять) struct ll_entry** hash_table; // Хеш-таблица для поиска по id (если hash_size > 0) size_t hash_size; // Размер хеш-таблицы @@ -190,26 +209,45 @@ void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg) */ void queue_resume_callback(struct ll_queue* q); -/* ==================== Пороговое ожидание ==================== */ +/* ==================== Пороговое ожидание (backpressure) ==================== */ /** - * @brief Регистрирует одноразовый коллбэк, который сработает, когда очередь освободится до заданного порога. + * @brief Устанавливает общий порог освобождения очереди. * @param q очередь - * @param max_packets максимальное количество элементов + * @param max_packets максимальное количество элементов (условие: count <= max_packets) * @param max_bytes максимальный объём данных (0 = не проверять) + * + * Порог — общий на всю очередь. Все waiters в связном списке используют этот порог. + * При достижении порога вызывается следующий waiter из списка (FIFO / round-robin). + * По умолчанию: max_packets=0, max_bytes=0 (пустая очередь). + */ +void queue_set_threshold(struct ll_queue* q, int max_packets, size_t max_bytes); + +/** + * @brief Инициализирует публичную управляющую структуру waiter. + * @param h указатель на handle (встраивается в структуру вызывающей стороны) * @param callback функция, вызываемая при достижении порога * @param arg аргумент коллбэка - * @return указатель на waiter (для отмены) или NULL, если условие уже выполнено (коллбэк вызван сразу) */ -struct queue_waiter* queue_wait_threshold(struct ll_queue* q, int max_packets, size_t max_bytes, - queue_threshold_callback_fn callback, void* arg); +void queue_waiter_handle_init(struct queue_waiter_handle* h, + queue_threshold_callback_fn callback, void* arg); /** - * @brief Отменяет ранее установленное ожидание порога. + * @brief Регистрирует ожидание освобождения очереди до общего порога. * @param q очередь - * @param waiter указатель, возвращённый queue_wait_threshold() + * @param h указатель на инициализированный handle + * @return 1 — условие уже выполнено (callback вызван немедленно), 0 — зарегистрирован в списке + */ +int queue_waiter_wait(struct ll_queue* q, struct queue_waiter_handle* h); + +/** + * @brief Отменяет ожидание (удаляет из списка, если зарегистрирован). + * @param q очередь + * @param h указатель на handle + * + * Безопасно вызывать в любом состоянии: если waiter не зарегистрирован — ничего не делает. */ -void queue_cancel_wait(struct ll_queue* q, struct queue_waiter* waiter); +void queue_waiter_cancel(struct ll_queue* q, struct queue_waiter_handle* h); /* ==================== Работа с данными ==================== */ diff --git a/src/Makefile.am b/src/Makefile.am index f8184016..a3799146 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,11 +34,11 @@ utun_CORE_SOURCES = \ eim_nat.c \ nat_transport.c \ dummynet.c \ - tcp_proxy.c \ + proxy/tcp_proxy_client.c \ etcp_router.c \ - remote_proxy.c \ - udp_proxy.c \ - icmp_proxy.c \ + proxy/tcp_proxy_server.c \ + proxy/udp_proxy.c \ + proxy/icmp_proxy.c \ lwip_tcp/lwip_pbuf.c \ lwip_tcp/lwip_tcp.c \ lwip_tcp/lwip_tcp_in.c \ diff --git a/src/config_parser.c b/src/config_parser.c index 9c95fd39..496070b1 100644 --- a/src/config_parser.c +++ b/src/config_parser.c @@ -45,8 +45,8 @@ typedef enum { SECTION_CONTROL, SECTION_ALLOWED_KEYS, SECTION_NAT, - SECTION_TCP_PROXY, - SECTION_REMOTE_PROXY + SECTION_TCP_PROXY_CLIENT, + SECTION_TCP_PROXY_SERVER } section_type_t; static char* trim(char *str) { @@ -436,30 +436,30 @@ static int parse_control(const char *key, const char *value, struct global_confi return -1; } -static int parse_tcp_proxy(const char *key, const char *value, struct global_config *global, const char *filename, int line_num) { +static int parse_tcp_proxy_client(const char *key, const char *value, struct global_config *global, const char *filename, int line_num) { if (strcmp(key, "enabled") == 0) { - global->tcp_proxy_enabled = strcasecmp(value, "yes") == 0 || strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0; + global->tcp_proxy_client_enabled = strcasecmp(value, "yes") == 0 || strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0; return 0; } if (strcmp(key, "tun_name") == 0) { - strncpy(global->tcp_proxy_tun_name, value, sizeof(global->tcp_proxy_tun_name) - 1); + strncpy(global->tcp_proxy_client_tun_name, value, sizeof(global->tcp_proxy_client_tun_name) - 1); return 0; } if (strcmp(key, "tun_ip") == 0) { - strncpy(global->tcp_proxy_tun_ip, value, sizeof(global->tcp_proxy_tun_ip) - 1); + strncpy(global->tcp_proxy_client_tun_ip, value, sizeof(global->tcp_proxy_client_tun_ip) - 1); return 0; } if (strcmp(key, "mtu") == 0) { - global->tcp_proxy_mtu = atoi(value); + global->tcp_proxy_client_mtu = atoi(value); return 0; } if (strcmp(key, "via_node") == 0) { - global->tcp_proxy_via_node_id = strtoull(value, NULL, 16); + global->tcp_proxy_client_via_node_id = strtoull(value, NULL, 16); return 0; } if (strcmp(key, "forward") == 0) { - if (global->tcp_proxy_mapping_count >= MAX_TCP_PROXY_MAPPINGS) { - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Too many tcp_proxy forward rules (max %d)", MAX_TCP_PROXY_MAPPINGS); + if (global->tcp_proxy_client_mapping_count >= MAX_TCP_PROXY_CLIENT_MAPPINGS) { + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Too many tcp_proxy_client forward rules (max %d)", MAX_TCP_PROXY_CLIENT_MAPPINGS); return -1; } char buf[256]; @@ -471,29 +471,29 @@ static int parse_tcp_proxy(const char *key, const char *value, struct global_con char* arrow = strtok(NULL, " "); char* remote_str = strtok(NULL, ""); if (!local_port_str || !arrow || !remote_str || strcmp(arrow, "->") != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy forward format: %s (expected: port -> ip:port)", value); + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy_client forward format: %s (expected: port -> ip:port)", value); return -1; } int local_port = atoi(local_port_str); - if (local_port <= 0 || local_port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy forward local port: %s", local_port_str); return -1; } + if (local_port <= 0 || local_port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy_client forward local port: %s", local_port_str); return -1; } char* remote_ip_str = strtok(remote_str, ":"); char* remote_port_str = strtok(NULL, ""); if (!remote_ip_str || !remote_port_str) { - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy forward remote: %s (expected ip:port)", remote_str); + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy_client forward remote: %s (expected ip:port)", remote_str); return -1; } int remote_port = atoi(remote_port_str); - if (remote_port <= 0 || remote_port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy forward remote port: %s", remote_port_str); return -1; } + if (remote_port <= 0 || remote_port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid tcp_proxy_client forward remote port: %s", remote_port_str); return -1; } - int idx = global->tcp_proxy_mapping_count; - global->tcp_proxy_mappings[idx].local_port = (uint16_t)local_port; - strncpy(global->tcp_proxy_mappings[idx].remote_ip, remote_ip_str, sizeof(global->tcp_proxy_mappings[idx].remote_ip) - 1); - global->tcp_proxy_mappings[idx].remote_port = (uint16_t)remote_port; - global->tcp_proxy_mapping_count++; + int idx = global->tcp_proxy_client_mapping_count; + global->tcp_proxy_client_mappings[idx].local_port = (uint16_t)local_port; + strncpy(global->tcp_proxy_client_mappings[idx].remote_ip, remote_ip_str, sizeof(global->tcp_proxy_client_mappings[idx].remote_ip) - 1); + global->tcp_proxy_client_mappings[idx].remote_port = (uint16_t)remote_port; + global->tcp_proxy_client_mapping_count++; return 0; } - DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown tcp_proxy option '%s'. Valid: enabled, tun_name, tun_ip, mtu, via_node, forward", filename, line_num, key); + DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown tcp_proxy_client option '%s'. Valid: enabled, tun_name, tun_ip, mtu, via_node, forward", filename, line_num, key); return -1; } @@ -690,8 +690,8 @@ static section_type_t parse_section_header(const char *line, char *name, size_t if (strcasecmp(section, "control") == 0) return SECTION_CONTROL; if (strcasecmp(section, "allowed_keys") == 0) return SECTION_ALLOWED_KEYS; if (strcasecmp(section, "nat") == 0) return SECTION_NAT; - if (strcasecmp(section, "tcp_proxy") == 0) return SECTION_TCP_PROXY; - if (strcasecmp(section, "remote_proxy") == 0) return SECTION_REMOTE_PROXY; + if (strcasecmp(section, "tcp_proxy_client") == 0) return SECTION_TCP_PROXY_CLIENT; + if (strcasecmp(section, "tcp_proxy_server") == 0) return SECTION_TCP_PROXY_SERVER; char *colon = strchr(section, ':'); if (!colon) return SECTION_UNKNOWN; @@ -861,15 +861,15 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename) // parse_nat already printed the error } break; - case SECTION_TCP_PROXY: - cfg->global.tcp_proxy_enabled = 1; - if (parse_tcp_proxy(key, value, &cfg->global, filename, line_num) < 0) { - // parse_tcp_proxy already printed the error + case SECTION_TCP_PROXY_CLIENT: + cfg->global.tcp_proxy_client_enabled = 1; + if (parse_tcp_proxy_client(key, value, &cfg->global, filename, line_num) < 0) { + // parse_tcp_proxy_client already printed the error } break; - case SECTION_REMOTE_PROXY: - cfg->global.remote_proxy_enabled = 1; - // remote_proxy section has no options — its presence alone enables it + case SECTION_TCP_PROXY_SERVER: + cfg->global.tcp_proxy_server_enabled = 1; + // tcp_proxy_server section has no options — its presence alone enables it (void)key; (void)value; break; default: diff --git a/src/config_parser.h b/src/config_parser.h index 062216a0..88917e9b 100644 --- a/src/config_parser.h +++ b/src/config_parser.h @@ -82,8 +82,8 @@ struct CFG_ALLOWED_KEY { struct CFG_ALLOWED_KEY *next; }; -#define MAX_TCP_PROXY_MAPPINGS 32 -struct tcp_proxy_mapping_config { +#define MAX_TCP_PROXY_CLIENT_MAPPINGS 32 +struct tcp_proxy_client_mapping_config { uint16_t local_port; char remote_ip[64]; uint16_t remote_port; @@ -150,17 +150,17 @@ struct global_config { } nat_forwards[MAX_NAT_FORWARDS]; int nat_forward_count; - // TCP proxy configuration ([tcp_proxy] section) - int tcp_proxy_enabled; - char tcp_proxy_tun_name[16]; - char tcp_proxy_tun_ip[64]; - int tcp_proxy_mtu; - uint64_t tcp_proxy_via_node_id; // через этот узел проксируются все forward-правила - struct tcp_proxy_mapping_config tcp_proxy_mappings[MAX_TCP_PROXY_MAPPINGS]; - int tcp_proxy_mapping_count; - - // Remote proxy (exit node) configuration ([remote_proxy] section) - int remote_proxy_enabled; + // TCP proxy client configuration ([tcp_proxy_client] section) + int tcp_proxy_client_enabled; + char tcp_proxy_client_tun_name[16]; + char tcp_proxy_client_tun_ip[64]; + int tcp_proxy_client_mtu; + uint64_t tcp_proxy_client_via_node_id; // через этот узел проксируются все forward-правила + struct tcp_proxy_client_mapping_config tcp_proxy_client_mappings[MAX_TCP_PROXY_CLIENT_MAPPINGS]; + int tcp_proxy_client_mapping_count; + + // TCP proxy server (exit node) configuration ([tcp_proxy_server] section) + int tcp_proxy_server_enabled; }; struct utun_config { diff --git a/src/control_server.c b/src/control_server.c index e10c0777..d45ddea2 100644 --- a/src/control_server.c +++ b/src/control_server.c @@ -93,6 +93,7 @@ static void close_client(struct control_server* server, struct control_client* c static void send_conn_list(struct control_server* server, struct control_client* client, uint8_t seq_id); static void send_socket_list(struct control_server* server, struct control_client* client, uint8_t seq_id); static void send_metrics(struct control_server* server, struct control_client* client, uint8_t seq_id); +static void send_debug_config(struct control_server* server, struct control_client* client, uint8_t seq_id); static void send_error(struct control_client* client, uint8_t error_code, const char* msg, uint8_t seq_id); static struct ETCP_CONN* find_connection_by_peer_id(struct UTUN_INSTANCE* instance, uint64_t peer_id); @@ -613,7 +614,7 @@ static void handle_client_data(struct control_server* server, struct control_cli close_client(server, client); return; } - if (hdr->type < ETCPMON_CMD_LIST_CONN || hdr->type > ETCPMON_CMD_ACTION) { + if (hdr->type < ETCPMON_CMD_LIST_CONN || hdr->type > ETCPMON_CMD_SET_DEBUG_CONFIG) { DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Invalid command type from client: 0x%02X", hdr->type); close_client(server, client); return; @@ -696,6 +697,30 @@ static void handle_client_data(struct control_server* server, struct control_cli } break; + case ETCPMON_CMD_GET_DEBUG_CONFIG: + send_debug_config(server, client, req_seq); + break; + + case ETCPMON_CMD_SET_DEBUG_CONFIG: + if (payload_size >= 2) { + uint8_t global_lvl = payload[0]; + uint8_t cat_count = payload[1]; + if (cat_count > ETCPMON_MAX_DEBUG_CATEGORIES) cat_count = ETCPMON_MAX_DEBUG_CATEGORIES; + if (global_lvl <= DEBUG_LEVEL_TRACE) debug_set_level((debug_level_t)global_lvl); + const uint8_t* levels = payload + 2; + for (uint8_t i = 0; i < cat_count && i + 1 < DEBUG_CATEGORY_COUNT; i++) { + if (levels[i] <= DEBUG_LEVEL_TRACE) + debug_set_category_level((debug_category_t)(i + 1), (debug_level_t)levels[i]); + } + DEBUG_INFO(DEBUG_CATEGORY_CONTROL, "Debug config applied: global=%d categories=%d", global_lvl, cat_count); + if (server->log_file) { + fprintf(server->log_file, "%llu: [LOG] Debug config set: global=%d categories=%d\n", + (unsigned long long)get_timestamp_ms(), global_lvl, cat_count); + fflush(server->log_file); + } + } + break; + case ETCPMON_CMD_DISCONNECT: if (server->log_file) { fprintf(server->log_file, "%llu: [LOG] Client requested disconnect\n", @@ -1154,6 +1179,51 @@ static void send_error(struct control_client* client, uint8_t error_code, const u_free(buffer); } +static void send_debug_config(struct control_server* server, struct control_client* client, uint8_t seq_id) { + uint8_t cat_count = 0; + char names_buf[512]; + names_buf[0] = '\0'; + size_t off = 0; + for (int i = 1; i < DEBUG_CATEGORY_COUNT; i++) { + const char* name = debug_get_category_name(i); + size_t name_len = strlen(name); + if (off + name_len + 2 > sizeof(names_buf)) break; + if (cat_count > 0) { names_buf[off++] = ','; } + memcpy(names_buf + off, name, name_len); off += name_len; + cat_count++; + } + names_buf[off] = '\0'; + uint16_t names_len = (uint16_t)(off + 1); + + uint16_t payload_size = 2 + names_len + cat_count; + uint16_t rsp_size = sizeof(struct etcpmon_msg_header) + payload_size; + uint8_t* buffer = (uint8_t*)u_malloc(rsp_size); + if (!buffer) { DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Failed to allocate debug config buffer"); return; } + + struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)buffer; + etcpmon_build_header(hdr, payload_size, ETCPMON_RSP_DEBUG_CONFIG, seq_id); + + uint8_t* pay = buffer + sizeof(*hdr); + pay[0] = (uint8_t)g_debug_config.level; + pay[1] = cat_count; + memcpy(pay + 2, names_buf, names_len); + for (uint8_t i = 0; i < cat_count; i++) pay[2 + names_len + i] = (uint8_t)g_debug_config.category_levels[i + 1]; + + if (server->log_file) { + fprintf(server->log_file, "%llu: [LOG] Sent RSP_DEBUG_CONFIG global=%d categories=%d\n", + (unsigned long long)get_timestamp_ms(), g_debug_config.level, cat_count); + fflush(server->log_file); + } + +#ifdef _WIN32 + send(client->fd, (const char*)buffer, rsp_size, 0); +#else + send(client->fd, buffer, rsp_size, 0); +#endif + + u_free(buffer); +} + /* ============================================================================ * Helper Functions * ============================================================================ */ diff --git a/src/etcp.c b/src/etcp.c index a886228d..e8e5cb46 100644 --- a/src/etcp.c +++ b/src/etcp.c @@ -198,6 +198,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n etcp->instance = instance; etcp->input_queue = queue_new(instance->ua, 0, 0, 0, "ETCP input"); // No hash for input_queue + queue_set_threshold(etcp->input_queue, 0, 0); // Backpressure: ждать полного освобождения etcp->output_queue = queue_new(instance->ua, 0, 0, 0, "ETCP output"); // No hash for output_queue etcp->input_send_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, 0, 4, "input_send_q"); // Hash for send_q etcp->input_wait_ack = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, 0, 4, "input_wait_ack"); // Hash for wait_ack diff --git a/src/etcp_api.h b/src/etcp_api.h index 7c1364ba..5fd80c86 100644 --- a/src/etcp_api.h +++ b/src/etcp_api.h @@ -25,7 +25,7 @@ #define ETCP_ID_ROUTE_ENTRY 0x01 // Элемент роутинг-таблицы #define ETCP_ID_NAT 0x02 // NAT трафик между узлами #define ETCP_ID_SVC_ROUTE 0x03 // Маршрутизируемые сервисные пакеты (etcp_router) -#define ETCP_ID_TCP_PROXY 0x04 // TCP proxy через удаленный узел (remote_proxy) +#define ETCP_ID_TCP_PROXY 0x04 // TCP proxy через удаленный узел (tcp_proxy_server) #define ETCP_ID_UDP_PROXY 0x05 // UDP datagram прокси (client ↔ exit) #define ETCP_ID_ICMP_PROXY 0x06 // ICMP echo прокси (ping через exit) diff --git a/src/etcp_router.c b/src/etcp_router.c index 3b9f977c..88c30842 100644 --- a/src/etcp_router.c +++ b/src/etcp_router.c @@ -515,6 +515,22 @@ int etcp_router_input_q_count(struct UTUN_INSTANCE* inst, uint64_t node_id) { return queue_entry_count(conn->normalizer->input); } +void etcp_router_waiter_register(struct UTUN_INSTANCE* inst, uint64_t peer_node_id, + struct queue_waiter_handle* h) { + if (!inst || !inst->bgp || !h) return; + struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, peer_node_id); + if (!conn || !conn->normalizer || !conn->normalizer->input) return; + queue_waiter_wait(conn->normalizer->input, h); +} + +void etcp_router_waiter_cancel(struct UTUN_INSTANCE* inst, uint64_t peer_node_id, + struct queue_waiter_handle* h) { + if (!inst || !inst->bgp || !h) return; + struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, peer_node_id); + if (!conn || !conn->normalizer || !conn->normalizer->input) return; + queue_waiter_cancel(conn->normalizer->input, h); +} + // ==================================================================== // Seq-connection API // ==================================================================== diff --git a/src/etcp_router.h b/src/etcp_router.h index d65410cf..6bba766e 100644 --- a/src/etcp_router.h +++ b/src/etcp_router.h @@ -91,4 +91,10 @@ void etcp_router_conn_close_all_for_node(struct UTUN_INSTANCE* inst, uint64_t re // Возвращает количество пакетов в очереди normalizer->input для узла node_id int etcp_router_input_q_count(struct UTUN_INSTANCE* inst, uint64_t node_id); +// Backpressure: зарегистрировать/отменить waiter на normalizer->input очереди +void etcp_router_waiter_register(struct UTUN_INSTANCE* inst, uint64_t peer_node_id, + struct queue_waiter_handle* h); +void etcp_router_waiter_cancel(struct UTUN_INSTANCE* inst, uint64_t peer_node_id, + struct queue_waiter_handle* h); + #endif // ETCP_ROUTER_H diff --git a/src/lwip_tcp/lwip_tcp.h b/src/lwip_tcp/lwip_tcp.h index a75b7eed..0b363d91 100644 --- a/src/lwip_tcp/lwip_tcp.h +++ b/src/lwip_tcp/lwip_tcp.h @@ -185,7 +185,7 @@ struct tcp_pcb_listen { tcp_accept_fn accept; }; -// Context for the TCP module (one per tcp_proxy instance) +// Context for the TCP module (one per tcp_proxy_client instance) struct lwip_tcp_ctx { struct UASYNC *ua; tcp_output_fn output; diff --git a/src/pkt_normalizer.c b/src/pkt_normalizer.c index 6051322c..24373767 100644 --- a/src/pkt_normalizer.c +++ b/src/pkt_normalizer.c @@ -69,6 +69,8 @@ struct PKTNORM* pn_init(struct ETCP_CONN* etcp) { pn->recvpart = NULL; pn->flush_timer = NULL; + queue_waiter_handle_init(&pn->input_waiter_handle, etcp_input_ready_cb, pn); + return pn; } @@ -120,7 +122,7 @@ void pn_deinit(struct PKTNORM* pn) { // during etcp_connection_close() when drain_and_free_fragment_queue() is called if (pn->etcp) { if (pn->etcp->input_queue) { - queue_cancel_wait(pn->etcp->input_queue, &pn->etcp->input_queue->waiter); + queue_waiter_cancel(pn->etcp->input_queue, &pn->input_waiter_handle); } if (pn->etcp->output_queue) { queue_set_callback(pn->etcp->output_queue, NULL, NULL); @@ -213,7 +215,7 @@ static void packer_cb(struct ll_queue* q, void* arg) { struct PKTNORM* pn = (struct PKTNORM*)arg; if (!pn) return; DEBUG_TRACE(DEBUG_CATEGORY_NORMALIZER, "input_q->pn: waiting etcp input threshold"); - queue_wait_threshold(pn->etcp->input_queue, 0, 0, etcp_input_ready_cb, pn); + queue_waiter_wait(pn->etcp->input_queue, &pn->input_waiter_handle); } // Helper to send block to ETCP as ETCP_FRAGMENT diff --git a/src/pkt_normalizer.h b/src/pkt_normalizer.h index 848be88d..b97a21e5 100644 --- a/src/pkt_normalizer.h +++ b/src/pkt_normalizer.h @@ -36,6 +36,9 @@ struct PKTNORM { struct ll_entry* pending; // Partial processed input entry uint16_t pending_in_ptr; // Pointer in pending entry + // backpressure: + struct queue_waiter_handle input_waiter_handle; // waiter на etcp->input_queue + // unpacker: struct ll_entry* recvpart; // блок ожидающий заполнение // uint16_t recvpart_rem; // сколько байт осталось собрать (0 = ждём заголовок нового пакета) diff --git a/src/proxy/PROTOCOL.md b/src/proxy/PROTOCOL.md new file mode 100644 index 00000000..4e189ac0 --- /dev/null +++ b/src/proxy/PROTOCOL.md @@ -0,0 +1,477 @@ +## TCP Proxy Protocol — архитектура (v2, без CONNECTED) + +### 1. Протокольный формат (общий для client и server) + +``` +┌──────┬──────┬───────────────────────┬──────────────────────┐ +│svc_id│subcmd│ stream_id (4) │ data ... │ +│ 1B │ 1B │ │ │ +└──────┴──────┴───────────────────────┴──────────────────────┘ + HDR = 6 байт + +subcmd: + 0x01 CONNECT client→server : dest_ip(4)+dest_port(2) + 0x03 DATA ↔ bidirectional: payload + 0x04 CLOSE ↔ bidirectional: (нет данных) + 0x05 ERROR ↔ bidirectional: (нет данных) +``` + +--- + +### 2. Диаграмма состояний `tcp_proxy_client_conn` + +``` + ┌──────────────┐ + │ ALLOC + │ accept_cb: u_calloc, stream_id++ + │ CONNECT │ send CONNECT → exit + │ отправлен │ + └──────┬───────┘ + │ + ┌───────────┴───────────┐ + │ send успешен? │ + └───────────┬───────────┘ + Y │ N + ┌────────┘ └──────┐ + ▼ ▼ + ┌────────────┐ ┌──────────────┐ + │ ACTIVE │ │ FREE │ + │ traffic │ └──────────────┘ + └────┬──┬────┘ + │ │ + │ └─────────────────────────────────┐ + │ │ + ▼ lwIP FIN (p=NULL) ▼ ERROR от lwIP/exit + ┌──────────────┐ ┌──────────────┐ + │ TUN_CLOSED │ │ ERROR │ + │ tun_closed=1 │ │ error=1 │ + │ ↓ send_close │ │ send ERROR→ │ + │ после drain │ └──────┬───────┘ + │ tx_buf+lwIP │ │ + └──────┬───────┘ poll_cb: tcp_abort+free + │ + │ recv CLOSE от exit + ▼ + ┌───────────────────┐ + │ TUN+REM CLOSED │ + │ tun=1 rem=1 │ + │ ждём flush буферов│ + └────────┬──────────┘ + │ poll_cb: pending=0, unsent=NULL, unacked=NULL + │ (или sndbuf==0) + ▼ + ┌──────────────┐ + │ CLEANUP │ + │ free(pc) │ + └──────────────┘ + + +ДОП. СОСТОЯНИЯ: +┌─────────────────┐ send_data в ETCP вернул -1 (normalizer полон) +│ TX_BACKPRESSURE│ не tcp_recved() → окно lwIP=0 → recv_cb остановлен +│ tx_buf != NULL │ etcp_router_waiter_register() → ждём освобождения +│ tx_waiter pend │ waiter_cb: retry send_data → tcp_recved → окно открыто +└─────────────────┘ + +┌─────────────────┐ send_close/send_error вернул -1 +│ CLOSE_PENDING │ → retry в poll_cb +│ close_pending=1│ +└─────────────────┘ +``` + +--- + +### 3. Диаграмма состояний `tcp_proxy_server_conn` + +``` + ┌───────────────────┐ + │ CREATING │ recv CONNECT + │ socket() создан │ → неблокирующий connect() + │ sock = неблок. │ + └────────┬──────────┘ + │ + ┌────────────┴────────────┐ + │ connect() результат? │ + └────┬──────────┬──────────┘ + EINPROGRESS│ │ошибка сразу + ▼ ▼ + ┌──────────┐ ┌──────────────┐ + │CONNECTING│ │ ERROR │ send ERROR→client + │ждём write│ │ FREE │ + └────┬─────┘ └──────────────┘ + │ write_cb: SO_ERROR==0 + ▼ + ┌─────────────┐ + │ CONNECTED │ connected=1 + │ flush pend │ → flush pending_buf в сокет + └──────┬──────┘ + │ + ┌──────────────┼──────────────┐ + │ │ │ + client→DATA │ │ ERROR (sock/pipe) + (sock_send) │ ▼ + или pending_buf │ ┌──────────────┐ + │ │ │ ERROR │ send ERROR→client + │ │ │ FREE │ + │ sock EOF (n=0) │ │ + │ │ └──────────────┘ + │ ▼ + │ ┌──────────────────┐ + │ │ SOCK_CLOSED │ sock_closed=1 + │ │ out_buf/pause? │ если очереди пусты → send_close + │ │ ждём drain │ иначе ждём retry_cb/waiter_cb + │ └────────┬─────────┘ + │ │ очереди опустошены → send_close + │ │ если cli_closed → FREE + │ │ + client→CLOSE │ + │ │ + ▼ │ + ┌──────────────┐ │ + │ CLI_CLOSED │ │ cli_closed=1 + │ out_buf pend? │◄────────┘ если out_buf пуст → shutdown(SHUT_WR) + │ ждём flush │ иначе ждём sock_retry_cb → shutdown + └──────┬───────┘ + │ + └──────────────┐ + ▼ + ┌──────────────────┐ + │ cli && sock │ оба закрыты + очереди пусты + │ очереди пусты │ + └────────┬─────────┘ + ▼ + ┌──────────────┐ + │ FREE │ conn_free(rc) + └──────────────┘ + + +ДОП. СОСТОЯНИЯ: +┌─────────────────┐ send_msg в ETCP вернул -1 +│ PAUSED │ буферизуем в pause_buf, убираем read_id (пауза чтения) +│ pause_buf>0 │ etcp_router_waiter_register() → ждём освобождения +│ pause_waiter │ waiter_cb: retry send_msg → resume read_id +└─────────────────┘ + +┌─────────────────┐ CLOSE/ERROR не доставлен через ETCP +│ CLOSE_PENDING │ close_timer с backoff 50..5000 tb → ретрай +│ close_pending=1│ +└─────────────────┘ +``` + +--- + +### 4. DATA FLOW + +``` +══════════════════════════════════════════════════════════════════════ + APP → lwIP → recv_cb → ETCP → handle → socket → DEST + APP ← lwIP ← tcp_write ← ETCP ← read_cb ← socket ← DEST +══════════════════════════════════════════════════════════════════════ + + CLIENT (узел A) SERVER (узел B) + + APP (через TUN) DEST (реальный) + │ │ + ▼ TCP SYN/ACK (lwIP) ▼ TCP SYN/ACK (OS) + ┌──────────┐ ┌──────────┐ + │ lwIP │ │ socket │ + │ TCP │ │ (неблок) │ + └────┬─────┘ └────┬─────┘ + │ recv_cb │ read_cb + │ │ + ┌────▼────────────┐ ┌────▼───────────────┐ + │ tcp_proxy │ ETCP │ tcp_proxy │ + │ _client │◄═══════════════════════►│ _server │ + │ │ │ │ + │ ┌────────────┐ │ │ ┌──────────────┐ │ + │ │ recv_cb │ │──CONNECT+DATA──► │ │handle_connect│ │ + │ │(lwIP→ETCP) │ │ │ │handle_data │ │ + │ │ │ │ backpressure: │ └──────┬───────┘ │ + │ │ send fail: │ │ waiter на normalizer │ │ sock_send │ + │ │ tx_buf+ │ │ │ ┌──────▼───────┐ │ + │ │ waiter │ │ │ │ out_buf │ │ + │ │ окно lwIP=0│ │ │ │ pending_buf │ │ + │ └────────────┘ │ │ └─────────────┘ │ + │ │ │ │ send() │ + │ ┌────────────┐ │ │ ┌──────▼───────┐ │ + │ │handle_data │ │◄────DATA──────── │ │ read_cb │ │ + │ │handle_close│ │ │ │(sock→ETCP) │ │ + │ └─────┬──────┘ │ │ │ │ │ + │ │ │ │ │ send fail: │ │ + │ ┌─────▼──────┐ │ │ │ pause_buf+ │ │ + │ │ to_lwip │ │ │ │ waiter+ │ │ + │ │ (очередь) │ │ │ │ пауза read_id│ │ + │ └─────┬──────┘ │ │ └─────────────┘ │ + │ │tcp_write│ │ │ + │ ▼ │ └───────────────────┘ + │ lwIP→TUN→APP │ + └─────────────────┘ +``` + +--- + +### 5. BACKPRESSURE (lwIP→ETCP и socket→ETCP) + +``` +══════════════════════════════════════════════════════════════════════ + + Используется встроенный механизм ll_queue threshold waiter: + etcp_router_waiter_register(inst, peer_node_id, &waiter) + → внутри: queue_waiter_wait(normalizer->input_queue, &waiter) + etcp_router_waiter_cancel(inst, peer_node_id, &waiter) + + CLIENT (lwIP → ETCP): + recv_cb(buf, len): + ret = send_data(buf, len) + if ret == 0: tcp_recved(len) + if ret < 0: + tx_buf = copy(buf), tx_len = len + НЕ tcp_recved() → окно lwIP = 0, recv_cb больше не вызывается + etcp_router_waiter_register(&tx_waiter) + + tx_waiter callback: + ret = send_data(tx_buf, tx_len) + if ret == 0: + tcp_recved(tx_len) → окно открыто, lwIP возобновляет recv_cb + free(tx_buf) + if tun_closed → send_close() + // если ret < 0 → waiter остаётся, normalizer вызовет снова + + SERVER (socket → ETCP): + read_cb(buf, n): + ret = send_msg(DATA, buf, n) + if ret == 0: ok + if ret < 0: + pause_buf = copy(buf), pause_len = n + uasync_remove_socket_t(sock) → пауза чтения с сокета + etcp_router_waiter_register(&pause_waiter) + + pause_waiter callback: + ret = send_msg(DATA, pause_buf, pause_len) + if ret == 0: + free(pause_buf) + uasync_add_socket_t(sock) → возобновляем чтение + if sock_closed → send_close() + // если ret < 0 → waiter остаётся +``` + +--- + +### 6. Последовательность: HANDSHAKE (упрощённый, без CONNECTED) + +``` +CLIENT (lwIP) CLIENT PROXY ETCP SERVER PROXY SOCKET +───────────────────────────────────────────────────────────────────────────────────────── + + tcp_proxy_client_accept_cb() CONNECT + ├─ u_calloc(conn) ────────────────────────────→ tcp_proxy_server_handle_connect() + ├─ stream_id++ ├─ socket(AF_INET,SOCK_STREAM) + ├─ send_connect() ├─ socket_set_nonblocking() + │ [dest_ip(4)+dest_port(2)] ├─ uasync_add_socket_t(read/write/error) + ├─ conn→conns list ├─ connect(неблок.) → EINPROGRESS + │ └─ rc→conns list + ▼ + ACTIVE — данные идут СРАЗУ (без ожидания CONNECTED) + │ + │ DATA ...connect завершён... + ├──────────────────────────────────────────────────→ tcp_proxy_server_sock_write_cb() + │ ├─ connected=1 + │ DATA └─ flush pending_buf в сокет + ├──────────────────────────────────────────────────→ + │ + │ DATA ←─ tcp_proxy_server_sock_read_cb() + │◄────────────────────────────────────────────── (socket recv → ETCP) + │ + └──→ трафик идёт в обе стороны + + При ошибке connect: + ...connect fail... → tcp_proxy_server_send_error() → client получает ERROR, error=1, cleanup +``` + +--- + +### 7. Последовательность: CLOSE (drain очередей → close в конце) + +``` +CLIENT (lwIP) CLIENT PROXY ETCP SERVER PROXY SOCKET +───────────────────────────────────────────────────────────────────────────────────────── + +══ Вариант А: lwIP закрывает первым ══ + + tcp_proxy_client_recv_cb(p=NULL) + ├─ tun_closed=1 + │ если tx_buf пуст → send_close CLOSE + │ если tx_buf pend → waiter досылает ──────────→ tcp_proxy_server_handle_close() + │ потом send_close ├─ cli_closed=1 + └─ │ если out_buf пуст → shutdown(SHUT_WR) + │ иначе ждём sock_retry_cb + │ + ...out_buf дослан... + sock_retry_cb: + shutdown(SHUT_WR) + │ + ...сокет дочитывает остаток... + │ + tcp_proxy_server_sock_read_cb(n=0) + ├─ sock_closed=1 + │ если pause_buf пуст → send_close + │ иначе ждём waiter_cb + │ + CLOSE │ + tcp_proxy_client_handle_close() ◄────────────────────────┘ + └─ rem_closed=1 + │ + poll_cb: tun=1, rem=1, to_lwip пуст, unsent=NULL, unacked=NULL + ├─ tcp_close(pcb) + └─ conn_free() + + +══ Вариант Б: серверный сокет закрывается первым ══ + + tcp_proxy_server_sock_read_cb(n=0) + ├─ sock_closed=1 + │ если out_buf+pause_buf пусты → send_close + │ иначе ждём retry/waiter + CLOSE │ + tcp_proxy_client_handle_close() ◄────────────────────────┘ + └─ rem_closed=1 + │ + ▼ + ...приложение закрывает TCP... + │ + tcp_proxy_client_recv_cb(p=NULL) + ├─ tun_closed=1 + │ если tx_buf пуст → send_close + └─ + + poll_cb: оба закрыты, буферы пусты → cleanup + + +══ Вариант В: Ошибка / аномалия ══ + + ERROR + ─────────────────────── (или ←) ────────── + error=1 + │ + ▼ + poll_cb: tcp_abort(pcb) → немедленная очистка + или: rc→error=1 → conn_free + + +══ Вариант Г: conn не найден / closed / error ══ + + Любой пришедший пакет (DATA/CLOSE/ERROR) для conn в закрытом состоянии: + → tcp_proxy_*_send_msg(ERROR, stream_id) + → удалённая сторона получает ERROR и немедленно закрывается +``` + +--- + +### 8. Диспетчеризация в `tcp_proxy_client_etcp_recv_cb()` + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ tcp_proxy_client_etcp_recv_cb() │ +│ (единая точка входа для обоих направлений) │ +├────────────────────────────────────────────────────────────────────────┤ +│ │ +│ subcmd == CONNECT? │ +│ └── tcp_proxy_server_handle_connect() ←── всегда сервер │ +│ │ +│ stream_id в server.conns? (если server.enabled) │ +│ ├── DATA → tcp_proxy_server_handle_data() │ +│ ├── CLOSE → tcp_proxy_server_handle_close() │ +│ └── ERROR → rc->error=1; conn_free() │ +│ │ +│ stream_id в client.conns? (если client proxy != NULL) │ +│ ├── DATA → tcp_proxy_client_handle_data() │ +│ ├── CLOSE → tcp_proxy_client_handle_close() │ +│ └── ERROR → tcp_proxy_client_handle_error() │ +│ │ +│ Приоритет: │ +│ 1. CONNECT всегда интерпретируется сервером │ +│ 2. Если server.enabled И stream_id найден в server.conns → server │ +│ 3. Иначе если client proxy != NULL → client │ +│ 4. Иначе drop │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +### 9. INIT + +``` +CLIENT SERVER +───────────────────────────────────────────────────────── + +utun_instance.c + │ + ├─ tcp_proxy_client_create() tcp_proxy_server_init() + │ ├─ tun_init_nat() (TUN iface) ├─ memset(ctx,0) + │ ├─ lwip_tcp_init() (TCP стек) ├─ ctx->enabled = config ... + │ ├─ etcp_router_bind( ├─ etcp_router_bind( + │ │ ETCP_ID_TCP_PROXY, │ ETCP_ID_TCP_PROXY, + │ │ tcp_proxy_client_etcp_recv_cb) │ tcp_proxy_client_etcp_recv_cb) + │ ├─ udp_proxy_init() ├─ udp_proxy_init() + │ └─ icmp_proxy_init() └─ icmp_proxy_init() (если client не активен) + │ + └─ ОБА слушают ETCP_ID_TCP_PROXY через один обработчик +``` + +--- + +### 10. Поля структур + +``` +tcp_proxy_client_conn: + stream_id — уникальный ID потока + tun_closed — lwIP/FIN (сторона TUN) + rem_closed — CLOSE от exit + error — немедленная очистка + close_sent — CLOSE/ERROR отправлен + close_pending — отправка не удалась, ретрай + to_lwip — очередь DATA от exit → lwIP (feed_from_transport) + pcb — lwIP tcp_pcb + tx_buf — буфер при backpressure lwIP→ETCP (send_data fail) + tx_len — длина tx_buf + tx_waiter — ll_queue waiter на normalizer.input_queue + +tcp_proxy_server_conn: + stream_id — уникальный ID потока + cli_closed — клиент прислал CLOSE + sock_closed — сокет получил EOF + error — ошибка + connected — сокет подключился (connect завершён) + close_pending — CLOSE/ERROR не доставлен, ретрай + pending_buf — буфер данных от клиента до завершения connect + out_buf — буфер отправки в сокет (неблокирующий send, EAGAIN retry) + out_off/out_len — offset/размер out_buf + out_timer — таймер повтора send в сокет + out_backoff — backoff для retry send в сокет + pause_buf — буфер при backpressure socket→ETCP (send_msg fail) + pause_len — длина pause_buf + pause_waiter — ll_queue waiter на normalizer.input_queue + close_timer — таймер повтора CLOSE/ERROR + close_backoff — backoff для retry CLOSE/ERROR + sock — OS сокет к адресату + read_id — handle uasync для событий сокета +``` + +--- + +### 11. etcp_router waiter API + +``` +// etcp_router.h +void etcp_router_waiter_register(struct UTUN_INSTANCE* inst, + uint64_t peer_node_id, struct queue_waiter_handle* h); +void etcp_router_waiter_cancel(struct UTUN_INSTANCE* inst, + uint64_t peer_node_id, struct queue_waiter_handle* h); + +// Внутри: +// conn = route_bgp_find_conn_for_node(peer_node_id) +// queue_waiter_wait(conn->normalizer->input, h) +// queue_waiter_cancel(conn->normalizer->input, h) +// +// normalizer.input_queue уже имеет threshold=0 (ждёт полного освобождения) +// через queue_set_threshold(input_queue, 0, 0) в etcp.c +``` diff --git a/src/icmp_proxy.c b/src/proxy/icmp_proxy.c similarity index 98% rename from src/icmp_proxy.c rename to src/proxy/icmp_proxy.c index 3fc0c9c0..dee23536 100644 --- a/src/icmp_proxy.c +++ b/src/proxy/icmp_proxy.c @@ -232,12 +232,12 @@ int icmp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id, int icmp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, uint32_t dst_ip, uint32_t src_ip, uint16_t echo_id, uint16_t echo_seq, const uint8_t* payload, size_t payload_len) { - if (!inst || !inst->tcp_proxy || !inst->tcp_proxy->tun) { + if (!inst || !inst->tcp_proxy_client || !inst->tcp_proxy_client->tun) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "icmp_proxy: deliver_reply failed — no %s", - !inst ? "inst" : !inst->tcp_proxy ? "tcp_proxy" : "TUN"); + !inst ? "inst" : !inst->tcp_proxy_client ? "tcp_proxy_client" : "TUN"); return -1; } - struct tun_if* tun = inst->tcp_proxy->tun; + struct tun_if* tun = inst->tcp_proxy_client->tun; size_t icmp_len = ICMP_MINLEN + payload_len; size_t pkt_len = 20 + icmp_len; @@ -306,7 +306,7 @@ int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { if (!ctx) return -1; ctx->inst = inst; ctx->ua = ua; ctx->raw_sock = SOCKET_INVALID; ctx->request_timeout_tb = ICMP_TIMEOUT_TB; - ctx->is_exit = inst->remote_proxy.enabled; + ctx->is_exit = inst->tcp_proxy_server.enabled; ctx->test_loopback = 0; ctx->expire_timer = NULL; g_icmp_ctx = ctx; diff --git a/src/icmp_proxy.h b/src/proxy/icmp_proxy.h similarity index 100% rename from src/icmp_proxy.h rename to src/proxy/icmp_proxy.h diff --git a/src/tcp_proxy.c b/src/proxy/tcp_proxy_client.c similarity index 55% rename from src/tcp_proxy.c rename to src/proxy/tcp_proxy_client.c index ff81c3a2..6dac168f 100644 --- a/src/tcp_proxy.c +++ b/src/proxy/tcp_proxy_client.c @@ -1,5 +1,5 @@ -// tcp_proxy.c — TCP прокси: стек lwIP TCP → ETCP → удалённый exit узел -#include "tcp_proxy.h" +// tcp_proxy_client.c — TCP прокси-клиент: стек lwIP TCP → ETCP → удалённый exit узел +#include "tcp_proxy_client.h" #include "lwip_tcp/lwip_tcp.h" #include "lwip_tcp/lwip_tcp_priv.h" #include "lwip_tcp/lwip_tcp_opts.h" @@ -9,7 +9,7 @@ #include "etcp.h" #include "etcp_api.h" #include "etcp_router.h" -#include "remote_proxy.h" +#include "tcp_proxy_server.h" #include "udp_proxy.h" #include "icmp_proxy.h" #include "../lib/u_async.h" @@ -32,28 +32,29 @@ // ==================================================================== // Предварительные объявления // ==================================================================== -static err_t proxy_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); -static err_t proxy_sent_cb(void *arg, struct tcp_pcb *pcb, uint16_t len); -static void proxy_err_cb(void *arg, err_t err); -static err_t proxy_poll_cb(void *arg, struct tcp_pcb *pcb); -static err_t proxy_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err); -static err_t tcp_output_cb(void *arg, struct pbuf *p, uint32_t src_ip, uint32_t dst_ip); -static void proxy_feed_from_transport(struct proxy_conn *pc); -static struct ll_entry* entry_from_data(struct memory_pool* pool, const uint8_t* data, uint16_t len); -static void proxy_conn_free(struct proxy_conn *pc); -static int proxy_send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len); -static int send_data(struct proxy_conn* pc, const uint8_t* data, uint16_t len); +static err_t tcp_proxy_client_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err); +static err_t tcp_proxy_client_sent_cb(void *arg, struct tcp_pcb *pcb, uint16_t len); +static void tcp_proxy_client_err_cb(void *arg, err_t err); +static err_t tcp_proxy_client_poll_cb(void *arg, struct tcp_pcb *pcb); +static err_t tcp_proxy_client_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err); +static err_t tcp_proxy_client_output_cb(void *arg, struct pbuf *p, uint32_t src_ip, uint32_t dst_ip); +static void tcp_proxy_client_feed_from_transport(struct tcp_proxy_client_conn *pc); +static struct ll_entry* tcp_proxy_client_entry_from_data(struct memory_pool* pool, const uint8_t* data, uint16_t len); +static void tcp_proxy_client_conn_free(struct tcp_proxy_client_conn *pc); +static int tcp_proxy_client_send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len); +static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const uint8_t* data, uint16_t len); +static void tcp_proxy_client_tx_waiter_cb(struct ll_queue* q, void* arg); // ==================================================================== // Помощник ll_entry // ==================================================================== -static struct ll_entry* entry_from_data(struct memory_pool* pool, const uint8_t* data, uint16_t len) { +static struct ll_entry* tcp_proxy_client_entry_from_data(struct memory_pool* pool, const uint8_t* data, uint16_t len) { struct ll_entry* e = queue_entry_new_from_pool(pool); - if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "entry_from_data: pool exhausted"); return NULL; } + if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_entry_from_data: pool exhausted"); return NULL; } e->len = 0; e->dgram = NULL; if (len > 0) { uint8_t* buf = u_malloc(len); - if (!buf) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "entry_from_data: malloc(%u) failed", len); queue_entry_free(e); return NULL; } + if (!buf) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_entry_from_data: malloc(%u) failed", len); queue_entry_free(e); return NULL; } memcpy(buf, data, len); e->dgram = buf; e->len = len; } return e; @@ -62,12 +63,12 @@ static struct ll_entry* entry_from_data(struct memory_pool* pool, const uint8_t* // ==================================================================== // Протокол: сборка и отправка сообщений прокси через ETCP // ==================================================================== -static int proxy_send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, +static int tcp_proxy_client_send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len) { struct ll_entry* e = queue_entry_new(0); - if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "proxy_send_msg: queue_entry_new failed subcmd=%02x sid=%08x", subcmd, sid); return -1; } + if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_send_msg: queue_entry_new failed subcmd=%02x sid=%08x", subcmd, sid); return -1; } e->dgram = u_malloc(TCP_PROXY_HDR_SIZE + len); - if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "proxy_send_msg: malloc(%zu) failed subcmd=%02x sid=%08x", TCP_PROXY_HDR_SIZE + len, subcmd, sid); queue_entry_free(e); return -1; } + if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_send_msg: malloc(%zu) failed subcmd=%02x sid=%08x", TCP_PROXY_HDR_SIZE + len, subcmd, sid); queue_entry_free(e); return -1; } e->dgram[0] = ETCP_ID_TCP_PROXY; e->dgram[1] = subcmd; memcpy(e->dgram + 2, &sid, 4); @@ -76,24 +77,24 @@ static int proxy_send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subc return etcp_route_send(inst, dst, e); } -static int send_connect(struct proxy_conn* pc) { +static int tcp_proxy_client_send_connect(struct tcp_proxy_client_conn* pc) { uint8_t buf[6]; memcpy(buf, pc->dest_ip, 4); memcpy(buf + 4, &pc->dest_port, 2); - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy: CONNECT sid=%08x to %d.%d.%d.%d:%d via node %016llx", + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client: CONNECT sid=%08x to %d.%d.%d.%d:%d via node %016llx", pc->stream_id, pc->dest_ip[0], pc->dest_ip[1], pc->dest_ip[2], pc->dest_ip[3], ntohs(pc->dest_port), (unsigned long long)pc->proxy->via_node_id); - return proxy_send_msg(pc->proxy->inst, pc->proxy->via_node_id, + return tcp_proxy_client_send_msg(pc->proxy->inst, pc->proxy->via_node_id, TCP_PROXY_SUBCMD_CONNECT, pc->stream_id, buf, 6); } -static int send_data(struct proxy_conn* pc, const uint8_t* data, uint16_t len) { +static int tcp_proxy_client_send_data(struct tcp_proxy_client_conn* pc, const uint8_t* data, uint16_t len) { DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "PROXY SEND sid=%08x len=%u", pc->stream_id, len); - return proxy_send_msg(pc->proxy->inst, pc->proxy->via_node_id, + return tcp_proxy_client_send_msg(pc->proxy->inst, pc->proxy->via_node_id, TCP_PROXY_SUBCMD_DATA, pc->stream_id, data, len); } -static int send_close(struct proxy_conn* pc) { - int ret = proxy_send_msg(pc->proxy->inst, pc->proxy->via_node_id, +static int tcp_proxy_client_send_close(struct tcp_proxy_client_conn* pc) { + int ret = tcp_proxy_client_send_msg(pc->proxy->inst, pc->proxy->via_node_id, TCP_PROXY_SUBCMD_CLOSE, pc->stream_id, NULL, 0); if (ret < 0) { pc->close_pending = 1; return -1; } pc->close_pending = 0; @@ -101,8 +102,8 @@ static int send_close(struct proxy_conn* pc) { return 0; } -static int send_error(struct proxy_conn* pc) { - int ret = proxy_send_msg(pc->proxy->inst, pc->proxy->via_node_id, +static int tcp_proxy_client_send_error(struct tcp_proxy_client_conn* pc) { + int ret = tcp_proxy_client_send_msg(pc->proxy->inst, pc->proxy->via_node_id, TCP_PROXY_SUBCMD_ERROR, pc->stream_id, NULL, 0); if (ret < 0) { pc->close_pending = 1; return -1; } pc->close_pending = 0; @@ -113,8 +114,8 @@ static int send_error(struct proxy_conn* pc) { // ==================================================================== // Вывод: lwIP TCP отправляет IP пакеты через этот callback // ==================================================================== -static err_t tcp_output_cb(void *arg, struct pbuf *p, uint32_t src_ip, uint32_t dst_ip) { - struct tcp_proxy *proxy = (struct tcp_proxy *)arg; +static err_t tcp_proxy_client_output_cb(void *arg, struct pbuf *p, uint32_t src_ip, uint32_t dst_ip) { + struct tcp_proxy_client *proxy = (struct tcp_proxy_client *)arg; (void)src_ip; (void)dst_ip; uint16_t len = p->tot_len; if (len > 2000) return LERR_BUF; @@ -134,7 +135,7 @@ static err_t tcp_output_cb(void *arg, struct pbuf *p, uint32_t src_ip, uint32_t // ==================================================================== // Обработчик не-TCP (UDP/ICMP прокси) // ==================================================================== -static int tcp_proxy_handle_non_tcp(struct tcp_proxy* p, uint8_t* buf, size_t len) { +static int tcp_proxy_client_handle_non_tcp(struct tcp_proxy_client* p, uint8_t* buf, size_t len) { if (len < 20) return 0; uint8_t ip_ver = (buf[0] >> 4) & 0xF; if (ip_ver != 4) return 0; @@ -164,7 +165,7 @@ static int tcp_proxy_handle_non_tcp(struct tcp_proxy* p, uint8_t* buf, size_t le // ==================================================================== // Помощник: передача данных из очереди to_lwip в lwIP TCP // ==================================================================== -static void proxy_feed_from_transport(struct proxy_conn *pc) { +static void tcp_proxy_client_feed_from_transport(struct tcp_proxy_client_conn *pc) { if (!pc->to_lwip || !pc->pcb || pc->tun_closed) return; int sent_any = 0; uint32_t q_pre = queue_entry_count(pc->to_lwip); @@ -196,12 +197,12 @@ static void proxy_feed_from_transport(struct proxy_conn *pc) { // ==================================================================== // lwIP TCP коллбэки // ==================================================================== -static err_t proxy_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err) { - struct tcp_proxy *p = (struct tcp_proxy *)arg; +static err_t tcp_proxy_client_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err) { + struct tcp_proxy_client *p = (struct tcp_proxy_client *)arg; if (err != LERR_OK || !newpcb) return LERR_ABRT; - struct proxy_conn *pc = u_calloc(1, sizeof(struct proxy_conn)); - if (!pc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy: accept alloc failed"); return LERR_MEM; } + struct tcp_proxy_client_conn *pc = u_calloc(1, sizeof(struct tcp_proxy_client_conn)); + if (!pc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy client: accept alloc failed"); return LERR_MEM; } pc->proxy = p; pc->pcb = newpcb; pc->stream_id = ++p->next_stream_id; int i; @@ -219,24 +220,41 @@ static err_t proxy_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err) { } tcp_arg(newpcb, pc); - tcp_recv(newpcb, proxy_recv_cb); - tcp_sent(newpcb, proxy_sent_cb); - tcp_err(newpcb, proxy_err_cb); - tcp_poll(newpcb, proxy_poll_cb, 2); + tcp_recv(newpcb, tcp_proxy_client_recv_cb); + tcp_sent(newpcb, tcp_proxy_client_sent_cb); + tcp_err(newpcb, tcp_proxy_client_err_cb); + tcp_poll(newpcb, tcp_proxy_client_poll_cb, 2); tcp_nagle_disable(newpcb); pc->to_lwip = queue_new(p->ua, 0, 0, 0, "to_lwip"); + queue_waiter_handle_init(&pc->tx_waiter, tcp_proxy_client_tx_waiter_cb, pc); - if (send_connect(pc) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy: send_connect failed sid=%08x to %d.%d.%d.%d:%d", pc->stream_id, pc->dest_ip[0], pc->dest_ip[1], pc->dest_ip[2], pc->dest_ip[3], ntohs(pc->dest_port)); u_free(pc); return LERR_MEM; } + if (tcp_proxy_client_send_connect(pc) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy client: send_connect failed sid=%08x to %d.%d.%d.%d:%d", pc->stream_id, pc->dest_ip[0], pc->dest_ip[1], pc->dest_ip[2], pc->dest_ip[3], ntohs(pc->dest_port)); u_free(pc); return LERR_MEM; } pc->next = p->conns; p->conns = pc; p->conn_count++; - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy: new conn sid=%08x local_port=%u -> %d.%d.%d.%d:%d total_conns=%d snd_wnd=%u mss=%u", + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client: new conn sid=%08x local_port=%u -> %d.%d.%d.%d:%d total_conns=%d snd_wnd=%u mss=%u", pc->stream_id, newpcb->local_port, pc->dest_ip[0], pc->dest_ip[1], pc->dest_ip[2], pc->dest_ip[3], ntohs(pc->dest_port), p->conn_count, newpcb->snd_wnd, newpcb->mss); return LERR_OK; } -static err_t proxy_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { - struct proxy_conn *pc = (struct proxy_conn *)arg; +// ==================================================================== +// Backpressure: waiter callback при освобождении normalizer очереди +// ==================================================================== +static void tcp_proxy_client_tx_waiter_cb(struct ll_queue* q, void* arg) { + (void)q; + struct tcp_proxy_client_conn* pc = (struct tcp_proxy_client_conn*)arg; + if (!pc || !pc->tx_buf) return; + int ret = tcp_proxy_client_send_data(pc, pc->tx_buf, pc->tx_len); + if (ret == 0) { + tcp_recved(pc->pcb, pc->tx_len); + u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; + if (pc->tun_closed && !pc->close_sent && !pc->close_pending) + tcp_proxy_client_send_close(pc); + } +} + +static err_t tcp_proxy_client_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { + struct tcp_proxy_client_conn *pc = (struct tcp_proxy_client_conn *)arg; if (!pc) { if (p) pbuf_free(p); return LERR_OK; } if (p == NULL || err != LERR_OK) { @@ -246,8 +264,8 @@ static err_t proxy_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t { uint16_t wnd_gap = TCP_WND_MAX(pcb) - pcb->rcv_wnd; if (wnd_gap > 0) tcp_recved(pcb, wnd_gap); } pc->tun_closed = 1; - if (!pc->close_sent && !pc->close_pending) { - if (send_close(pc) < 0) DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY FIN send_close failed sid=%08x", pc->stream_id); + if (!pc->tx_buf && !pc->close_sent && !pc->close_pending) { + if (tcp_proxy_client_send_close(pc) < 0) DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY FIN send_close failed sid=%08x", pc->stream_id); } return LERR_OK; } @@ -257,49 +275,55 @@ static err_t proxy_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t tcp_recved(pcb, len); pbuf_free(p); return LERR_OK; } + if (pc->tx_buf) { pbuf_free(p); return LERR_OK; } + uint8_t *data = u_malloc(len); if (data) { pbuf_copy_partial(p, data, len, 0); - if (send_data(pc, data, len) < 0) DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY recv send_data failed sid=%08x len=%u", pc->stream_id, len); - u_free(data); + DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "PROXY RECV client->exit sid=%08x len=%u", pc->stream_id, len); + int ret = tcp_proxy_client_send_data(pc, data, len); + if (ret == 0) { tcp_recved(pcb, len); u_free(data); } + else { + pc->tx_buf = data; pc->tx_len = len; + etcp_router_waiter_register(pc->proxy->inst, pc->proxy->via_node_id, &pc->tx_waiter); + } } else DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY recv malloc(%u) failed sid=%08x", len, pc->stream_id); - DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "PROXY RECV client->exit sid=%08x len=%u", pc->stream_id, len); - tcp_recved(pcb, len); pbuf_free(p); + pbuf_free(p); return LERR_OK; } -static err_t proxy_sent_cb(void *arg, struct tcp_pcb *pcb, uint16_t len) { +static err_t tcp_proxy_client_sent_cb(void *arg, struct tcp_pcb *pcb, uint16_t len) { (void)len; - struct proxy_conn *pc = (struct proxy_conn *)arg; + struct tcp_proxy_client_conn *pc = (struct tcp_proxy_client_conn *)arg; if (!pc) return LERR_OK; - proxy_feed_from_transport(pc); + tcp_proxy_client_feed_from_transport(pc); return LERR_OK; } -static void proxy_err_cb(void *arg, err_t err) { - struct proxy_conn *pc = (struct proxy_conn *)arg; - if (!pc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY proxy_err_cb: pc=NULL err=%d", err); return; } - DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy: error %d sid=%08x pcb_state=%u tun_closed=%d rem_closed=%d connected=%d", - err, pc->stream_id, pc->pcb ? pc->pcb->state : 0, pc->tun_closed, pc->rem_closed, pc->connected); +static void tcp_proxy_client_err_cb(void *arg, err_t err) { + struct tcp_proxy_client_conn *pc = (struct tcp_proxy_client_conn *)arg; + if (!pc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY tcp_proxy_client_err_cb: pc=NULL err=%d", err); return; } + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy client: error %d sid=%08x pcb_state=%u tun_closed=%d rem_closed=%d", + err, pc->stream_id, pc->pcb ? pc->pcb->state : 0, pc->tun_closed, pc->rem_closed); pc->error = 1; - if (send_error(pc) < 0) DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY send_error failed sid=%08x", pc->stream_id); + if (tcp_proxy_client_send_error(pc) < 0) DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY send_error failed sid=%08x", pc->stream_id); } -static err_t proxy_poll_cb(void *arg, struct tcp_pcb *pcb) { - struct proxy_conn *pc = (struct proxy_conn *)arg; +static err_t tcp_proxy_client_poll_cb(void *arg, struct tcp_pcb *pcb) { + struct tcp_proxy_client_conn *pc = (struct tcp_proxy_client_conn *)arg; if (!pc) return LERR_OK; if (pc->error) { - DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY CLEANUP error sid=%08x tun_closed=%d rem_closed=%d connected=%d", pc->stream_id, pc->tun_closed, pc->rem_closed, pc->connected); + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY CLEANUP error sid=%08x tun_closed=%d rem_closed=%d", pc->stream_id, pc->tun_closed, pc->rem_closed); if (pc->pcb) { tcp_arg(pc->pcb, NULL); tcp_abort(pc->pcb); pc->pcb = NULL; } - proxy_conn_free(pc); + tcp_proxy_client_conn_free(pc); return LERR_OK; } /* Retry pending CLOSE/ERROR if previous send failed */ if (pc->close_pending) { - if (pc->error) send_error(pc); - else send_close(pc); + if (pc->error) tcp_proxy_client_send_error(pc); + else tcp_proxy_client_send_close(pc); } if (pc->tun_closed && pc->rem_closed && pc->pcb) { @@ -316,7 +340,7 @@ static err_t proxy_poll_cb(void *arg, struct tcp_pcb *pcb) { if (wnd_gap > 0) tcp_recved(save, wnd_gap); } while (save->unsent) { struct tcp_seg *seg = save->unsent; save->unsent = seg->next; u_free(seg); } tcp_close(save); - proxy_conn_free(pc); + tcp_proxy_client_conn_free(pc); } } return LERR_OK; @@ -325,38 +349,38 @@ static err_t proxy_poll_cb(void *arg, struct tcp_pcb *pcb) { // ==================================================================== // Обеспечить динамический listen pcb для прозрачного исходящего TCP проксирования // ==================================================================== -static void tcp_proxy_ensure_outbound_listen(struct tcp_proxy* p, uint16_t dport_net) { +static void tcp_proxy_client_ensure_outbound_listen(struct tcp_proxy_client* p, uint16_t dport_net) { uint16_t dport_host = ntohs(dport_net); struct tcp_pcb* lp = p->lwip->listen_pcbs; while (lp) { if (lp->local_port == dport_host) return; lp = lp->next; } struct tcp_pcb* lpcb = tcp_new(p->lwip); - if (!lpcb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy: tcp_new failed for dynamic listen port %u", dport_host); return; } + if (!lpcb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy client: tcp_new failed for dynamic listen port %u", dport_host); return; } tcp_bind(lpcb, INADDR_ANY, dport_net); struct tcp_pcb* listen_pcb = tcp_listen(lpcb); if (listen_pcb) { - tcp_arg(listen_pcb, p); tcp_accept(listen_pcb, proxy_accept_cb); - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy: dynamic listen on port %u", dport_host); + tcp_arg(listen_pcb, p); tcp_accept(listen_pcb, tcp_proxy_client_accept_cb); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client: dynamic listen on port %u", dport_host); } } // ==================================================================== // Ввод: IP пакет → lwip_tcp (из TUN output_queue) // ==================================================================== -static void tcp_proxy_tun_input(struct ll_queue* q, void* arg) { - struct tcp_proxy* p = (struct tcp_proxy*)arg; +static void tcp_proxy_client_tun_input(struct ll_queue* q, void* arg) { + struct tcp_proxy_client* p = (struct tcp_proxy_client*)arg; struct ll_entry* entry = queue_data_get(q); if (!entry) return; if (entry->dgram && entry->len > 1) { uint8_t* ip = entry->dgram + 1; size_t len = entry->len - 1; if (len > 20 && len <= 2000) { - if (!tcp_proxy_handle_non_tcp(p, ip, len)) { + if (!tcp_proxy_client_handle_non_tcp(p, ip, len)) { uint8_t proto = ip[9]; if (proto == IPPROTO_TCP) { uint16_t ip_hdr_len = (ip[0] & 0x0F) * 4; uint16_t ip_total = ((uint16_t)ip[2] << 8) | ip[3]; if (ip_hdr_len >= 20 && ip_total >= ip_hdr_len && len >= ip_total) { uint16_t dport_net; memcpy(&dport_net, ip + ip_hdr_len + 2, 2); - tcp_proxy_ensure_outbound_listen(p, dport_net); + tcp_proxy_client_ensure_outbound_listen(p, dport_net); uint32_t src_ip, dst_ip; memcpy(&src_ip, ip + 12, 4); memcpy(&dst_ip, ip + 16, 4); uint16_t tcp_len = ip_total - ip_hdr_len; @@ -371,149 +395,138 @@ static void tcp_proxy_tun_input(struct ll_queue* q, void* arg) { } // ==================================================================== -// Очистка proxy_conn +// Очистка tcp_proxy_client_conn // ==================================================================== -static void proxy_conn_free(struct proxy_conn *pc) { +static void tcp_proxy_client_conn_free(struct tcp_proxy_client_conn *pc) { if (!pc) return; - struct tcp_proxy *p = pc->proxy; + struct tcp_proxy_client *p = pc->proxy; uint32_t pending = pc->to_lwip ? queue_entry_count(pc->to_lwip) : 0; DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY FREE sid=%08x total_conns=%d to_lwip_q=%u", pc->stream_id, p->conn_count, pending); - struct proxy_conn **prev = &p->conns; + struct tcp_proxy_client_conn **prev = &p->conns; while (*prev) { if (*prev == pc) { *prev = pc->next; p->conn_count--; break; } prev = &(*prev)->next; } if (pc->to_lwip) { struct ll_entry *e; while ((e = queue_data_get(pc->to_lwip))) { queue_dgram_free(e); queue_entry_free(e); } queue_free(pc->to_lwip); pc->to_lwip = NULL; } + if (pc->tx_buf) { u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; } + if (pc->proxy && pc->proxy->inst) etcp_router_waiter_cancel(pc->proxy->inst, pc->proxy->via_node_id, &pc->tx_waiter); u_free(pc); } // ==================================================================== // Обработчики входящих сообщений (сторона клиента) // ==================================================================== -static struct proxy_conn* find_pc_by_stream(struct tcp_proxy* p, uint32_t stream_id) { - struct proxy_conn* pc; +static struct tcp_proxy_client_conn* tcp_proxy_client_find_conn(struct tcp_proxy_client* p, uint32_t stream_id) { + struct tcp_proxy_client_conn* pc; for (pc = p->conns; pc; pc = pc->next) if (pc->stream_id == stream_id) return pc; return NULL; } -static void handle_connected(struct tcp_proxy* p, uint32_t stream_id, struct ll_entry* entry) { - struct proxy_conn* pc = find_pc_by_stream(p, stream_id); - if (!pc) { DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "PROXY CONNECTED sid=%08x — no conn, drop", stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } - if (entry->len >= TCP_PROXY_CONNECTED_HDR_SIZE) { - uint8_t status = entry->dgram[TCP_PROXY_HDR_SIZE + 2]; - if (status == TCP_PROXY_CONNECTED_OK) { - pc->connected = 1; - uint16_t local_port = 0; memcpy(&local_port, entry->dgram + TCP_PROXY_HDR_SIZE, 2); - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY CONNECTED sid=%08x exit_port=%u", stream_id, ntohs(local_port)); - } else { - DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy: remote refused sid=%08x", stream_id); - pc->rem_closed = 1; - } - } - queue_dgram_free(entry); queue_entry_free(entry); -} - -static void handle_data(struct tcp_proxy* p, struct ETCP_CONN* conn, uint32_t stream_id, struct ll_entry* entry) { - struct proxy_conn* pc = find_pc_by_stream(p, stream_id); - if (!pc) { - DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "PROXY DATA sid=%08x — нет соединения, шлём CLOSE", stream_id); - if (conn) proxy_send_msg(p->inst, conn->peer_node_id, TCP_PROXY_SUBCMD_CLOSE, stream_id, NULL, 0); +static void tcp_proxy_client_handle_data(struct tcp_proxy_client* p, struct ETCP_CONN* conn, uint32_t stream_id, struct ll_entry* entry) { + struct tcp_proxy_client_conn* pc = tcp_proxy_client_find_conn(p, stream_id); + if (!pc || pc->rem_closed || pc->error || pc->tun_closed) { + DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "PROXY DATA sid=%08x — no conn/closed, шлём ERROR", stream_id); + tcp_proxy_client_send_msg(p->inst, p->via_node_id, TCP_PROXY_SUBCMD_ERROR, stream_id, NULL, 0); queue_dgram_free(entry); queue_entry_free(entry); return; } - if (pc->rem_closed || pc->error || pc->tun_closed) { queue_dgram_free(entry); queue_entry_free(entry); return; } size_t data_len = entry->len - TCP_PROXY_HDR_SIZE; DEBUG_INFO(DEBUG_CATEGORY_TRAFFIC, "PROXY DATA <- sid=%08x len=%zu", stream_id, data_len); if (data_len > 0) { - struct ll_entry* e = entry_from_data(pc->proxy->entry_pool, entry->dgram + TCP_PROXY_HDR_SIZE, (uint16_t)data_len); + struct ll_entry* e = tcp_proxy_client_entry_from_data(pc->proxy->entry_pool, entry->dgram + TCP_PROXY_HDR_SIZE, (uint16_t)data_len); if (e) queue_data_put(pc->to_lwip, e); - proxy_feed_from_transport(pc); + tcp_proxy_client_feed_from_transport(pc); } queue_dgram_free(entry); queue_entry_free(entry); } -static void handle_close(struct tcp_proxy* p, uint32_t stream_id) { - struct proxy_conn* pc = find_pc_by_stream(p, stream_id); +static void tcp_proxy_client_handle_close(struct tcp_proxy_client* p, uint32_t stream_id) { + struct tcp_proxy_client_conn* pc = tcp_proxy_client_find_conn(p, stream_id); if (pc) { - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY REM_CLOSED sid=%08x tun_closed=%d connected=%d", - stream_id, pc->tun_closed, pc->connected); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY REM_CLOSED sid=%08x tun_closed=%d", + stream_id, pc->tun_closed); pc->rem_closed = 1; - } else DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "PROXY CLOSE sid=%08x — no conn", stream_id); + } else { + DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "PROXY CLOSE sid=%08x — no conn, шлём ERROR", stream_id); + tcp_proxy_client_send_msg(p->inst, p->via_node_id, TCP_PROXY_SUBCMD_ERROR, stream_id, NULL, 0); + } } -static void handle_error(struct tcp_proxy* p, uint32_t stream_id) { - struct proxy_conn* pc = find_pc_by_stream(p, stream_id); +static void tcp_proxy_client_handle_error(struct tcp_proxy_client* p, uint32_t stream_id) { + struct tcp_proxy_client_conn* pc = tcp_proxy_client_find_conn(p, stream_id); if (pc) { - DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY ERROR from exit sid=%08x tun_closed=%d rem_closed=%d connected=%d", - stream_id, pc->tun_closed, pc->rem_closed, pc->connected); + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY ERROR from exit sid=%08x tun_closed=%d rem_closed=%d", + stream_id, pc->tun_closed, pc->rem_closed); pc->error = 1; - } else DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "PROXY ERROR sid=%08x — no conn", stream_id); + } else { + DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "PROXY ERROR sid=%08x — no conn, шлём ERROR", stream_id); + tcp_proxy_client_send_msg(p->inst, p->via_node_id, TCP_PROXY_SUBCMD_ERROR, stream_id, NULL, 0); + } } // ==================================================================== -// Единый обработчик etcp_router (диспетчеризация в tcp_proxy или remote_proxy) +// Единый обработчик etcp_router (диспетчеризация в tcp_proxy_client или tcp_proxy_server) // ==================================================================== -void tcp_proxy_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { +void tcp_proxy_client_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { if (!entry || !entry->dgram || entry->len < TCP_PROXY_HDR_SIZE) { - if (entry) { DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy: bad entry len=%u", entry->len); queue_dgram_free(entry); queue_entry_free(entry); } + if (entry) { DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy client: bad entry len=%u", entry->len); queue_dgram_free(entry); queue_entry_free(entry); } return; } uint8_t subcmd = entry->dgram[1]; uint32_t stream_id; memcpy(&stream_id, entry->dgram + 2, 4); struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL; - struct tcp_proxy* proxy = inst ? inst->tcp_proxy : NULL; + struct tcp_proxy_client* proxy = inst ? inst->tcp_proxy_client : NULL; if (subcmd == TCP_PROXY_SUBCMD_CONNECT) { uint64_t src_node_id = conn ? conn->peer_node_id : (inst ? inst->node_id : 0); - remote_proxy_handle_connect(inst, entry, stream_id, src_node_id); + tcp_proxy_server_handle_connect(inst, entry, stream_id, src_node_id); return; } - if (inst && inst->remote_proxy.enabled) { - struct remote_proxy_conn* rc = remote_proxy_find_conn(&inst->remote_proxy, stream_id); + if (inst && inst->tcp_proxy_server.enabled) { + struct tcp_proxy_server_conn* rc = tcp_proxy_server_find_conn(&inst->tcp_proxy_server, stream_id); if (rc) { - if (subcmd == TCP_PROXY_SUBCMD_DATA) { remote_proxy_handle_data(inst, conn, entry, stream_id); return; } - if (subcmd == TCP_PROXY_SUBCMD_CLOSE) { remote_proxy_handle_close(inst, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } - if (subcmd == TCP_PROXY_SUBCMD_ERROR) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "RP ERROR recv sid=%08x", stream_id); rc->error = 1; rp_conn_free(rc); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (subcmd == TCP_PROXY_SUBCMD_DATA) { tcp_proxy_server_handle_data(inst, conn, entry, stream_id); return; } + if (subcmd == TCP_PROXY_SUBCMD_CLOSE) { tcp_proxy_server_handle_close(inst, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (subcmd == TCP_PROXY_SUBCMD_ERROR) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "RP ERROR recv sid=%08x", stream_id); rc->error = 1; tcp_proxy_server_conn_free(rc); queue_dgram_free(entry); queue_entry_free(entry); return; } } } if (proxy) { - if (subcmd == TCP_PROXY_SUBCMD_CONNECTED) { handle_connected(proxy, stream_id, entry); return; } - if (subcmd == TCP_PROXY_SUBCMD_DATA) { handle_data(proxy, conn, stream_id, entry); return; } - if (subcmd == TCP_PROXY_SUBCMD_CLOSE) { handle_close(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } - if (subcmd == TCP_PROXY_SUBCMD_ERROR) { handle_error(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (subcmd == TCP_PROXY_SUBCMD_DATA) { tcp_proxy_client_handle_data(proxy, conn, stream_id, entry); return; } + if (subcmd == TCP_PROXY_SUBCMD_CLOSE) { tcp_proxy_client_handle_close(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } + if (subcmd == TCP_PROXY_SUBCMD_ERROR) { tcp_proxy_client_handle_error(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; } } - DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy: unhandled subcmd=%02x sid=%08x", subcmd, stream_id); + DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy client: unhandled subcmd=%02x sid=%08x", subcmd, stream_id); queue_dgram_free(entry); queue_entry_free(entry); } // ==================================================================== // Публичное API // ==================================================================== -struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua, +struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua, const char* tun_name, const char* tun_ip, int mtu, int test_mode, - struct tcp_proxy_mapping_config* mappings, int mapping_count, + struct tcp_proxy_client_mapping_config* mappings, int mapping_count, uint64_t via_node_id) { - if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_create: ua is NULL"); return NULL; } - if (!tun_name || !tun_ip) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_create: tun name/ip required"); return NULL; } + if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: ua is NULL"); return NULL; } + if (!tun_name || !tun_ip) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: tun name/ip required"); return NULL; } - struct tcp_proxy* p = u_calloc(1, sizeof(struct tcp_proxy)); - if (!p) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_create: u_calloc failed"); return NULL; } + struct tcp_proxy_client* p = u_calloc(1, sizeof(struct tcp_proxy_client)); + if (!p) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: u_calloc failed"); return NULL; } p->inst = inst; p->ua = ua; p->next_stream_id = 1; p->via_node_id = via_node_id; p->mappings = mappings; p->mapping_count = mapping_count; p->entry_pool = memory_pool_init(sizeof(struct ll_entry)); - if (!p->entry_pool) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_create: memory_pool_init failed"); u_free(p); return NULL; } + if (!p->entry_pool) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: memory_pool_init failed"); u_free(p); return NULL; } p->tun = tun_init_nat(ua, tun_name, tun_ip, mtu > 0 ? mtu : 1500, test_mode); - if (!p->tun) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tcp_proxy: failed to create TUN %s", tun_name); memory_pool_destroy(p->entry_pool); u_free(p); return NULL; } - queue_set_callback(p->tun->output_queue, tcp_proxy_tun_input, p); + if (!p->tun) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tcp_proxy_client: failed to create TUN %s", tun_name); memory_pool_destroy(p->entry_pool); u_free(p); return NULL; } + queue_set_callback(p->tun->output_queue, tcp_proxy_client_tun_input, p); - p->lwip = lwip_tcp_init(ua, tcp_output_cb, p); + p->lwip = lwip_tcp_init(ua, tcp_proxy_client_output_cb, p); if (!p->lwip) { - DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_create: lwip_tcp_init failed"); + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: lwip_tcp_init failed"); tun_close(p->tun); memory_pool_destroy(p->entry_pool); u_free(p); return NULL; } @@ -527,37 +540,37 @@ struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua struct tcp_pcb *listen_pcb = tcp_listen(lpcb); if (listen_pcb) { tcp_arg(listen_pcb, p); - tcp_accept(listen_pcb, proxy_accept_cb); + tcp_accept(listen_pcb, tcp_proxy_client_accept_cb); } } } if (inst) { - if (etcp_router_bind(inst, ETCP_ID_TCP_PROXY, tcp_proxy_etcp_recv_cb) != 0) { - DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy: etcp_router_bind failed"); + if (etcp_router_bind(inst, ETCP_ID_TCP_PROXY, tcp_proxy_client_etcp_recv_cb) != 0) { + DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy client: etcp_router_bind failed"); } else { - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy: etcp_router bind registered for ID=0x%02x", ETCP_ID_TCP_PROXY); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client: etcp_router bind registered for ID=0x%02x", ETCP_ID_TCP_PROXY); udp_proxy_init(inst, ua); icmp_proxy_init(inst, ua); } } - DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy created: mappings=%d via_node=%016llx", mapping_count, (unsigned long long)via_node_id); + DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy client created: mappings=%d via_node=%016llx", mapping_count, (unsigned long long)via_node_id); return p; } -void tcp_proxy_destroy(struct tcp_proxy* p) { +void tcp_proxy_client_destroy(struct tcp_proxy_client* p) { if (!p) return; - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy destroying: conns=%d", p->conn_count); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client destroying: conns=%d", p->conn_count); if (p->inst) etcp_router_unbind(p->inst, ETCP_ID_TCP_PROXY); udp_proxy_destroy(p->inst); icmp_proxy_destroy(p->inst); if (p->lwip) { lwip_tcp_destroy(p->lwip); p->lwip = NULL; } - struct proxy_conn* pc = p->conns; + struct tcp_proxy_client_conn* pc = p->conns; while (pc) { - struct proxy_conn* next = pc->next; + struct tcp_proxy_client_conn* next = pc->next; if (pc->pcb) { tcp_arg(pc->pcb, NULL); tcp_abort(pc->pcb); pc->pcb = NULL; } if (pc->to_lwip) { struct ll_entry *e; diff --git a/src/tcp_proxy.h b/src/proxy/tcp_proxy_client.h similarity index 53% rename from src/tcp_proxy.h rename to src/proxy/tcp_proxy_client.h index f6369fa4..6c2ceff5 100644 --- a/src/tcp_proxy.h +++ b/src/proxy/tcp_proxy_client.h @@ -1,10 +1,11 @@ -// tcp_proxy.h — TCP прокси: стек lwIP TCP → ETCP → удалённый exit узел -#ifndef TCP_PROXY_H -#define TCP_PROXY_H +// tcp_proxy_client.h — TCP прокси-клиент: стек lwIP TCP → ETCP → удалённый exit узел +#ifndef TCP_PROXY_CLIENT_H +#define TCP_PROXY_CLIENT_H #include #include #include "../lib/socket_compat.h" +#include "../lib/ll_queue.h" struct UASYNC; struct UTUN_INSTANCE; @@ -13,16 +14,16 @@ struct tun_if; struct ll_queue; struct ll_entry; struct memory_pool; -struct tcp_proxy_mapping_config; -struct remote_proxy_ctx; +struct tcp_proxy_client_mapping_config; +struct tcp_proxy_server; struct lwip_tcp_ctx; struct tcp_pcb; -struct proxy_conn { - struct proxy_conn* next; - struct tcp_proxy* proxy; - struct tcp_pcb* pcb; +struct tcp_proxy_client_conn { + struct tcp_proxy_client_conn* next; + struct tcp_proxy_client* proxy; + struct tcp_pcb* pcb; uint32_t stream_id; @@ -33,32 +34,35 @@ struct proxy_conn { uint8_t error; // ошибка, немедленная очистка uint8_t close_sent; // отправили CLOSE/ERROR в exit uint8_t close_pending; // CLOSE/ERROR не доставлен, ждём повтора - uint8_t connected; // CONNECTED(OK) получен от exit + + uint8_t* tx_buf; // буфер при backpressure (lwIP→ETCP send fail) + uint16_t tx_len; + struct queue_waiter_handle tx_waiter; uint8_t dest_ip[4]; uint16_t dest_port; }; -struct tcp_proxy { +struct tcp_proxy_client { struct UTUN_INSTANCE* inst; struct UASYNC* ua; struct tun_if* tun; - struct proxy_conn* conns; + struct tcp_proxy_client_conn* conns; int conn_count; struct memory_pool* entry_pool; uint32_t next_stream_id; uint64_t via_node_id; struct lwip_tcp_ctx* lwip; - struct tcp_proxy_mapping_config* mappings; // указатель на конфиг + struct tcp_proxy_client_mapping_config* mappings; // указатель на конфиг int mapping_count; }; -struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua, +struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua, const char* tun_name, const char* tun_ip, int mtu, int test_mode, - struct tcp_proxy_mapping_config* mappings, int mapping_count, + struct tcp_proxy_client_mapping_config* mappings, int mapping_count, uint64_t via_node_id); -void tcp_proxy_destroy(struct tcp_proxy* p); -void tcp_proxy_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry); +void tcp_proxy_client_destroy(struct tcp_proxy_client* p); +void tcp_proxy_client_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry); -#endif // TCP_PROXY_H +#endif // TCP_PROXY_CLIENT_H diff --git a/src/remote_proxy.c b/src/proxy/tcp_proxy_server.c similarity index 54% rename from src/remote_proxy.c rename to src/proxy/tcp_proxy_server.c index ccc9fc1e..a9738b3b 100644 --- a/src/remote_proxy.c +++ b/src/proxy/tcp_proxy_server.c @@ -1,6 +1,6 @@ -// remote_proxy.c — Удаленный TCP прокси (exit node) -#include "remote_proxy.h" -#include "tcp_proxy.h" +// tcp_proxy_server.c — TCP прокси-сервер (exit node) +#include "tcp_proxy_server.h" +#include "tcp_proxy_client.h" #include "udp_proxy.h" #include "icmp_proxy.h" #include "etcp.h" @@ -26,25 +26,23 @@ #define MSG_NOSIGNAL 0 #endif -static struct remote_proxy_ctx* g_rp_ctx = NULL; +static struct tcp_proxy_server* g_tcp_proxy_server_ctx = NULL; -static void rp_sock_read_cb(socket_t sock, void* arg); -static void rp_sock_write_cb(socket_t sock, void* arg); -static void rp_sock_error_cb(socket_t sock, void* arg); -static void rp_sock_retry_cb(void* arg); -static void rp_sock_pause_cb(void* arg); -static void rp_close_retry_cb(void* arg); -static void rp_sock_send(struct remote_proxy_conn* rc, const uint8_t* data, size_t len); -void rp_conn_free(struct remote_proxy_conn* rc); +static void tcp_proxy_server_sock_read_cb(socket_t sock, void* arg); +static void tcp_proxy_server_sock_write_cb(socket_t sock, void* arg); +static void tcp_proxy_server_sock_error_cb(socket_t sock, void* arg); +static void tcp_proxy_server_sock_retry_cb(void* arg); +static void tcp_proxy_server_pause_waiter_cb(struct ll_queue* q, void* arg); +static void tcp_proxy_server_close_retry_cb(void* arg); +static void tcp_proxy_server_sock_send(struct tcp_proxy_server_conn* rc, const uint8_t* data, size_t len); +void tcp_proxy_server_conn_free(struct tcp_proxy_server_conn* rc); -#define RP_NORMALIZER_Q_THRESHOLD 64 - -static int rp_send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, +static int tcp_proxy_server_send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len) { struct ll_entry* e = queue_entry_new(0); - if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "rp_send_msg: queue_entry_new failed subcmd=%02x sid=%08x", subcmd, sid); return -1; } + if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_server_send_msg: queue_entry_new failed subcmd=%02x sid=%08x", subcmd, sid); return -1; } e->dgram = u_malloc(TCP_PROXY_HDR_SIZE + len); - if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "rp_send_msg: malloc(%zu) failed subcmd=%02x sid=%08x", TCP_PROXY_HDR_SIZE + len, subcmd, sid); queue_entry_free(e); return -1; } + if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_server_send_msg: malloc(%zu) failed subcmd=%02x sid=%08x", TCP_PROXY_HDR_SIZE + len, subcmd, sid); queue_entry_free(e); return -1; } e->dgram[0] = ETCP_ID_TCP_PROXY; e->dgram[1] = subcmd; memcpy(e->dgram + 2, &sid, 4); @@ -53,54 +51,47 @@ static int rp_send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, return etcp_route_send(inst, dst, e); } -static int rp_send_connected(struct UTUN_INSTANCE* inst, uint64_t dst, uint32_t sid, - uint16_t local_port, uint8_t status) { - uint8_t buf[3]; - memcpy(buf, &local_port, 2); buf[2] = status; - return rp_send_msg(inst, dst, TCP_PROXY_SUBCMD_CONNECTED, sid, buf, 3); -} - -static void rp_close_retry_cb(void* arg) { - struct remote_proxy_conn* rc = (struct remote_proxy_conn*)arg; +static void tcp_proxy_server_close_retry_cb(void* arg) { + struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg; if (!rc) return; rc->close_timer = NULL; if (!rc->close_pending) return; struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL; if (!inst) return; uint8_t subcmd = rc->error ? TCP_PROXY_SUBCMD_ERROR : TCP_PROXY_SUBCMD_CLOSE; - if (rp_send_msg(inst, rc->peer_node_id, subcmd, rc->stream_id, NULL, 0) < 0) { + if (tcp_proxy_server_send_msg(inst, rc->peer_node_id, subcmd, rc->stream_id, NULL, 0) < 0) { rc->close_backoff = rc->close_backoff < 5000 ? rc->close_backoff * 2 : 5000; - rc->close_timer = uasync_set_timeout(rc->ua, rc->close_backoff, rc, rp_close_retry_cb, "rp_close_retry"); + rc->close_timer = uasync_set_timeout(rc->ua, rc->close_backoff, rc, tcp_proxy_server_close_retry_cb, "tcp_proxy_server_close_retry"); return; } rc->close_pending = 0; } -static void rp_send_close(struct remote_proxy_conn* rc) { +static void tcp_proxy_server_send_close(struct tcp_proxy_server_conn* rc) { struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL; if (!inst) return; - if (rp_send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_CLOSE, rc->stream_id, NULL, 0) < 0) { + if (tcp_proxy_server_send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_CLOSE, rc->stream_id, NULL, 0) < 0) { rc->close_pending = 1; rc->close_backoff = 50; if (!rc->close_timer) - rc->close_timer = uasync_set_timeout(rc->ua, rc->close_backoff, rc, rp_close_retry_cb, "rp_close_retry"); + rc->close_timer = uasync_set_timeout(rc->ua, rc->close_backoff, rc, tcp_proxy_server_close_retry_cb, "tcp_proxy_server_close_retry"); } } -static void rp_send_error(struct remote_proxy_conn* rc) { +static void tcp_proxy_server_send_error(struct tcp_proxy_server_conn* rc) { struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL; if (!inst) return; - if (rp_send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_ERROR, rc->stream_id, NULL, 0) < 0) { + if (tcp_proxy_server_send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_ERROR, rc->stream_id, NULL, 0) < 0) { rc->close_pending = 1; rc->close_backoff = 50; if (!rc->close_timer) - rc->close_timer = uasync_set_timeout(rc->ua, rc->close_backoff, rc, rp_close_retry_cb, "rp_close_retry"); + rc->close_timer = uasync_set_timeout(rc->ua, rc->close_backoff, rc, tcp_proxy_server_close_retry_cb, "tcp_proxy_server_close_retry"); } } -static int rp_conn_total(struct remote_proxy_conn* rc) { +static int tcp_proxy_server_conn_total(struct tcp_proxy_server_conn* rc) { if (!rc || !rc->ctx) return 0; - int n = 0; struct remote_proxy_conn* c; + int n = 0; struct tcp_proxy_server_conn* c; for (c = rc->ctx->conns; c; c = c->next) n++; return n; } @@ -108,7 +99,7 @@ static int rp_conn_total(struct remote_proxy_conn* rc) { // ==================================================================== // Неблокирующий send с EAGAIN и таймером повтора // ==================================================================== -static int rp_sock_try_send(struct remote_proxy_conn* rc) { +static int tcp_proxy_server_sock_try_send(struct tcp_proxy_server_conn* rc) { if (!rc || rc->sock == SOCKET_INVALID || !rc->out_buf) return 0; while (rc->out_off < rc->out_len) { ssize_t n = send(rc->sock, rc->out_buf + rc->out_off, rc->out_len - rc->out_off, MSG_NOSIGNAL); @@ -127,23 +118,26 @@ static int rp_sock_try_send(struct remote_proxy_conn* rc) { return 0; } -static void rp_sock_retry_cb(void* arg) { - struct remote_proxy_conn* rc = (struct remote_proxy_conn*)arg; +static void tcp_proxy_server_sock_retry_cb(void* arg) { + struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg; if (!rc || !rc->out_buf) { rc->out_timer = NULL; return; } - int ret = rp_sock_try_send(rc); + int ret = tcp_proxy_server_sock_try_send(rc); if (ret == 0) { rc->out_timer = NULL; rc->out_backoff = 0; + if (rc->cli_closed && rc->sock != SOCKET_INVALID && rc->connected == 1) shutdown(rc->sock, SHUT_WR); + if (rc->sock_closed) tcp_proxy_server_send_close(rc); + if (rc->cli_closed && rc->sock_closed) tcp_proxy_server_conn_free(rc); } else if (ret == 1) { rc->out_backoff = rc->out_backoff < 20 ? 20 : rc->out_backoff * 2; if (rc->out_backoff > 5000) rc->out_backoff = 5000; - rc->out_timer = uasync_set_timeout(rc->ua, rc->out_backoff, rc, rp_sock_retry_cb, "rp_send"); + rc->out_timer = uasync_set_timeout(rc->ua, rc->out_backoff, rc, tcp_proxy_server_sock_retry_cb, "tcp_proxy_server_send"); } else { u_free(rc->out_buf); rc->out_buf = NULL; rc->out_len = rc->out_off = 0; rc->out_timer = NULL; rc->out_backoff = 0; } } -static void rp_sock_send(struct remote_proxy_conn* rc, const uint8_t* data, size_t len) { +static void tcp_proxy_server_sock_send(struct tcp_proxy_server_conn* rc, const uint8_t* data, size_t len) { if (!rc || !data || len == 0) return; if (rc->out_buf) { size_t new_len = rc->out_len + len; @@ -156,87 +150,77 @@ static void rp_sock_send(struct remote_proxy_conn* rc, const uint8_t* data, size rc->out_buf = u_malloc(len); if (!rc->out_buf) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "SOCK:SEND malloc(%zu) failed sid=%08x", len, rc->stream_id); return; } memcpy(rc->out_buf, data, len); rc->out_len = len; rc->out_off = 0; - int ret = rp_sock_try_send(rc); + int ret = tcp_proxy_server_sock_try_send(rc); if (ret == 0) return; if (ret == 1) { rc->out_backoff = 10; - rc->out_timer = uasync_set_timeout(rc->ua, rc->out_backoff, rc, rp_sock_retry_cb, "rp_send"); + rc->out_timer = uasync_set_timeout(rc->ua, rc->out_backoff, rc, tcp_proxy_server_sock_retry_cb, "tcp_proxy_server_send"); } else { u_free(rc->out_buf); rc->out_buf = NULL; rc->out_len = rc->out_off = 0; } } -static void rp_sock_pause_cb(void* arg) { - struct remote_proxy_conn* rc = (struct remote_proxy_conn*)arg; - if (!rc || rc->sock == SOCKET_INVALID) { rc->pause_timer = NULL; return; } +static void tcp_proxy_server_pause_waiter_cb(struct ll_queue* q, void* arg) { + (void)q; + struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg; + if (!rc || rc->sock == SOCKET_INVALID) return; struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL; - int qcnt = -1; - if (inst) qcnt = etcp_router_input_q_count(inst, rc->peer_node_id); - if (qcnt > RP_NORMALIZER_Q_THRESHOLD) { - rc->pause_backoff = rc->pause_backoff * 2; - if (rc->pause_backoff > 5000) rc->pause_backoff = 5000; - rc->pause_timer = uasync_set_timeout(rc->ua, rc->pause_backoff, rc, rp_sock_pause_cb, "rp_pause"); - return; - } + if (!inst) return; if (rc->pause_buf && rc->pause_len > 0) { - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:PAUSE_FLUSH fd=%d sid=%08x len=%zu qcnt=%d", (int)rc->sock, rc->stream_id, rc->pause_len, qcnt); - if (rp_send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_DATA, rc->stream_id, rc->pause_buf, rc->pause_len) < 0) { + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:PAUSE_FLUSH fd=%d sid=%08x len=%zu", (int)rc->sock, rc->stream_id, rc->pause_len); + if (tcp_proxy_server_send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_DATA, rc->stream_id, rc->pause_buf, rc->pause_len) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "SOCK:PAUSE_FLUSH send failed — closing sid=%08x", rc->stream_id); - rc->error = 1; u_free(rc->pause_buf); rc->pause_buf = NULL; rc->pause_len = 0; rp_conn_free(rc); return; + rc->error = 1; u_free(rc->pause_buf); rc->pause_buf = NULL; rc->pause_len = 0; tcp_proxy_server_conn_free(rc); return; } u_free(rc->pause_buf); rc->pause_buf = NULL; rc->pause_len = 0; } - rc->read_id = uasync_add_socket_t(rc->ua, rc->sock, rp_sock_read_cb, rp_sock_write_cb, rp_sock_error_cb, rc); - rc->pause_timer = NULL; + rc->read_id = uasync_add_socket_t(rc->ua, rc->sock, tcp_proxy_server_sock_read_cb, tcp_proxy_server_sock_write_cb, tcp_proxy_server_sock_error_cb, rc); + if (rc->sock_closed) tcp_proxy_server_send_close(rc); } // ==================================================================== // Socket callbacks // ==================================================================== -static void rp_sock_read_cb(socket_t sock, void* arg) { - (void)sock; struct remote_proxy_conn* rc = (struct remote_proxy_conn*)arg; +static void tcp_proxy_server_sock_read_cb(socket_t sock, void* arg) { + (void)sock; struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg; if (!rc || rc->sock == SOCKET_INVALID) return; uint8_t buf[8192]; ssize_t n = recv(rc->sock, buf, sizeof(buf), 0); if (n > 0) { if (rc->cli_closed || rc->error) return; struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL; if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "SOCK:RECV fd=%d sid=%08x — inst is NULL, drop", (int)rc->sock, rc->stream_id); return; } - int qcnt = etcp_router_input_q_count(inst, rc->peer_node_id); - if (qcnt > RP_NORMALIZER_Q_THRESHOLD) { - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:PAUSE fd=%d sid=%08x len=%zd qcnt=%d — normalizer переполнен, пауза", (int)rc->sock, rc->stream_id, n, qcnt); + if (rc->pause_buf) return; + int ret = tcp_proxy_server_send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_DATA, rc->stream_id, buf, (size_t)n); + if (ret == 0) { + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:RECV fd=%d sid=%08x len=%zd total=%d", (int)rc->sock, rc->stream_id, n, tcp_proxy_server_conn_total(rc)); + } else { + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:PAUSE fd=%d sid=%08x len=%zd — backpressure, пауза", (int)rc->sock, rc->stream_id, n); rc->pause_buf = u_malloc(n); if (rc->pause_buf) { memcpy(rc->pause_buf, buf, n); rc->pause_len = n; } else DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "SOCK:PAUSE malloc(%zd) failed sid=%08x", n, rc->stream_id); if (rc->read_id) { uasync_remove_socket_t(rc->ua, rc->sock); rc->read_id = NULL; } - rc->pause_backoff = 50; - rc->pause_timer = uasync_set_timeout(rc->ua, rc->pause_backoff, rc, rp_sock_pause_cb, "rp_pause"); - return; - } - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:RECV fd=%d sid=%08x len=%zd total=%d", (int)rc->sock, rc->stream_id, n, rp_conn_total(rc)); - if (rp_send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_DATA, rc->stream_id, buf, (size_t)n) < 0) { - DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "SOCK:RECV send failed — closing sid=%08x", rc->stream_id); - rc->error = 1; rp_conn_free(rc); return; + etcp_router_waiter_register(inst, rc->peer_node_id, &rc->pause_waiter); } } else if (n == 0) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:EOF fd=%d sid=%08x total=%d cli_closed=%d sock_closed=%d", - (int)rc->sock, rc->stream_id, rp_conn_total(rc), rc->cli_closed, rc->sock_closed); + (int)rc->sock, rc->stream_id, tcp_proxy_server_conn_total(rc), rc->cli_closed, rc->sock_closed); rc->sock_closed = 1; - rp_send_close(rc); + if (!rc->pause_buf && !rc->out_buf) tcp_proxy_server_send_close(rc); if (rc->read_id) { uasync_remove_socket_t(rc->ua, rc->sock); rc->read_id = NULL; } - if (rc->cli_closed) rp_conn_free(rc); + if (rc->cli_closed && !rc->out_buf && !rc->pause_buf) tcp_proxy_server_conn_free(rc); } else if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "SOCK:ERROR fd=%d sid=%08x errno=%d %s total=%d", - (int)rc->sock, rc->stream_id, errno, strerror(errno), rp_conn_total(rc)); + (int)rc->sock, rc->stream_id, errno, strerror(errno), tcp_proxy_server_conn_total(rc)); rc->error = 1; struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL; - if (inst) rp_send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_ERROR, rc->stream_id, NULL, 0); + if (inst) tcp_proxy_server_send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_ERROR, rc->stream_id, NULL, 0); else DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "SOCK:ERROR fd=%d sid=%08x — inst is NULL, can't send ERROR", (int)rc->sock, rc->stream_id); - rp_conn_free(rc); + tcp_proxy_server_conn_free(rc); } } -static void rp_sock_write_cb(socket_t sock, void* arg) { - (void)sock; struct remote_proxy_conn* rc = (struct remote_proxy_conn*)arg; +static void tcp_proxy_server_sock_write_cb(socket_t sock, void* arg) { + (void)sock; struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg; if (!rc || rc->sock == SOCKET_INVALID || rc->connected) return; int err = 0; socklen_t len = sizeof(err); if (getsockopt(rc->sock, SOL_SOCKET, SO_ERROR, &err, &len) == 0 && err == 0) { @@ -247,39 +231,36 @@ static void rp_sock_write_cb(socket_t sock, void* arg) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:CONN fd=%d sid=%08x local=%d dest=%d.%d.%d.%d:%d total=%d", (int)rc->sock, rc->stream_id, ntohs(local_port), rc->dest_ip[0], rc->dest_ip[1], rc->dest_ip[2], rc->dest_ip[3], ntohs(rc->dest_port), - rp_conn_total(rc)); - struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL; - if (inst) rp_send_connected(inst, rc->peer_node_id, rc->stream_id, local_port, TCP_PROXY_CONNECTED_OK); + tcp_proxy_server_conn_total(rc)); if (rc->pending_buf && rc->pending_len > 0) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:FLUSH fd=%d sid=%08x len=%zu", (int)rc->sock, rc->stream_id, rc->pending_len); - rp_sock_send(rc, rc->pending_buf, rc->pending_len); + tcp_proxy_server_sock_send(rc, rc->pending_buf, rc->pending_len); u_free(rc->pending_buf); rc->pending_buf = NULL; rc->pending_len = 0; } } else { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "SOCK:FAIL fd=%d sid=%08x err=%d %s", (int)rc->sock, rc->stream_id, err, err ? strerror(err) : "unknown"); - struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL; - if (inst) rp_send_connected(inst, rc->peer_node_id, rc->stream_id, 0, TCP_PROXY_CONNECTED_REFUSED); - rp_conn_free(rc); + tcp_proxy_server_send_error(rc); + tcp_proxy_server_conn_free(rc); } } -static void rp_sock_error_cb(socket_t sock, void* arg) { - (void)sock; struct remote_proxy_conn* rc = (struct remote_proxy_conn*)arg; +static void tcp_proxy_server_sock_error_cb(socket_t sock, void* arg) { + (void)sock; struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg; if (!rc || rc->sock == SOCKET_INVALID) return; DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "SOCK:ERR fd=%d sid=%08x", (int)rc->sock, rc->stream_id); rc->error = 1; - rp_send_error(rc); - rp_conn_free(rc); + tcp_proxy_server_send_error(rc); + tcp_proxy_server_conn_free(rc); } -void rp_conn_free(struct remote_proxy_conn* rc) { +void tcp_proxy_server_conn_free(struct tcp_proxy_server_conn* rc) { if (!rc) return; - int total = rp_conn_total(rc); + int total = tcp_proxy_server_conn_total(rc); DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:FREE fd=%d sid=%08x total=%d cli_closed=%d sock_closed=%d error=%d", (int)rc->sock, rc->stream_id, total, rc->cli_closed, rc->sock_closed, rc->error); if (rc->ctx) { - struct remote_proxy_conn** prev = &rc->ctx->conns; + struct tcp_proxy_server_conn** prev = &rc->ctx->conns; while (*prev) { if (*prev == rc) { *prev = rc->next; rc->ctx->conn_count--; break; } prev = &(*prev)->next; } } if (rc->sock != SOCKET_INVALID) { @@ -289,51 +270,52 @@ void rp_conn_free(struct remote_proxy_conn* rc) { if (rc->pending_buf) { u_free(rc->pending_buf); rc->pending_buf = NULL; } if (rc->out_timer) { uasync_cancel_timeout(rc->ua, rc->out_timer); rc->out_timer = NULL; } if (rc->out_buf) { u_free(rc->out_buf); rc->out_buf = NULL; } - if (rc->pause_timer) { uasync_cancel_timeout(rc->ua, rc->pause_timer); rc->pause_timer = NULL; } - if (rc->pause_buf) { u_free(rc->pause_buf); rc->pause_buf = NULL; } + if (rc->pause_buf) { u_free(rc->pause_buf); rc->pause_buf = NULL; rc->pause_len = 0; } if (rc->close_timer) { uasync_cancel_timeout(rc->ua, rc->close_timer); rc->close_timer = NULL; } + if (rc->ctx && rc->ctx->inst) etcp_router_waiter_cancel(rc->ctx->inst, rc->peer_node_id, &rc->pause_waiter); u_free(rc); } // ==================================================================== // Public API // ==================================================================== -struct remote_proxy_conn* remote_proxy_find_conn(struct remote_proxy_ctx* ctx, uint32_t stream_id) { - struct remote_proxy_conn* c; +struct tcp_proxy_server_conn* tcp_proxy_server_find_conn(struct tcp_proxy_server* ctx, uint32_t stream_id) { + struct tcp_proxy_server_conn* c; for (c = ctx->conns; c; c = c->next) if (c->stream_id == stream_id) return c; return NULL; } -int remote_proxy_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry* entry, +int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry* entry, uint32_t stream_id, uint64_t src_node_id) { - if (!inst || !inst->remote_proxy.enabled) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return -1; } - struct remote_proxy_ctx* ctx = &inst->remote_proxy; - if (entry->len < TCP_PROXY_CONNECT_HDR_SIZE) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "remote_proxy: CONNECT too short len=%u", entry->len); queue_dgram_free(entry); queue_entry_free(entry); return -1; } + if (!inst || !inst->tcp_proxy_server.enabled) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return -1; } + struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; + if (entry->len < TCP_PROXY_CONNECT_HDR_SIZE) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy server: CONNECT too short len=%u", entry->len); queue_dgram_free(entry); queue_entry_free(entry); return -1; } uint8_t* dest_ip = entry->dgram + TCP_PROXY_HDR_SIZE; uint16_t dest_port = 0; memcpy(&dest_port, dest_ip + 4, 2); - struct remote_proxy_conn* rc = u_calloc(1, sizeof(struct remote_proxy_conn)); - if (!rc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "remote_proxy: u_calloc failed for sid=%08x", stream_id); queue_dgram_free(entry); queue_entry_free(entry); return -1; } + struct tcp_proxy_server_conn* rc = u_calloc(1, sizeof(struct tcp_proxy_server_conn)); + if (!rc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy server: u_calloc failed for sid=%08x", stream_id); queue_dgram_free(entry); queue_entry_free(entry); return -1; } rc->ctx = ctx; rc->stream_id = stream_id; rc->peer_node_id = src_node_id; memcpy(rc->dest_ip, dest_ip, 4); rc->dest_port = dest_port; rc->ua = inst->ua; rc->sock = SOCKET_INVALID; + queue_waiter_handle_init(&rc->pause_waiter, tcp_proxy_server_pause_waiter_cb, rc); rc->sock = socket(AF_INET, SOCK_STREAM, 0); - if (rc->sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "remote_proxy: socket() failed"); u_free(rc); queue_dgram_free(entry); queue_entry_free(entry); return -1; } + if (rc->sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy server: socket() failed"); u_free(rc); queue_dgram_free(entry); queue_entry_free(entry); return -1; } ctx->conn_count++; DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:NEW fd=%d sid=%08x dest=%d.%d.%d.%d:%d total=%d", (int)rc->sock, stream_id, dest_ip[0],dest_ip[1],dest_ip[2],dest_ip[3],ntohs(dest_port), ctx->conn_count); socket_set_nonblocking(rc->sock); - rc->read_id = uasync_add_socket_t(rc->ua, rc->sock, rp_sock_read_cb, rp_sock_write_cb, rp_sock_error_cb, rc); - if (!rc->read_id) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "remote_proxy: uasync_add_socket_t failed"); u_free(rc); queue_dgram_free(entry); queue_entry_free(entry); return -1; } + rc->read_id = uasync_add_socket_t(rc->ua, rc->sock, tcp_proxy_server_sock_read_cb, tcp_proxy_server_sock_write_cb, tcp_proxy_server_sock_error_cb, rc); + if (!rc->read_id) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy server: uasync_add_socket_t failed"); u_free(rc); queue_dgram_free(entry); queue_entry_free(entry); return -1; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; memcpy(&addr.sin_addr.s_addr, dest_ip, 4); addr.sin_port = dest_port; int ret = connect(rc->sock, (struct sockaddr*)&addr, sizeof(addr)); if (ret < 0 && errno != EINPROGRESS) { - DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "remote_proxy: connect() to %d.%d.%d.%d:%d failed: %s", + DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy server: connect() to %d.%d.%d.%d:%d failed: %s", dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3], ntohs(dest_port), strerror(errno)); - rp_send_connected(inst, src_node_id, stream_id, 0, TCP_PROXY_CONNECTED_REFUSED); + tcp_proxy_server_send_msg(inst, src_node_id, TCP_PROXY_SUBCMD_ERROR, stream_id, NULL, 0); uasync_remove_socket_t(rc->ua, rc->sock); socket_close_wrapper(rc->sock); u_free(rc); queue_dgram_free(entry); queue_entry_free(entry); return -1; } @@ -343,81 +325,82 @@ int remote_proxy_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry* ent return 0; } -int remote_proxy_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn, struct ll_entry* entry, uint32_t stream_id) { +int tcp_proxy_server_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn, struct ll_entry* entry, uint32_t stream_id) { if (!inst) { queue_dgram_free(entry); queue_entry_free(entry); return -1; } - struct remote_proxy_ctx* ctx = &inst->remote_proxy; - struct remote_proxy_conn* rc = remote_proxy_find_conn(ctx, stream_id); - if (!rc || rc->sock == SOCKET_INVALID) { - DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "RP handle_data drop: нет соединения для sid=%08x, шлём CLOSE", stream_id); - if (conn) rp_send_msg(inst, conn->peer_node_id, TCP_PROXY_SUBCMD_CLOSE, stream_id, NULL, 0); + struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; + struct tcp_proxy_server_conn* rc = tcp_proxy_server_find_conn(ctx, stream_id); + if (!rc || rc->sock == SOCKET_INVALID || rc->error) { + DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TPS handle_data: нет/error conn для sid=%08x, шлём ERROR", stream_id); + if (conn) tcp_proxy_server_send_msg(inst, conn->peer_node_id, TCP_PROXY_SUBCMD_ERROR, stream_id, NULL, 0); queue_dgram_free(entry); queue_entry_free(entry); return -1; } - if (rc->error) { queue_dgram_free(entry); queue_entry_free(entry); return -1; } size_t data_len = entry->len - TCP_PROXY_HDR_SIZE; if (rc->connected) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:SEND fd=%d sid=%08x len=%zu total=%d", - (int)rc->sock, stream_id, data_len, rp_conn_total(rc)); - if (data_len > 0) rp_sock_send(rc, entry->dgram + TCP_PROXY_HDR_SIZE, data_len); + (int)rc->sock, stream_id, data_len, tcp_proxy_server_conn_total(rc)); + if (data_len > 0) tcp_proxy_server_sock_send(rc, entry->dgram + TCP_PROXY_HDR_SIZE, data_len); } else { size_t new_len = rc->pending_len + data_len; uint8_t* new_buf = u_realloc(rc->pending_buf, new_len); if (new_buf) { if (data_len > 0) memcpy(new_buf + rc->pending_len, entry->dgram + TCP_PROXY_HDR_SIZE, data_len); rc->pending_buf = new_buf; rc->pending_len = new_len; - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "RP buffer sid=%08x added=%zu total=%zu", stream_id, data_len, new_len); - } else DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "RP buffer realloc(%zu) failed sid=%08x", new_len, stream_id); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TPS buffer sid=%08x added=%zu total=%zu", stream_id, data_len, new_len); + } else DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TPS buffer realloc(%zu) failed sid=%08x", new_len, stream_id); } queue_dgram_free(entry); queue_entry_free(entry); return 0; } -void remote_proxy_handle_close(struct UTUN_INSTANCE* inst, uint32_t stream_id) { +void tcp_proxy_server_handle_close(struct UTUN_INSTANCE* inst, uint32_t stream_id) { if (!inst) return; - struct remote_proxy_ctx* ctx = &inst->remote_proxy; - struct remote_proxy_conn** prev = &ctx->conns; + struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; + struct tcp_proxy_server_conn** prev = &ctx->conns; while (*prev) { - struct remote_proxy_conn* rc = *prev; + struct tcp_proxy_server_conn* rc = *prev; if (rc->stream_id == stream_id) { - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:CLOSE_RECV fd=%d sid=%08x total=%d sock_closed=%d connected=%d", - (int)rc->sock, stream_id, rp_conn_total(rc), rc->sock_closed, rc->connected); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:CLOSE_RECV fd=%d sid=%08x total=%d sock_closed=%d out=%zu", + (int)rc->sock, stream_id, tcp_proxy_server_conn_total(rc), rc->sock_closed, + rc->out_buf ? rc->out_len - rc->out_off : (size_t)0); rc->cli_closed = 1; - if (rc->sock != SOCKET_INVALID && rc->connected == 1) shutdown(rc->sock, SHUT_WR); - if (rc->sock_closed) rp_conn_free(rc); + if (rc->sock != SOCKET_INVALID && rc->connected == 1 && !rc->out_buf) shutdown(rc->sock, SHUT_WR); + if (rc->sock_closed && !rc->out_buf && !rc->pause_buf) tcp_proxy_server_conn_free(rc); return; } prev = &rc->next; } - DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "remote_proxy: CLOSE sid=%08x — no conn", stream_id); + DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy server: CLOSE sid=%08x — no conn, шлём ERROR", stream_id); + tcp_proxy_server_send_msg(inst, inst->node_id, TCP_PROXY_SUBCMD_ERROR, stream_id, NULL, 0); } -int remote_proxy_init(struct UTUN_INSTANCE* inst) { +int tcp_proxy_server_init(struct UTUN_INSTANCE* inst) { if (!inst) return -1; - struct remote_proxy_ctx* ctx = &inst->remote_proxy; + struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; memset(ctx, 0, sizeof(*ctx)); if (!inst->config) return 0; - ctx->enabled = inst->config->global.remote_proxy_enabled; + ctx->enabled = inst->config->global.tcp_proxy_server_enabled; ctx->inst = inst; if (!ctx->enabled) return 0; - g_rp_ctx = ctx; - etcp_router_bind(inst, ETCP_ID_TCP_PROXY, tcp_proxy_etcp_recv_cb); - if (!inst->config->global.tcp_proxy_enabled) { + g_tcp_proxy_server_ctx = ctx; + etcp_router_bind(inst, ETCP_ID_TCP_PROXY, tcp_proxy_client_etcp_recv_cb); + if (!inst->config->global.tcp_proxy_client_enabled) { udp_proxy_init(inst, inst->ua); icmp_proxy_init(inst, inst->ua); } - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "remote_proxy initialized node=%016llx", (unsigned long long)inst->node_id); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy server initialized node=%016llx", (unsigned long long)inst->node_id); return 0; } -void remote_proxy_destroy(struct UTUN_INSTANCE* inst) { +void tcp_proxy_server_destroy(struct UTUN_INSTANCE* inst) { if (!inst) return; - struct remote_proxy_ctx* ctx = &inst->remote_proxy; - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "remote_proxy destroying: conn_count=%d", ctx->conn_count); - struct remote_proxy_conn* rc = ctx->conns; - while (rc) { struct remote_proxy_conn* next = rc->next; rp_conn_free(rc); rc = next; } + struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy server destroying: conn_count=%d", ctx->conn_count); + struct tcp_proxy_server_conn* rc = ctx->conns; + while (rc) { struct tcp_proxy_server_conn* next = rc->next; tcp_proxy_server_conn_free(rc); rc = next; } ctx->conns = NULL; ctx->enabled = 0; - if (g_rp_ctx == ctx) g_rp_ctx = NULL; + if (g_tcp_proxy_server_ctx == ctx) g_tcp_proxy_server_ctx = NULL; udp_proxy_destroy(inst); icmp_proxy_destroy(inst); - DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "remote_proxy destroyed"); + DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy server destroyed"); } diff --git a/src/remote_proxy.h b/src/proxy/tcp_proxy_server.h similarity index 53% rename from src/remote_proxy.h rename to src/proxy/tcp_proxy_server.h index 33631ca9..8ca4a8f1 100644 --- a/src/remote_proxy.h +++ b/src/proxy/tcp_proxy_server.h @@ -1,32 +1,27 @@ -// remote_proxy.h — Удаленный TCP прокси (exit node) -#ifndef REMOTE_PROXY_H -#define REMOTE_PROXY_H +// tcp_proxy_server.h — TCP прокси-сервер (exit node) +#ifndef TCP_PROXY_SERVER_H +#define TCP_PROXY_SERVER_H #include #include "../lib/socket_compat.h" +#include "../lib/ll_queue.h" struct UTUN_INSTANCE; struct UASYNC; struct ll_entry; struct ETCP_CONN; -struct remote_proxy_ctx; #define TCP_PROXY_SUBCMD_CONNECT 0x01 -#define TCP_PROXY_SUBCMD_CONNECTED 0x02 #define TCP_PROXY_SUBCMD_DATA 0x03 #define TCP_PROXY_SUBCMD_CLOSE 0x04 #define TCP_PROXY_SUBCMD_ERROR 0x05 -#define TCP_PROXY_CONNECTED_OK 0 -#define TCP_PROXY_CONNECTED_REFUSED 1 - #define TCP_PROXY_HDR_SIZE 6 // svc_id(1)+subcmd(1)+stream_id(4) #define TCP_PROXY_CONNECT_HDR_SIZE 12 // HDR_SIZE + dest_ip(4)+dest_port(2) -#define TCP_PROXY_CONNECTED_HDR_SIZE 9 // HDR_SIZE + local_port(2)+status(1) -struct remote_proxy_conn { - struct remote_proxy_conn* next; - struct remote_proxy_ctx* ctx; +struct tcp_proxy_server_conn { + struct tcp_proxy_server_conn* next; + struct tcp_proxy_server* ctx; uint32_t stream_id; uint64_t peer_node_id; socket_t sock; @@ -53,28 +48,27 @@ struct remote_proxy_conn { void* close_timer; // таймер повтора CLOSE/ERROR int close_backoff; // backoff: 50..5000 tb (5ms..500ms) - uint8_t* pause_buf; // буфер при паузе чтения (normalizer переполнен) + uint8_t* pause_buf; // буфер при backpressure (ETCP send fail) size_t pause_len; - void* pause_timer; // таймер паузы чтения - int pause_backoff; // backoff: 50..5000 tb (5ms..500ms) + struct queue_waiter_handle pause_waiter; }; -struct remote_proxy_ctx { +struct tcp_proxy_server { int enabled; int conn_count; - struct remote_proxy_conn* conns; + struct tcp_proxy_server_conn* conns; struct UTUN_INSTANCE* inst; }; -int remote_proxy_init(struct UTUN_INSTANCE* inst); -void remote_proxy_destroy(struct UTUN_INSTANCE* inst); +int tcp_proxy_server_init(struct UTUN_INSTANCE* inst); +void tcp_proxy_server_destroy(struct UTUN_INSTANCE* inst); -struct remote_proxy_conn* remote_proxy_find_conn(struct remote_proxy_ctx* ctx, uint32_t stream_id); -void rp_conn_free(struct remote_proxy_conn* rc); +struct tcp_proxy_server_conn* tcp_proxy_server_find_conn(struct tcp_proxy_server* ctx, uint32_t stream_id); +void tcp_proxy_server_conn_free(struct tcp_proxy_server_conn* rc); -int remote_proxy_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry* entry, - uint32_t stream_id, uint64_t src_node_id); -int remote_proxy_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn, struct ll_entry* entry, uint32_t stream_id); -void remote_proxy_handle_close(struct UTUN_INSTANCE* inst, uint32_t stream_id); +int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry* entry, + uint32_t stream_id, uint64_t src_node_id); +int tcp_proxy_server_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* conn, struct ll_entry* entry, uint32_t stream_id); +void tcp_proxy_server_handle_close(struct UTUN_INSTANCE* inst, uint32_t stream_id); #endif diff --git a/src/udp_proxy.c b/src/proxy/udp_proxy.c similarity index 98% rename from src/udp_proxy.c rename to src/proxy/udp_proxy.c index 16b4e8df..7131a0a3 100644 --- a/src/udp_proxy.c +++ b/src/proxy/udp_proxy.c @@ -179,7 +179,7 @@ int udp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, uint32_t src_ip, uint16_t src_port, uint32_t dst_ip, uint16_t dst_port, const uint8_t* payload, size_t payload_len) { - if (!inst || !inst->tcp_proxy || !inst->tcp_proxy->tun) return -1; + if (!inst || !inst->tcp_proxy_client || !inst->tcp_proxy_client->tun) return -1; // Собрать IP/UDP ответный пакет size_t ip_len = 20 + 8 + payload_len; @@ -208,7 +208,7 @@ int udp_proxy_deliver_reply(struct UTUN_INSTANCE* inst, e->dgram = u_malloc(1 + ip_len); e->dgram[0] = 4; memcpy(e->dgram + 1, pkt, ip_len); e->len = 1 + ip_len; u_free(pkt); - queue_data_put(inst->tcp_proxy->tun->input_queue, e); + queue_data_put(inst->tcp_proxy_client->tun->input_queue, e); return 0; } @@ -242,7 +242,7 @@ int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) { ctx->inst = inst; ctx->ua = ua; ctx->flow_timeout_tb = UDP_FLOW_TIMEOUT_TB; ctx->expire_timer = NULL; - ctx->is_exit = inst->remote_proxy.enabled; + ctx->is_exit = inst->tcp_proxy_server.enabled; g_udp_ctx = ctx; etcp_router_bind(inst, ETCP_ID_UDP_PROXY, udp_proxy_recv_cb); diff --git a/src/udp_proxy.h b/src/proxy/udp_proxy.h similarity index 100% rename from src/udp_proxy.h rename to src/proxy/udp_proxy.h diff --git a/src/utun_instance.c b/src/utun_instance.c index f60f9a00..c16bb59e 100644 --- a/src/utun_instance.c +++ b/src/utun_instance.c @@ -145,29 +145,29 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u return -1; } - // Remote proxy (exit node, optional) — must be before tcp_proxy so - // tcp_proxy_create can overwrite the handler if it has remote mappings - if (remote_proxy_init(instance) != 0) { - DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "Failed to initialize remote_proxy (non-fatal)"); - } - - // TCP proxy (from [tcp_proxy] config section) - if (config->global.tcp_proxy_enabled) { - const char* tun_name = config->global.tcp_proxy_tun_name[0] ? config->global.tcp_proxy_tun_name : "tun_tcp"; - const char* tun_ip = config->global.tcp_proxy_tun_ip[0] ? config->global.tcp_proxy_tun_ip : "10.99.0.1"; - int mtu = config->global.tcp_proxy_mtu > 0 ? config->global.tcp_proxy_mtu : 1500; - instance->tcp_proxy = tcp_proxy_create(instance, ua, tun_name, tun_ip, mtu, g_tun_init_enabled ? 0 : 1, - config->global.tcp_proxy_mappings, config->global.tcp_proxy_mapping_count, - config->global.tcp_proxy_via_node_id); - if (instance->tcp_proxy) { - DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy enabled: TUN=%s IP=%s MTU=%d mappings=%d", - tun_name, tun_ip, mtu, config->global.tcp_proxy_mapping_count); + // TCP proxy server (exit node, optional) — must be before tcp_proxy_client so + // tcp_proxy_client_create can overwrite the handler if it has remote mappings + if (tcp_proxy_server_init(instance) != 0) { + DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "Failed to initialize tcp_proxy_server (non-fatal)"); + } + + // TCP proxy client (from [tcp_proxy] config section) + if (config->global.tcp_proxy_client_enabled) { + const char* tun_name = config->global.tcp_proxy_client_tun_name[0] ? config->global.tcp_proxy_client_tun_name : "tun_tcp"; + const char* tun_ip = config->global.tcp_proxy_client_tun_ip[0] ? config->global.tcp_proxy_client_tun_ip : "10.99.0.1"; + int mtu = config->global.tcp_proxy_client_mtu > 0 ? config->global.tcp_proxy_client_mtu : 1500; + instance->tcp_proxy_client = tcp_proxy_client_create(instance, ua, tun_name, tun_ip, mtu, g_tun_init_enabled ? 0 : 1, + config->global.tcp_proxy_client_mappings, config->global.tcp_proxy_client_mapping_count, + config->global.tcp_proxy_client_via_node_id); + if (instance->tcp_proxy_client) { + DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy client enabled: TUN=%s IP=%s MTU=%d mappings=%d", + tun_name, tun_ip, mtu, config->global.tcp_proxy_client_mapping_count); } else { - DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create TCP proxy"); + DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create TCP proxy client"); return -1; } } else { - instance->tcp_proxy = NULL; + instance->tcp_proxy_client = NULL; } return 0; @@ -339,15 +339,15 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) { instance->tun = NULL; } - // Cleanup TCP proxy module - if (instance->tcp_proxy) { - DEBUG_INFO(DEBUG_CATEGORY_TUN, "Destroying TCP proxy module"); - tcp_proxy_destroy(instance->tcp_proxy); - instance->tcp_proxy = NULL; + // Cleanup TCP proxy client module + if (instance->tcp_proxy_client) { + DEBUG_INFO(DEBUG_CATEGORY_TUN, "Destroying TCP proxy client module"); + tcp_proxy_client_destroy(instance->tcp_proxy_client); + instance->tcp_proxy_client = NULL; } - // Cleanup remote proxy - remote_proxy_destroy(instance); + // Cleanup TCP proxy server + tcp_proxy_server_destroy(instance); // Cleanup routing module (unbinds from etcp_router before etcp_router_destroy) routing_destroy(instance); diff --git a/src/utun_instance.h b/src/utun_instance.h index b6986c7f..80189e80 100644 --- a/src/utun_instance.h +++ b/src/utun_instance.h @@ -11,9 +11,9 @@ #include "firewall.h" #include "eim_nat.h" #include "nat_transport.h" -#include "tcp_proxy.h" +#include "proxy/tcp_proxy_client.h" #include "etcp_router.h" -#include "remote_proxy.h" +#include "proxy/tcp_proxy_server.h" // Forward declarations struct utun_config; @@ -97,15 +97,15 @@ struct UTUN_INSTANCE { // Socket initialization status: 0=OK, 1=partial (some sockets failed), -1=error (none created) int socket_init_status; - // TCP proxy (optional, NULL if not enabled) - struct tcp_proxy* tcp_proxy; + // TCP proxy client (optional, NULL if not enabled) + struct tcp_proxy_client* tcp_proxy_client; // etcp_router bindings и seq-connections (per-instance service routing) struct ETCP_ROUTER_BINDINGS router_bindings; struct ll_queue* router_conns; - // Remote proxy (exit node) - struct remote_proxy_ctx remote_proxy; + // TCP proxy server (exit node) + struct tcp_proxy_server tcp_proxy_server; }; // Functions diff --git a/tests/Makefile.am b/tests/Makefile.am index d75f816f..439f8d52 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -32,11 +32,11 @@ check_PROGRAMS = \ test_nat_engine \ test_nat_transport \ test_nat_stress \ - test_tcp_proxy \ + test_tcp_proxy_client \ test_lwip_tcp \ test_etcp_router \ test_etcp_router_unit \ - test_remote_proxy \ + test_tcp_proxy_server \ test_udp_proxy \ test_icmp_proxy \ test_radix \ @@ -46,7 +46,7 @@ check_PROGRAMS = \ # Долгие тесты: запускаются только вручную, не включаются в make check # test_etcp_congestion — DISABLED: старый congestion control удалён, ждёт новых BBR тестов -# test_tcp_proxy_remote — 2-node TCP через etcp, требует fix g_tcp_proxy singleton +# test_tcp_proxy_remote — 2-node TCP через etcp, требует fix tcp_proxy_client singleton noinst_PROGRAMS = # test_crypto and test_ecc_encrypt only needed for TinyCrypt (not when using OpenSSL) @@ -119,11 +119,11 @@ ETCP_FULL_OBJS = \ $(top_builddir)/src/utun-control_server.o \ $(TUN_PLATFORM_OBJ) \ $(top_builddir)/src/utun-utun_instance.o \ - $(top_builddir)/src/utun-tcp_proxy.o \ + $(top_builddir)/src/proxy/utun-tcp_proxy_client.o \ $(top_builddir)/src/utun-etcp_router.o \ - $(top_builddir)/src/utun-remote_proxy.o \ - $(top_builddir)/src/utun-udp_proxy.o \ - $(top_builddir)/src/utun-icmp_proxy.o \ + $(top_builddir)/src/proxy/utun-tcp_proxy_server.o \ + $(top_builddir)/src/proxy/utun-udp_proxy.o \ + $(top_builddir)/src/proxy/utun-icmp_proxy.o \ $(top_builddir)/src/lwip_tcp/utun-lwip_pbuf.o \ $(top_builddir)/src/lwip_tcp/utun-lwip_tcp.o \ $(top_builddir)/src/lwip_tcp/utun-lwip_tcp_in.o \ @@ -204,8 +204,8 @@ test_ipv6_sockets_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS #test_etcp_reinit_inflight_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source #test_etcp_reinit_inflight_LDADD = $(top_builddir)/src/utun-dummynet.o $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) -test_tcp_proxy_SOURCES = test_tcp_proxy.c -test_tcp_proxy_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_tcp_proxy_client_SOURCES = test_tcp_proxy_client.c +test_tcp_proxy_client_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) test_lwip_tcp_SOURCES = test_lwip_tcp.c test_lwip_tcp_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib @@ -222,8 +222,8 @@ test_etcp_router_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) test_etcp_router_unit_SOURCES = test_etcp_router_unit.c test_etcp_router_unit_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) -test_remote_proxy_SOURCES = test_remote_proxy.c -test_remote_proxy_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) +test_tcp_proxy_server_SOURCES = test_tcp_proxy_server.c +test_tcp_proxy_server_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) test_udp_proxy_SOURCES = test_udp_proxy.c test_udp_proxy_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS) diff --git a/tests/tcp_proxy_full/client.conf b/tests/tcp_proxy_full/client.conf index f6e4c1d7..95c608c1 100644 --- a/tests/tcp_proxy_full/client.conf +++ b/tests/tcp_proxy_full/client.conf @@ -15,7 +15,7 @@ keepalive=1 link=s1:127.0.0.1:15001 peer_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9 -[tcp_proxy] +[tcp_proxy_client] enabled=yes tun_name=tun_test_proxy tun_ip=10.200.30.1 diff --git a/tests/tcp_proxy_full/exit.conf b/tests/tcp_proxy_full/exit.conf index 887a297f..12e4298c 100644 --- a/tests/tcp_proxy_full/exit.conf +++ b/tests/tcp_proxy_full/exit.conf @@ -13,7 +13,7 @@ type=public [allowed_keys] allow_all=1 -[remote_proxy] +[tcp_proxy_server] enabled=yes [debug] diff --git a/tests/test_icmp_proxy.c b/tests/test_icmp_proxy.c index f5dc5504..460aee61 100644 --- a/tests/test_icmp_proxy.c +++ b/tests/test_icmp_proxy.c @@ -1,5 +1,5 @@ // test_icmp_proxy.c — 2-node ICMP ping test via etcp_router -// Node1 (client): tcp_proxy → sends ICMP_REQUEST to exit +// Node1 (client): tcp_proxy_client → sends ICMP_REQUEST to exit // Node2 (exit): receives → raw socket → sends echo → receives reply → sends ICMP_REPLY #include #include @@ -21,8 +21,8 @@ #include "../src/etcp_connections.h" #include "../src/etcp_api.h" #include "../src/etcp_router.h" -#include "../src/icmp_proxy.h" -#include "../src/remote_proxy.h" +#include "../src/proxy/icmp_proxy.h" +#include "../src/proxy/tcp_proxy_server.h" #include "../src/config_parser.h" #include "../src/utun_instance.h" #include "../src/routing.h" @@ -48,7 +48,7 @@ static const char* cfg_node_client(void) { "[server: s1]\naddr=127.0.0.1:9081\ntype=public\n" "[client: c1]\nkeepalive=1\nlink=s1:127.0.0.1:9082\n" "peer_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n" - "[tcp_proxy]\n" + "[tcp_proxy_client]\n" "enabled=yes\n" "tun_name=tun_tcp\n" "tun_ip=10.99.0.1\n" @@ -67,7 +67,7 @@ static const char* cfg_node_exit(void) { "tun_ifname=tun98\n" "[server: s1]\naddr=127.0.0.1:9082\ntype=public\n" "[allowed_keys]\nallow_all=1\n" - "[remote_proxy]\nenabled=yes\n"); + "[tcp_proxy_server]\nenabled=yes\n"); return buf; } diff --git a/tests/test_intensive_memory_pool.c b/tests/test_intensive_memory_pool.c index 513d112a..e69f7fce 100644 --- a/tests/test_intensive_memory_pool.c +++ b/tests/test_intensive_memory_pool.c @@ -30,31 +30,28 @@ static double test_without_pools(int iterations) { struct ll_queue* queue = queue_new(ua, 0, 0, 0,"q1"); // Без пулов for (int cycle = 0; cycle < iterations; cycle++) { - // Создать много waiters - struct queue_waiter* waiters[32]; - for (int i = 0; i < 32; i++) { - waiters[i] = queue_wait_threshold(queue, i, 0, intensive_waiter_callback, NULL); - } - - // Добавить записи + // Добавить записи (чтобы waiters становились в очередь) for (int i = 0; i < 10; i++) { void* data = queue_entry_new(64); - queue_data_put(queue, data); // Используем ID = i + queue_data_put(queue, data); } - // Удалить записи (триггер waiters) + // Создать много waiters (общий порог 0, count=10 => все в очередь) + struct queue_waiter_handle handles[32]; + for (int i = 0; i < 32; i++) { + queue_waiter_handle_init(&handles[i], intensive_waiter_callback, NULL); + queue_waiter_wait(queue, &handles[i]); + } + + // Удалить записи (триггер waiters — по одному за get) for (int i = 0; i < 10; i++) { void* retrieved = queue_data_get(queue); - if (retrieved) { - queue_entry_free(retrieved); - } + if (retrieved) queue_entry_free(retrieved); } - // Отменить waiters + // Отменить оставшиеся waiters for (int i = 0; i < 32; i++) { - if (waiters[i]) { - queue_cancel_wait(queue, waiters[i]); - } + queue_waiter_cancel(queue, &handles[i]); } } @@ -81,33 +78,28 @@ static double test_with_pools(int iterations) { } for (int cycle = 0; cycle < iterations; cycle++) { - // Создать много waiters - struct queue_waiter* waiters[32]; - for (int i = 0; i < 32; i++) { - waiters[i] = queue_wait_threshold(queue, i, 0, intensive_waiter_callback, NULL); - } - // Добавить записи из пула for (int i = 0; i < 10; i++) { void* data = queue_entry_new_from_pool(pool); - if (data) { - queue_data_put(queue, data); // Используем ID = i - } + if (data) queue_data_put(queue, data); + } + + // Создать много waiters + struct queue_waiter_handle handles[32]; + for (int i = 0; i < 32; i++) { + queue_waiter_handle_init(&handles[i], intensive_waiter_callback, NULL); + queue_waiter_wait(queue, &handles[i]); } // Удалить записи (триггер waiters) for (int i = 0; i < 10; i++) { void* retrieved = queue_data_get(queue); - if (retrieved) { - queue_entry_free(retrieved); // Вернется в пул - } + if (retrieved) queue_entry_free(retrieved); // Вернется в пул } - // Отменить waiters + // Отменить оставшиеся waiters for (int i = 0; i < 32; i++) { - if (waiters[i]) { - queue_cancel_wait(queue, waiters[i]); - } + queue_waiter_cancel(queue, &handles[i]); } } diff --git a/tests/test_ll_queue.c b/tests/test_ll_queue.c index 90abc86f..3dc38d0e 100644 --- a/tests/test_ll_queue.c +++ b/tests/test_ll_queue.c @@ -73,6 +73,15 @@ static void waiter_cb(struct ll_queue *q, void *arg) { (*(int*)arg)++; stats.cb_waiter++; } +static int waiter_order[10]; +static int waiter_order_idx; + +static void waiter_cb_order(struct ll_queue *q, void *arg) { + (void)q; + waiter_order[waiter_order_idx++] = *(int*)arg; + stats.cb_waiter++; +} + /* ------------------------------------------------------------------ */ static void test_basic(void) { TEST("basic creation / free"); @@ -165,10 +174,13 @@ static void test_waiter(void) { TEST("wait_threshold + cancel"); struct UASYNC *ua = uasync_create(); struct ll_queue *q = queue_new(ua, 0, 0, 0, "q6"); + queue_set_threshold(q, 2, 0); int called = 0; - struct queue_waiter *w = queue_wait_threshold(q, 2, 0, waiter_cb, &called); - ASSERT(w == NULL && called == 1, "immediate when condition met"); // empty queue + struct queue_waiter_handle h; + queue_waiter_handle_init(&h, waiter_cb, &called); + int ret = queue_waiter_wait(q, &h); + ASSERT(ret == 1 && called == 1, "immediate when condition met"); // empty queue for (int i = 0; i < 5; i++) { test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i; @@ -176,15 +188,151 @@ static void test_waiter(void) { } called = 0; - w = queue_wait_threshold(q, 2, 0, waiter_cb, &called); - ASSERT(w != NULL && called == 0, ""); + ret = queue_waiter_wait(q, &h); + ASSERT(ret == 0 && called == 0, ""); for (int i = 0; i < 3; i++) queue_entry_free((struct ll_entry*)queue_data_get(q)); for (int i = 0; i < 15; i++) uasync_poll(ua, 1); ASSERT_EQ(called, 1, ""); - queue_cancel_wait(q, w); + queue_waiter_cancel(q, &h); + queue_free(q); uasync_destroy(ua, 0); + PASS(); +} + +static void test_waiter_multiple(void) { + TEST("waiter multiple FIFO (round-robin)"); + struct UASYNC *ua = uasync_create(); + struct ll_queue *q = queue_new(ua, 0, 0, 0, "wm"); + queue_set_threshold(q, 2, 0); + + for (int i = 0; i < 5; i++) { + test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i; + queue_data_put(q, (struct ll_entry*)d); + } + + waiter_order_idx = 0; + int ids[4] = {10, 20, 30, 40}; + struct queue_waiter_handle handles[4]; + for (int i = 0; i < 4; i++) { + queue_waiter_handle_init(&handles[i], waiter_cb_order, &ids[i]); + ASSERT(queue_waiter_wait(q, &handles[i]) == 0, "should queue"); + } + + for (int i = 0; i < 5; i++) queue_entry_free((struct ll_entry*)queue_data_get(q)); + ASSERT_EQ(waiter_order_idx, 3, "3 waiters fired"); + ASSERT(waiter_order[0] == 10 && waiter_order[1] == 20 && waiter_order[2] == 30, "FIFO order"); + + for (int i = 0; i < 4; i++) queue_waiter_cancel(q, &handles[i]); + queue_free(q); uasync_destroy(ua, 0); + PASS(); +} + +static void test_waiter_cancel(void) { + TEST("waiter cancel middle"); + struct UASYNC *ua = uasync_create(); + struct ll_queue *q = queue_new(ua, 0, 0, 0, "wc"); + queue_set_threshold(q, 3, 0); + + for (int i = 0; i < 10; i++) { + test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i; + queue_data_put(q, (struct ll_entry*)d); + } + + waiter_order_idx = 0; + int ids[3] = {1, 2, 3}; + struct queue_waiter_handle handles[3]; + for (int i = 0; i < 3; i++) { + queue_waiter_handle_init(&handles[i], waiter_cb_order, &ids[i]); + queue_waiter_wait(q, &handles[i]); // count=10 > 3, all queued + } + + queue_waiter_cancel(q, &handles[1]); // cancel middle (ID 2) + + for (int i = 0; i < 10; i++) queue_entry_free((struct ll_entry*)queue_data_get(q)); + ASSERT_EQ(waiter_order_idx, 2, "2 fired (middle cancelled)"); + ASSERT(waiter_order[0] == 1 && waiter_order[1] == 3, "skip cancelled"); + + for (int i = 0; i < 3; i++) queue_waiter_cancel(q, &handles[i]); + queue_free(q); uasync_destroy(ua, 0); + PASS(); +} + +static void test_waiter_queue_free(void) { + TEST("queue_free clears waiters"); + struct UASYNC *ua = uasync_create(); + struct ll_queue *q = queue_new(ua, 0, 0, 0, "wf"); + + for (int i = 0; i < 3; i++) { + test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i; + queue_data_put(q, (struct ll_entry*)d); + } + + int called = 0; + struct queue_waiter_handle h; + queue_waiter_handle_init(&h, waiter_cb, &called); + queue_waiter_wait(q, &h); + ASSERT(h.internal != NULL, "waiter is queued"); + + queue_free(q); + ASSERT(h.internal == NULL, "internal cleared by queue_free"); + uasync_destroy(ua, 0); + PASS(); +} + +static void test_waiter_threshold(void) { + TEST("waiter dynamic threshold change"); + struct UASYNC *ua = uasync_create(); + struct ll_queue *q = queue_new(ua, 0, 0, 0, "wt"); + queue_set_threshold(q, 0, 0); // default, but explicit + + for (int i = 0; i < 10; i++) { + test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i; + queue_data_put(q, (struct ll_entry*)d); + } + + int called = 0; + struct queue_waiter_handle h; + queue_waiter_handle_init(&h, waiter_cb, &called); + int ret = queue_waiter_wait(q, &h); + ASSERT(ret == 0 && called == 0, "queued (10 > 0)"); + + queue_set_threshold(q, 10, 0); // raise threshold + queue_entry_free((struct ll_entry*)queue_data_get(q)); // count=9, check: 9 <= 10? YES + ASSERT_EQ(called, 1, "fired after threshold raised"); + + while (queue_entry_count(q)) queue_entry_free((struct ll_entry*)queue_data_get(q)); + queue_free(q); uasync_destroy(ua, 0); + PASS(); +} + +static void test_waiter_reuse(void) { + TEST("waiter handle reuse"); + struct UASYNC *ua = uasync_create(); + struct ll_queue *q = queue_new(ua, 0, 0, 0, "wr"); + + int called = 0; + struct queue_waiter_handle h; + queue_waiter_handle_init(&h, waiter_cb, &called); + + int ret = queue_waiter_wait(q, &h); + ASSERT(ret == 1 && called == 1, "immediate (empty queue)"); + + for (int i = 0; i < 3; i++) { + test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i; + queue_data_put(q, (struct ll_entry*)d); + } + + called = 0; + ret = queue_waiter_wait(q, &h); + ASSERT(ret == 0 && called == 0, "queued"); + + for (int i = 0; i < 3; i++) queue_entry_free((struct ll_entry*)queue_data_get(q)); + ASSERT_EQ(called, 1, "fired again on same handle"); + + queue_waiter_cancel(q, &h); + ASSERT(h.internal == NULL, "internal cleared"); queue_free(q); uasync_destroy(ua, 0); PASS(); } @@ -284,6 +432,11 @@ int main(void) { test_lifo_priority(); test_callback(); test_waiter(); + test_waiter_multiple(); + test_waiter_cancel(); + test_waiter_queue_free(); + test_waiter_threshold(); + test_waiter_reuse(); test_limits_hash(); test_pool(); test_stress(); diff --git a/tests/test_memory_pool_and_config.c b/tests/test_memory_pool_and_config.c index c0916802..64143e81 100644 --- a/tests/test_memory_pool_and_config.c +++ b/tests/test_memory_pool_and_config.c @@ -16,11 +16,6 @@ static int test_callback_count = 0; -static void test_callback(struct ll_queue* q, void* data, void* arg) { - (void)q; (void)data; (void)arg; - test_callback_count++; -} - static void test_waiter_callback(struct ll_queue* q, void* arg) { (void)q; (void)arg; test_callback_count++; @@ -49,41 +44,32 @@ int main() { uasync_destroy(ua, 0); return 1; } + queue_set_threshold(queue, 2, 0); - // Test multiple waiter allocations to trigger pool usage - struct queue_waiter* waiters[10]; + // Test multiple waiters with new linked-list backpressure + struct queue_waiter_handle handles[10]; for (int i = 0; i < 10; i++) { - waiters[i] = queue_wait_threshold(queue, i * 2, 0, test_waiter_callback, NULL); + queue_waiter_handle_init(&handles[i], test_waiter_callback, NULL); + queue_waiter_wait(queue, &handles[i]); // queue empty => immediate callback } - // Add some entries and trigger waiters + // Add some entries — waiters already fired, none queued for (int i = 0; i < 5; i++) { void* data = queue_entry_new(10); - queue_data_put(queue, data); // Используем ID = i + queue_data_put(queue, data); } - // Remove entries to trigger waiter callbacks + // Remove entries for (int i = 0; i < 5; i++) { void* retrieved = queue_data_get(queue); - if (retrieved) { - queue_entry_free(retrieved); - } + if (retrieved) queue_entry_free(retrieved); } - // Cancel remaining waiters + // Cancel (safe call even if not waiting) for (int i = 0; i < 10; i++) { - if (waiters[i]) { - queue_cancel_wait(queue, waiters[i]); - } + queue_waiter_cancel(queue, &handles[i]); } - // Get pool statistics - size_t allocations, reuse_count; - - DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "Pool statistics: allocations=%zu, reuse_count=%zu", allocations, reuse_count); - DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "Pool efficiency: %.1f%%", - allocations > 0 ? (100.0 * reuse_count / allocations) : 0.0); - queue_free(queue); uasync_destroy(ua, 0); diff --git a/tests/test_tcp_proxy.c b/tests/test_tcp_proxy_client.c similarity index 54% rename from tests/test_tcp_proxy.c rename to tests/test_tcp_proxy_client.c index c007965f..407e90d4 100644 --- a/tests/test_tcp_proxy.c +++ b/tests/test_tcp_proxy_client.c @@ -1,9 +1,9 @@ -// test_tcp_proxy.c — TCP proxy smoke test: create/destroy without leaks +// test_tcp_proxy_client.c — TCP proxy client smoke test: create/destroy without leaks #include #include #include #include "test_utils.h" -#include "../src/tcp_proxy.h" +#include "../src/proxy/tcp_proxy_client.h" #include "../src/tun_if.h" #include "../src/config_parser.h" #include "../lib/u_async.h" @@ -24,12 +24,12 @@ int main(void) { { struct UASYNC* ua = uasync_create(); if (!ua) { printf("[FAIL] uasync_create\n"); return 1; } - struct tcp_proxy_mapping_config m = {.local_port = 9090, .remote_ip = "127.0.0.1", .remote_port = 9999}; + struct tcp_proxy_client_mapping_config m = {.local_port = 9090, .remote_ip = "127.0.0.1", .remote_port = 9999}; - struct tcp_proxy* p = tcp_proxy_create(NULL, ua, "tun_tcp", "10.99.0.1", 1500, 1, &m, 1, 0); - if (!p) { printf("[FAIL] tcp_proxy_create with mapping\n"); uasync_destroy(ua, 0); return 1; } + struct tcp_proxy_client* p = tcp_proxy_client_create(NULL, ua, "tun_tcp", "10.99.0.1", 1500, 1, &m, 1, 0); + if (!p) { printf("[FAIL] tcp_proxy_client_create with mapping\n"); uasync_destroy(ua, 0); return 1; } - tcp_proxy_destroy(p); + tcp_proxy_client_destroy(p); uasync_destroy(ua, 0); check_no_leaks("create/destroy with mapping"); } @@ -39,14 +39,14 @@ int main(void) { struct UASYNC* ua = uasync_create(); if (!ua) { printf("[FAIL] uasync_create\n"); return 1; } - struct tcp_proxy* p = tcp_proxy_create(NULL, ua, "tun_tcp", "10.99.0.1", 1500, 1, NULL, 0, 0); - if (!p) { printf("[FAIL] tcp_proxy_create without mapping\n"); uasync_destroy(ua, 0); return 1; } + struct tcp_proxy_client* p = tcp_proxy_client_create(NULL, ua, "tun_tcp", "10.99.0.1", 1500, 1, NULL, 0, 0); + if (!p) { printf("[FAIL] tcp_proxy_client_create without mapping\n"); uasync_destroy(ua, 0); return 1; } - tcp_proxy_destroy(p); + tcp_proxy_client_destroy(p); uasync_destroy(ua, 0); check_no_leaks("create/destroy without mapping"); } - if (g_ok) printf("[PASS] test_tcp_proxy — create/destroy\n"); + if (g_ok) printf("[PASS] test_tcp_proxy_client — create/destroy\n"); return g_ok ? 0 : 1; } diff --git a/tests/test_tcp_proxy_remote.c b/tests/test_tcp_proxy_remote.c index 6f69c6ec..43cf944b 100644 --- a/tests/test_tcp_proxy_remote.c +++ b/tests/test_tcp_proxy_remote.c @@ -1,5 +1,5 @@ // test_tcp_proxy_remote.c — 2-node TCP 1MB forward via etcp_router -// Architecture: Client_A (active uIP) ↔ socketpair ↔ B (passive, etcp) ↔ Exit (remote_proxy) ↔ echo +// Architecture: Client_A (active uIP) ↔ socketpair ↔ B (passive, etcp) ↔ Exit (tcp_proxy_server) ↔ echo #include #include #include @@ -16,10 +16,10 @@ #include #include -#include "../src/tcp_proxy.h" +#include "../src/proxy/tcp_proxy_client.h" #include "../src/etcp.h" #include "../src/etcp_router.h" -#include "../src/remote_proxy.h" +#include "../src/proxy/tcp_proxy_server.h" #include "../src/config_parser.h" #include "../src/utun_instance.h" #include "../src/uip/uip.h" @@ -36,7 +36,7 @@ static pid_t g_echo_pid; static struct UTUN_INSTANCE* g_exit, *g_b; static struct UASYNC* g_ua; static int g_pair[2]; -static struct tcp_proxy* g_proxy_b, *g_cli; +static struct tcp_proxy_client* g_proxy_b, *g_cli; static int g_conn_idx, g_conn_up; static struct uip_conn* g_uc; static uint8_t *g_send_buf, *g_recv_buf; @@ -75,12 +75,12 @@ done: close(cli); close(srv); _exit(0); } static void start_test(void) { - struct tcp_proxy_mapping_config m = {.local_port=9090,.remote_ip="127.0.0.1",.remote_port=g_echo_port}; - g_proxy_b = tcp_proxy_create(g_b, g_ua, NULL, NULL, 0, 0, &m, 1, 0, g_pair[1], 0xCCCC000000000001ULL); + struct tcp_proxy_client_mapping_config m = {.local_port=9090,.remote_ip="127.0.0.1",.remote_port=g_echo_port}; + g_proxy_b = tcp_proxy_client_create(g_b, g_ua, NULL, NULL, 0, 0, &m, 1, 0, g_pair[1], 0xCCCC000000000001ULL); if (!g_proxy_b) { printf("[FAIL] proxy_b create\n"); g_done=-1; return; } g_b->tcp_proxy = g_proxy_b; - g_cli = tcp_proxy_create(NULL, g_ua, NULL, NULL, 0, 0, NULL, 0, 0, g_pair[0], 0); + g_cli = tcp_proxy_client_create(NULL, g_ua, NULL, NULL, 0, 0, NULL, 0, 0, g_pair[0], 0); if (!g_cli) { printf("[FAIL] cli create\n"); g_done=-1; return; } g_conn_idx = tcp_proxy_active_open(g_cli, "10.99.0.100", 9090); @@ -138,7 +138,7 @@ static const char* cfg_exit(int srv_port) { static char b[1024]; snprintf(b,size "my_private_key=67b705a92b41bcaae105af2d6a17743faa7b26ccebba8b3b9b0af05e9cd1d5fb\n" "my_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" "tun_ip=10.99.0.1/24\ntun_ifname=tun99\n" - "[server:s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n[remote_proxy]\nenabled=yes\n", srv_port); return b; } + "[server:s1]\naddr=127.0.0.1:%d\ntype=public\n[allowed_keys]\nallow_all=1\n[tcp_proxy_server]\nenabled=yes\n", srv_port); return b; } static const char* cfg_b(int srv_port, int cli_port) { static char b[1024]; snprintf(b,sizeof(b), "[global]\nmy_node_id=0xCCCC000000000002\n" @@ -147,7 +147,7 @@ static const char* cfg_b(int srv_port, int cli_port) { static char b[1024]; snpr "tun_ip=10.99.0.2/24\ntun_ifname=tun98\n" "[server:s1]\naddr=127.0.0.1:%d\ntype=public\n[client:c1]\nkeepalive=1\nlink=s1:127.0.0.1:%d\n" "peer_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" - "[remote_proxy]\nenabled=yes\n", srv_port, cli_port); return b; } + "[tcp_proxy_server]\nenabled=yes\n", srv_port, cli_port); return b; } int main(void) { printf("[SKIP] Active connections API removed, test skipped\n"); @@ -189,8 +189,8 @@ int main(void) { done: if (g_to_id) uasync_cancel_timeout(g_ua, g_to_id); - if (g_cli) tcp_proxy_destroy(g_cli); - if (g_proxy_b) tcp_proxy_destroy(g_proxy_b); + if (g_cli) tcp_proxy_client_destroy(g_cli); + if (g_proxy_b) tcp_proxy_client_destroy(g_proxy_b); if (g_exit) { g_exit->running=0; utun_instance_destroy(g_exit); } if (g_b) { g_b->running=0; utun_instance_destroy(g_b); } if (g_ua) uasync_destroy(g_ua, 0); diff --git a/tests/test_remote_proxy.c b/tests/test_tcp_proxy_server.c similarity index 62% rename from tests/test_remote_proxy.c rename to tests/test_tcp_proxy_server.c index 6affca02..0088cc5f 100644 --- a/tests/test_remote_proxy.c +++ b/tests/test_tcp_proxy_server.c @@ -1,4 +1,4 @@ -// test_remote_proxy.c — Test remote_proxy: CONNECT → socket → echo → verify +// test_tcp_proxy_server.c — Test tcp_proxy_server: CONNECT → socket → echo → verify #include #include #include @@ -15,7 +15,7 @@ #include "../src/etcp.h" #include "../src/etcp_api.h" #include "../src/etcp_router.h" -#include "../src/remote_proxy.h" +#include "../src/proxy/tcp_proxy_server.h" #include "../src/config_parser.h" #include "../src/utun_instance.h" #include "../src/routing.h" @@ -37,7 +37,6 @@ static int g_ok = 0, g_done = 0; static void* g_mon_id = NULL; static pid_t echo_pid = 0; static uint8_t send_buf[PAYLOAD_SIZE], recv_buf[PAYLOAD_SIZE]; -static int connected_ok = 0; static uint32_t stream_id = 1; static const char* cfg = @@ -47,7 +46,7 @@ static const char* cfg = "my_public_key=1c55e4ccae7c4470707759086738b10681bf88b81f198cc2ab54a647d1556e17c65e6b1833e0c771e5a39382c03067c388915a4c732191bc130480f20f8e00b9\n" "tun_ip=10.99.0.1/24\n" "tun_ifname=tun99\n" - "[remote_proxy]\n" + "[tcp_proxy_server]\n" "enabled=yes\n"; static void echo_server(void) { @@ -67,63 +66,46 @@ static void echo_server(void) { done: close(cli); close(srv); } -static void test_handler(struct ETCP_CONN* conn, struct ll_entry* entry) { - struct UTUN_INSTANCE* i = conn ? conn->instance : inst; - if (!i || !entry || entry->dgram == NULL || entry->len < TCP_PROXY_HDR_SIZE) { - if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; - } - uint8_t subcmd = entry->dgram[1]; - uint32_t sid = 0; memcpy(&sid, entry->dgram + 2, 4); - uint64_t src = conn ? conn->peer_node_id : i->node_id; - if (subcmd == TCP_PROXY_SUBCMD_CONNECT && sid == stream_id) { remote_proxy_handle_connect(i, entry, sid, src); return; } - if (sid != stream_id) { queue_dgram_free(entry); queue_entry_free(entry); return; } - if (subcmd == TCP_PROXY_SUBCMD_CONNECTED) { - uint8_t status = (entry->len >= TCP_PROXY_CONNECTED_HDR_SIZE) ? entry->dgram[TCP_PROXY_HDR_SIZE + 2] : 1; - connected_ok = (status == TCP_PROXY_CONNECTED_OK) ? 1 : -1; - } - queue_dgram_free(entry); queue_entry_free(entry); -} - static void monitor(void* arg) { (void)arg; if (g_done) { g_mon_id = NULL; return; } static int phase = 0; if (phase == 0) { phase = 1; - etcp_router_bind(inst, ETCP_ID_TCP_PROXY, test_handler); + struct tcp_proxy_server* ctx = &inst->tcp_proxy_server; + if (!ctx->enabled) { + /* enable if config didn't */ + ctx->enabled = 1; ctx->conn_count = 0; + etcp_router_bind(inst, ETCP_ID_TCP_PROXY, tcp_proxy_client_etcp_recv_cb); + } uint32_t ip = inet_addr("127.0.0.1"); uint16_t port = htons(ECHO_PORT); uint8_t payload[6]; memcpy(payload, &ip, 4); memcpy(payload + 4, &port, 2); struct ll_entry* e = queue_entry_new(0); - if (e) { - e->dgram = u_malloc(TCP_PROXY_HDR_SIZE + 6); - e->dgram[0] = ETCP_ID_TCP_PROXY; e->dgram[1] = TCP_PROXY_SUBCMD_CONNECT; - memcpy(e->dgram + 2, &stream_id, 4); - memset(e->dgram + 6, 0, 2); - memcpy(e->dgram + TCP_PROXY_HDR_SIZE, payload, 6); - e->len = TCP_PROXY_HDR_SIZE + 6; - etcp_route_send(inst, inst->node_id, e); - } + e->dgram = u_malloc(TCP_PROXY_HDR_SIZE + 6); + e->dgram[0] = ETCP_ID_TCP_PROXY; e->dgram[1] = TCP_PROXY_SUBCMD_CONNECT; + memcpy(e->dgram + 2, &stream_id, 4); + memcpy(e->dgram + TCP_PROXY_HDR_SIZE, payload, 6); + e->len = TCP_PROXY_HDR_SIZE + 6; + tcp_proxy_server_handle_connect(inst, e, stream_id, inst->node_id); } - if (phase == 1 && (connected_ok == 1 || connected_ok == -1)) { - if (connected_ok != 1) { printf("[FAIL] connect refused\n"); g_done = -1; return; } - // Get proxy conn, detach from uasync so we can use socket directly - struct remote_proxy_conn* rc = remote_proxy_find_conn(&inst->remote_proxy, stream_id); - if (!rc || rc->sock == SOCKET_INVALID) { printf("[FAIL] no proxy conn\n"); g_done = -1; return; } - uasync_remove_socket_t(rc->ua, rc->sock); rc->read_id = NULL; - // Send data through OS socket directly - for (int i = 0; i < PAYLOAD_SIZE; i++) send_buf[i] = (uint8_t)(rand() & 0xFF); - ssize_t n = send(rc->sock, send_buf, PAYLOAD_SIZE, MSG_NOSIGNAL); - if (n != PAYLOAD_SIZE) { printf("[FAIL] send %zd\n", n); g_done = -1; return; } - phase = 2; + if (phase == 1) { + struct tcp_proxy_server_conn* rc = tcp_proxy_server_find_conn(&inst->tcp_proxy_server, stream_id); + if (rc && rc->sock != SOCKET_INVALID && rc->connected) { + uasync_remove_socket_t(rc->ua, rc->sock); rc->read_id = NULL; + for (int i = 0; i < PAYLOAD_SIZE; i++) send_buf[i] = (uint8_t)(rand() & 0xFF); + ssize_t n = send(rc->sock, send_buf, PAYLOAD_SIZE, MSG_NOSIGNAL); + if (n != PAYLOAD_SIZE) { printf("[FAIL] send %zd\n", n); g_done = -1; return; } + phase = 2; + } } if (phase == 2) { - struct remote_proxy_conn* rc = remote_proxy_find_conn(&inst->remote_proxy, stream_id); + struct tcp_proxy_server_conn* rc = tcp_proxy_server_find_conn(&inst->tcp_proxy_server, stream_id); if (rc && rc->sock != SOCKET_INVALID) { ssize_t n = recv(rc->sock, recv_buf, PAYLOAD_SIZE, 0); if (n > 0) { if ((size_t)n == PAYLOAD_SIZE && memcmp(send_buf, recv_buf, PAYLOAD_SIZE) == 0) { - printf("[PASS] test_remote_proxy — %zd bytes echoed\n", n); g_ok = 1; + printf("[PASS] test_tcp_proxy_server — %zd bytes echoed\n", n); g_ok = 1; } else { printf("[FAIL] echo mismatch: got %zd expected %d\n", n, PAYLOAD_SIZE); } @@ -136,7 +118,7 @@ static void monitor(void* arg) { static void timeout_cb(void* arg) { (void)arg; - if (!g_done) { printf("[FAIL] timeout: conn=%d\n", connected_ok); g_done = -1; } + if (!g_done) { printf("[FAIL] timeout\n"); g_done = -1; } if (g_mon_id) { uasync_cancel_timeout(ua, g_mon_id); g_mon_id = NULL; } } @@ -152,7 +134,7 @@ int main(void) { if (!f) { kill(echo_pid, SIGTERM); waitpid(echo_pid,NULL,0); test_rmdir(temp_dir); return 1; } fprintf(f, "%s", cfg); fclose(f); - printf("=== test_remote_proxy ===\n"); + printf("=== test_tcp_proxy_server ===\n"); debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_categories(DEBUG_CATEGORY_ALL); utun_instance_set_tun_init_enabled(0); srand((unsigned)time(NULL)); diff --git a/tests/test_udp_proxy.c b/tests/test_udp_proxy.c index c64ce10a..d71c9679 100644 --- a/tests/test_udp_proxy.c +++ b/tests/test_udp_proxy.c @@ -1,5 +1,5 @@ // test_udp_proxy.c — 2-node UDP echo test via etcp_router -// Node1 (client): tcp_proxy → sends UDP_REQUEST to exit +// Node1 (client): tcp_proxy_client → sends UDP_REQUEST to exit // Node2 (exit): receives → creates UDP socket → echoes → sends UDP_REPLY #include #include @@ -19,8 +19,8 @@ #include "../src/etcp_connections.h" #include "../src/etcp_api.h" #include "../src/etcp_router.h" -#include "../src/udp_proxy.h" -#include "../src/remote_proxy.h" +#include "../src/proxy/udp_proxy.h" +#include "../src/proxy/tcp_proxy_server.h" #include "../src/config_parser.h" #include "../src/utun_instance.h" #include "../src/routing.h" @@ -47,7 +47,7 @@ static const char* cfg_node_client(void) { "[server: s1]\naddr=127.0.0.1:9071\ntype=public\n" "[client: c1]\nkeepalive=1\nlink=s1:127.0.0.1:9072\n" "peer_public_key=c594f33c91f3a2222795c2c110c527bf214ad1009197ce14556cb13df3c461b3c373bed8f205a8dd1fc0c364f90bf471d7c6f5db49564c33e4235d268569ac71\n" - "[tcp_proxy]\n" + "[tcp_proxy_client]\n" "enabled=yes\n" "tun_name=tun_tcp\n" "tun_ip=10.99.0.1\n" @@ -66,7 +66,7 @@ static const char* cfg_node_exit(void) { "tun_ifname=tun98\n" "[server: s1]\naddr=127.0.0.1:9072\ntype=public\n" "[allowed_keys]\nallow_all=1\n" - "[remote_proxy]\nenabled=yes\n"); + "[tcp_proxy_server]\nenabled=yes\n"); return buf; } @@ -126,7 +126,7 @@ static void monitor(void* arg) { if (l->initialized && c->crypto_ctx.initialized) exit_ok = 1; if (cli_ok && exit_ok) { g_test_phase = 1; - // Bind client handler for UDP replies (overrides what tcp_proxy_create set, for test verification) + // Bind client handler for UDP replies (overrides what tcp_proxy_client_create set, for test verification) etcp_router_bind(cli, ETCP_ID_UDP_PROXY, cli_recv_cb); // Send UDP_REQUEST for (int i = 0; i < PAYLOAD_SIZE; i++) send_buf[i] = (uint8_t)(rand() & 0xFF); diff --git a/tools/etcpmon/etcpmon_client.c b/tools/etcpmon/etcpmon_client.c index 8454fdf8..6c890eeb 100644 --- a/tools/etcpmon/etcpmon_client.c +++ b/tools/etcpmon/etcpmon_client.c @@ -602,6 +602,31 @@ static void handle_response(struct etcpmon_client* client, uint8_t msg_type, } break; } + + case ETCPMON_RSP_DEBUG_CONFIG: { + if (payload_size >= 2) { + client->debug_global_level = payload[0]; + client->debug_category_count = payload[1]; + uint16_t names_len = (uint16_t)strlen((const char*)(payload + 2)) + 1; + if (2 + names_len <= payload_size) { + size_t copy_len = names_len < sizeof(client->debug_category_names) ? names_len : sizeof(client->debug_category_names) - 1; + memcpy(client->debug_category_names, payload + 2, copy_len); + client->debug_category_names[copy_len] = '\0'; + uint16_t levels_off = 2 + names_len; + uint8_t lvl_count = client->debug_category_count; + if (lvl_count > ETCPMON_MAX_DEBUG_CATEGORIES) lvl_count = ETCPMON_MAX_DEBUG_CATEGORIES; + for (uint8_t i = 0; i < lvl_count && levels_off + i < payload_size; i++) + client->debug_category_levels[i] = payload[levels_off + i]; + client->debug_config_dirty = 1; + if (client->log_file) { + fprintf(client->log_file, "%llu: [LOG] RSP_DEBUG_CONFIG global=%d categories=%d\n", + (unsigned long long)get_timestamp_ms(), client->debug_global_level, client->debug_category_count); + fflush(client->log_file); + } + } + } + break; + } } } @@ -666,7 +691,7 @@ static void handle_response_with_callback(struct etcpmon_client* client, uint8_t /* === НОВОЕ: ВСЕГДА вызываем legacy обработчики (on_conn_list / on_metrics / on_error) === * Это позволяет использовать и старый код, и новый асинхронный send_request одновременно */ if (seq_id == 0 || msg_type == ETCPMON_RSP_CONN_LIST || - msg_type == ETCPMON_RSP_SOCKET_LIST || + msg_type == ETCPMON_RSP_SOCKET_LIST || msg_type == ETCPMON_RSP_DEBUG_CONFIG || msg_type == ETCPMON_RSP_METRICS || msg_type == ETCPMON_RSP_ERROR) { handle_response(client, msg_type, payload, payload_size); } @@ -859,6 +884,44 @@ struct metrics_history* etcpmon_client_get_history(struct etcpmon_client* client return &client->history; } +static void debug_config_auto_callback(void* arg, uint8_t msg_type, const uint8_t* payload, uint16_t payload_size) { + (void)arg; (void)msg_type; (void)payload; (void)payload_size; +} + +int etcpmon_client_request_debug_config(struct etcpmon_client* client) { + if (!client || client->sock == INVALID_SOCKET) return -1; + return etcpmon_client_send_request(client, ETCPMON_CMD_GET_DEBUG_CONFIG, NULL, 0, + debug_config_auto_callback, NULL); +} + +int etcpmon_client_send_debug_config(struct etcpmon_client* client, + uint8_t global_level, uint8_t count, const uint8_t* levels) { + if (!client || client->sock == INVALID_SOCKET || !levels || count == 0 || count > ETCPMON_MAX_DEBUG_CATEGORIES) return -1; + + uint8_t payload[2 + ETCPMON_MAX_DEBUG_CATEGORIES]; + payload[0] = global_level; + payload[1] = count; + memcpy(payload + 2, levels, count); + uint16_t payload_size = 2 + count; + + struct etcpmon_msg_header hdr; + etcpmon_build_header(&hdr, payload_size, ETCPMON_CMD_SET_DEBUG_CONFIG, 0); + + if (client->log_file) { + fprintf(client->log_file, "%llu: [LOG] CMD_SET_DEBUG_CONFIG global=%d count=%d\n", + (unsigned long long)get_timestamp_ms(), global_level, count); + fflush(client->log_file); + } + + int sent = send(client->sock, (const char*)&hdr, sizeof(hdr), 0); + if (sent != sizeof(hdr)) return -1; + sent = send(client->sock, (const char*)payload, payload_size, 0); + if (sent != payload_size) return -1; + client->debug_global_level = global_level; + for (uint8_t i = 0; i < count; i++) client->debug_category_levels[i] = levels[i]; + return 0; +} + void etcpmon_client_add_to_history(struct etcpmon_client* client, struct etcpmon_rsp_metrics* metrics) { if (!client || !metrics) return; diff --git a/tools/etcpmon/etcpmon_client.h b/tools/etcpmon/etcpmon_client.h index 66fe3c5b..1df6f9ee 100644 --- a/tools/etcpmon/etcpmon_client.h +++ b/tools/etcpmon/etcpmon_client.h @@ -89,6 +89,13 @@ struct etcpmon_client { /* Last received response seq_id (for matching) */ uint8_t last_response_seq_id; + /* Debug config cache */ + uint8_t debug_global_level; + uint8_t debug_category_count; + uint8_t debug_category_levels[ETCPMON_MAX_DEBUG_CATEGORIES]; + char debug_category_names[512]; + uint8_t debug_config_dirty; + /* Callbacks */ void (*on_connected)(void* user_data); void (*on_disconnected)(void* user_data); @@ -189,6 +196,9 @@ void etcpmon_client_add_to_history(struct etcpmon_client* client, struct etcpmon /* Send action command to server */ int etcpmon_client_send_action(struct etcpmon_client* client, const char* action); +int etcpmon_client_request_debug_config(struct etcpmon_client* client); +int etcpmon_client_send_debug_config(struct etcpmon_client* client, uint8_t global_level, uint8_t count, const uint8_t* levels); + uint64_t get_timestamp_ms(void); // Add this declaration #ifdef __cplusplus diff --git a/tools/etcpmon/etcpmon_gui.c b/tools/etcpmon/etcpmon_gui.c index dcfc4ee3..47eee1dc 100644 --- a/tools/etcpmon/etcpmon_gui.c +++ b/tools/etcpmon/etcpmon_gui.c @@ -20,7 +20,7 @@ //#pragma comment(lib, "user32.lib") //#pragma comment(lib, "gdi32.lib") #define WINDOW_WIDTH 1000 -#define WINDOW_HEIGHT 1250 +#define WINDOW_HEIGHT 1810 #define UPDATE_INTERVAL 10 /* 50ms → 20 samples per second */ /* Global app pointer for callbacks */ static struct etcpmon_app* g_app = NULL; @@ -33,6 +33,8 @@ static void OnConnectionSelect(struct etcpmon_app* app); static void OnAction(struct etcpmon_app* app); static void OnTimer(struct etcpmon_app* app); static void UpdateUIState(struct etcpmon_app* app); +static void OnDebugRadio(struct etcpmon_app* app, int control_id); +static void RefreshDebugUI(struct etcpmon_app* app); static void AddTooltip(HWND hTip, HWND hCtrl, const char* text); static void CreateTooltips(struct etcpmon_app* app); /* Callback functions for client events */ @@ -724,6 +726,61 @@ static void CreateControls(struct etcpmon_app* app) { q_col1 + 85 + i * 65, qy - 2, 60, 18, hWnd, (HMENU)((UINT_PTR)(IDC_EDIT_DEBUG_0 + i)), hInst, NULL); } + /* ======================================================================== + * Debug Levels section + * ======================================================================== */ + { + int dy = 1255; + app->hDebugGroupBox = CreateWindowExA(0, "BUTTON", "Debug Levels", + WS_CHILD | WS_VISIBLE | BS_GROUPBOX, + 10, dy, WINDOW_WIDTH - 20, 545, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + dy += 20; + const char* global_texts[6] = { "NONE", "ERROR", "WARN", "INFO", "DEBUG", "TRACE" }; + const char* header_texts[6] = { + "N\r\nO\r\nN\r\nE", "E\r\nR\r\nR\r\nO\r\nR", "W\r\nA\r\nR\r\nN", + "I\r\nN\r\nF\r\nO", "D\r\nE\r\nB\r\nU\r\nG", "T\r\nR\r\nA\r\nC\r\nE" + }; + static const char* cat_names[] = { + "UASYNC","LL_QUEUE","CONNECTION","ETCP","CRYPTO", + "MEMORY","TIMING","CONFIG","TUN","ROUTING", + "TIMERS","NORMALIZER","BGP","SOCKET","CONTROL", + "DUMP","TRAFFIC","DEBUG_CAT","GENERAL","NAT","KEEPALIVE" + }; + int ncat = sizeof(cat_names) / sizeof(cat_names[0]); + app->debug_cat_count = ncat; + + CreateWindowExA(0, "STATIC", "Global:", WS_CHILD | WS_VISIBLE, + 20, dy + 2, 50, 20, hWnd, (HMENU)IDC_STATIC, hInst, NULL); + for (int l = 0; l < 6; l++) { + int lx = 75 + l * 85; + app->hDebugGlobalRadio[l] = CreateWindowExA(0, "BUTTON", global_texts[l], + WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, + lx, dy, 80, 20, hWnd, (HMENU)(UINT_PTR)(IDC_DEBUG_GLOBAL_BASE + l), hInst, NULL); + } + + dy += 22; + for (int l = 0; l < 6; l++) { + int lx = 75 + l * 85; + app->hDebugHeaderEdit[l] = CreateWindowExA(WS_EX_CLIENTEDGE, "EDIT", header_texts[l], + WS_CHILD | WS_VISIBLE | ES_READONLY | ES_CENTER | ES_MULTILINE, + lx, dy, 80, 60, hWnd, (HMENU)(UINT_PTR)(IDC_DEBUG_HEADER_BASE + l), hInst, NULL); + } + + dy += 65; + for (int r = 0; r < ncat; r++) { + app->hDebugCatLabel[r] = CreateWindowExA(0, "STATIC", cat_names[r], + WS_CHILD | WS_VISIBLE | SS_LEFT | SS_CENTERIMAGE, + 20, dy + 1, 50, 16, hWnd, (HMENU)(UINT_PTR)(IDC_DEBUG_NAME_BASE + r), hInst, NULL); + for (int l = 0; l < 6; l++) { + int lx = 75 + l * 85; + app->hDebugCatRadio[r][l] = CreateWindowExA(0, "BUTTON", "", + WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, + lx, dy, 80, 16, hWnd, (HMENU)(UINT_PTR)(IDC_DEBUG_ROW_BASE + r * 8 + l), hInst, NULL); + } + dy += 18; + } + } + } static void AddTooltip(HWND hTip, HWND hCtrl, const char* text) { @@ -942,21 +999,27 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM l switch (message) { case WM_COMMAND: if (app) { - switch (LOWORD(wParam)) { - case IDC_BTN_CONNECT: - OnConnect(app); - break; - case IDC_BTN_DISCONNECT: - OnDisconnect(app); - break; - case IDC_LIST_CONNECTIONS: - if (HIWORD(wParam) == LBN_SELCHANGE) { - OnConnectionSelect(app); - } - break; - case IDC_BTN_ACTION: - OnAction(app); - break; + WORD ctrl_id = LOWORD(wParam); + if ((ctrl_id >= IDC_DEBUG_GLOBAL_BASE && ctrl_id <= IDC_DEBUG_GLOBAL_BASE + 5) || + (ctrl_id >= IDC_DEBUG_ROW_BASE && ctrl_id < IDC_DEBUG_ROW_BASE + IDC_DEBUG_MAX_CAT * 8)) { + OnDebugRadio(app, ctrl_id); + } else { + switch (ctrl_id) { + case IDC_BTN_CONNECT: + OnConnect(app); + break; + case IDC_BTN_DISCONNECT: + OnDisconnect(app); + break; + case IDC_LIST_CONNECTIONS: + if (HIWORD(wParam) == LBN_SELCHANGE) { + OnConnectionSelect(app); + } + break; + case IDC_BTN_ACTION: + OnAction(app); + break; + } } } break; @@ -1049,6 +1112,57 @@ static void OnAction(struct etcpmon_app* app) { } } +static void OnDebugRadio(struct etcpmon_app* app, int control_id) { + static int in_handler = 0; + if (in_handler) return; + if (!app || !app->client) return; + in_handler = 1; + + if (control_id >= IDC_DEBUG_GLOBAL_BASE && control_id <= IDC_DEBUG_GLOBAL_BASE + 5) { + int level = control_id - IDC_DEBUG_GLOBAL_BASE; + for (int l = 0; l <= 5; l++) + SendMessage(app->hDebugGlobalRadio[l], BM_SETCHECK, (l == level) ? BST_CHECKED : BST_UNCHECKED, 0); + app->client->debug_global_level = (uint8_t)level; + } else if (control_id >= IDC_DEBUG_ROW_BASE) { + int flat = control_id - IDC_DEBUG_ROW_BASE; + int row = flat / 8; + int level = flat % 8; + if (row >= app->debug_cat_count || level > 5) { in_handler = 0; return; } + for (int l = 0; l <= 5; l++) { + if (app->hDebugCatRadio[row][l]) + SendMessage(app->hDebugCatRadio[row][l], BM_SETCHECK, (l == level) ? BST_CHECKED : BST_UNCHECKED, 0); + } + app->client->debug_category_levels[row] = (uint8_t)level; + } + + etcpmon_client_send_debug_config(app->client, app->client->debug_global_level, + app->debug_cat_count, app->client->debug_category_levels); + in_handler = 0; +} + +static void RefreshDebugUI(struct etcpmon_app* app) { + if (!app || !app->client) return; + struct etcpmon_client* cl = app->client; + + for (int l = 0; l <= 5; l++) { + if (app->hDebugGlobalRadio[l]) + SendMessage(app->hDebugGlobalRadio[l], BM_SETCHECK, + (l == cl->debug_global_level) ? BST_CHECKED : BST_UNCHECKED, 0); + } + + int n = cl->debug_category_count; + if (n > app->debug_cat_count) n = app->debug_cat_count; + for (int r = 0; r < n; r++) { + int level = cl->debug_category_levels[r]; + if (level > 5) level = 0; + for (int l = 0; l <= 5; l++) { + if (app->hDebugCatRadio[r][l]) + SendMessage(app->hDebugCatRadio[r][l], BM_SETCHECK, + (l == level) ? BST_CHECKED : BST_UNCHECKED, 0); + } + } +} + static void OnTimer(struct etcpmon_app* app) { if (!app || !app->client) return; @@ -1064,9 +1178,15 @@ static void OnTimer(struct etcpmon_app* app) { if (app->need_initial_request && app->isConnected) { etcpmon_client_request_list(app->client); etcpmon_client_request_socket_list(app->client); + etcpmon_client_request_debug_config(app->client); app->need_initial_request = 0; } + if (app->isConnected && app->client->debug_config_dirty) { + RefreshDebugUI(app); + app->client->debug_config_dirty = 0; + } + } static void UpdateUIState(struct etcpmon_app* app) { diff --git a/tools/etcpmon/etcpmon_gui.h b/tools/etcpmon/etcpmon_gui.h index bae55f5c..bc317059 100644 --- a/tools/etcpmon/etcpmon_gui.h +++ b/tools/etcpmon/etcpmon_gui.h @@ -156,6 +156,13 @@ extern "C" { #define IDC_EDIT_ACTIVE_TIMEOUTS 700 #define IDC_EDIT_BUSY_MEMORY 701 +/* Debug Levels control IDs */ +#define IDC_DEBUG_MAX_CAT 64 +#define IDC_DEBUG_GLOBAL_BASE 1000 /* +0..5 radio: NONE(0)..TRACE(5) */ +#define IDC_DEBUG_HEADER_BASE 1010 /* +0..5 header EDIT */ +#define IDC_DEBUG_ROW_BASE 1100 /* + row*8 + level */ +#define IDC_DEBUG_NAME_BASE 2000 /* + row: category name label */ + /* Graph control ID */ #define IDC_GRAPH 500 @@ -315,6 +322,14 @@ struct etcpmon_app { int graph_cursor_y; int graph_cursor_active; + /* Debug Levels controls */ + HWND hDebugGroupBox; + HWND hDebugGlobalRadio[6]; /* 0=NONE..5=TRACE */ + HWND hDebugHeaderEdit[6]; /* вертикальный текст: T\nR\nA\nC\nE итд */ + HWND hDebugCatLabel[IDC_DEBUG_MAX_CAT]; /* метки категорий */ + HWND hDebugCatRadio[IDC_DEBUG_MAX_CAT][6]; /* [row][level] радио-кнопки */ + int debug_cat_count; + /* Client state */ struct etcpmon_client* client; diff --git a/tools/etcpmon/etcpmon_protocol.h b/tools/etcpmon/etcpmon_protocol.h index dc2cbaef..bbe9c903 100644 --- a/tools/etcpmon/etcpmon_protocol.h +++ b/tools/etcpmon/etcpmon_protocol.h @@ -35,6 +35,7 @@ extern "C" { #define ETCPMON_MAX_CONN_NAME 32 #define ETCPMON_MAX_CONNECTIONS 250 #define ETCPMON_MAX_LINKS 16 +#define ETCPMON_MAX_DEBUG_CATEGORIES 64 /* Update interval in milliseconds */ #define ETCPMON_UPDATE_INTERVAL_MS 100 @@ -50,11 +51,14 @@ extern "C" { #define ETCPMON_CMD_DISCONNECT 0x04 /* Disconnect from monitoring */ #define ETCPMON_CMD_LIST_SOCKETS 0x05 /* Request local socket list */ #define ETCPMON_CMD_ACTION 0x06 /* Execute action (text command) */ +#define ETCPMON_CMD_GET_DEBUG_CONFIG 0x07 /* Request current debug levels */ +#define ETCPMON_CMD_SET_DEBUG_CONFIG 0x08 /* Set debug levels (byte array) */ /* Server -> Client responses */ #define ETCPMON_RSP_CONN_LIST 0x81 /* Connection list response */ #define ETCPMON_RSP_METRICS 0x82 /* Metrics response */ #define ETCPMON_RSP_SOCKET_LIST 0x83 /* Socket list response */ +#define ETCPMON_RSP_DEBUG_CONFIG 0x84 /* Debug levels response */ #define ETCPMON_RSP_ERROR 0xFF /* Error response */ /* Error codes for RSP_ERROR */ @@ -286,6 +290,21 @@ struct etcpmon_rsp_metrics { /* Followed by etcp.links_count * struct etcpmon_link_metrics */ }; +/* RSP_DEBUG_CONFIG: Server -> Client debug levels */ +struct etcpmon_rsp_debug_config { + uint8_t global_level; /* Global debug level (0=NONE..5=TRACE) */ + uint8_t category_count; /* Number of categories */ + /* Followed by: char names_csv[]; null-terminated CSV: "UASYNC,LL_QUEUE,...\0" */ + /* Followed by: uint8_t levels[category_count]; per-category levels */ +}; + +/* CMD_SET_DEBUG_CONFIG: Client -> Server set debug levels */ +struct etcpmon_cmd_set_debug { + uint8_t global_level; /* Global debug level (0=NONE..5=TRACE) */ + uint8_t category_count; /* Number of categories */ + uint8_t levels[ETCPMON_MAX_DEBUG_CATEGORIES]; /* Actual length = category_count */ +}; + /* RSP_ERROR: Error response */ struct etcpmon_rsp_error { uint8_t error_code; /* Error code */ diff --git a/utun.conf.sample b/utun.conf.sample index f115fffc..84eee8c6 100644 --- a/utun.conf.sample +++ b/utun.conf.sample @@ -76,10 +76,10 @@ allow=all #tun_ip=100.64.1.1/24 # IP клиентского NAT TUN #nat_via=0xABCD000000000001 # node_id провайдера (у кого запрашивать NAT) -# --- TCP Proxy (локальный TCP/UDP/ICMP прокси) --- -# Проксирует входящие TCP (через uIP), UDP и ICMP ping через указанный via_node. +# --- TCP Proxy Client (локальный TCP/UDP/ICMP прокси) --- +# Проксирует входящие TCP (через lwIP), UDP и ICMP ping через указанный via_node. # Все три протокола идут через один и тот же удалённый узел. -#[tcp_proxy] +#[tcp_proxy_client] #enabled=yes #tun_name=tun_tcp # имя TUN интерфейса (по умолчанию tun_tcp) #tun_ip=10.99.0.1 # IP адрес TUN интерфейса @@ -89,7 +89,7 @@ allow=all #forward=8000 -> 10.0.0.50:80 #forward=2222 -> 10.0.0.50:22 -# --- Remote Proxy (удаленный exit node) --- +# --- TCP Proxy Server (удаленный exit node) --- # Принимает CONNECT-запросы от других нод и открывает OS сокеты к адресатам. -#[remote_proxy] +#[tcp_proxy_server] #enabled=yes