From d694c92b1febedd7e8c5d29fef2c086c6bcaf8e9 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Sat, 14 Feb 2026 01:39:55 +0300 Subject: [PATCH] Refactor etcp_api: per-instance bindings with struct ETCP_BINDINGS Changed from global bindings to per-instance bindings: 1. Added struct ETCP_BINDINGS in etcp_api.h: - Contains array of callbacks[ETCP_MAX_BINDINGS] - NULL means not bound 2. Updated UTUN_INSTANCE (utun_instance.h): - Added field: struct ETCP_BINDINGS api_bindings - Initialized to NULL by calloc in utun_instance_create 3. Updated API functions (etcp_api.h/c): - etcp_bind(inst, id, callback) - per-instance binding - etcp_unbind(inst, id) - per-instance unbinding - Removed etcp_api_init/etcp_api_deinit (not needed) - etcp_int_recv now uses conn->instance->api_bindings.callbacks[id] 4. Updated test_etcp_api.c: - Separate callbacks for server and client - Register via etcp_bind(server_instance, ...) and etcp_bind(client_instance, ...) - Removed global etcp_api_init/etcp_api_deinit calls Test passes: All 100 packets transmitted in each direction. --- src/etcp_api.c | 98 +++++++++++++++++---------------------- src/etcp_api.h | 42 ++++++++--------- src/utun_instance.h | 4 ++ tests/test_etcp_api.c | 104 +++++++++++++++++++++++------------------- 4 files changed, 123 insertions(+), 125 deletions(-) diff --git a/src/etcp_api.c b/src/etcp_api.c index e5a663d5..2d95567f 100644 --- a/src/etcp_api.c +++ b/src/etcp_api.c @@ -1,11 +1,12 @@ /** * @file etcp_api.c - * @brief Реализация API для приёма-передачи пакетов через ETCP + * @brief Реализация API для приёма-передачи пакетов через ETCP (per-instance bindings) */ #include "etcp_api.h" #include "etcp.h" #include "pkt_normalizer.h" +#include "utun_instance.h" #include "../lib/debug_config.h" #include @@ -13,43 +14,14 @@ #define ETCP_MAX_BINDINGS 256 /**< Максимальное количество bindings (по одному на каждый ID 0-255) */ -/** - * @brief Структура binding для ID - */ -struct ETCP_BINDING { - etcp_recv_fn callback; /**< Коллбэк для обработки пакетов */ - uint8_t active; /**< Флаг активности binding */ -}; - -// Таблица bindings (индекс = ID пакета) -static struct ETCP_BINDING g_bindings[ETCP_MAX_BINDINGS]; -static int g_api_initialized = 0; - -void etcp_api_init(void) { - if (g_api_initialized) { - return; - } - - memset(g_bindings, 0, sizeof(g_bindings)); - g_api_initialized = 1; - - DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "ETCP API initialized"); -} - -void etcp_api_deinit(void) { - if (!g_api_initialized) { - return; +int etcp_bind(struct UTUN_INSTANCE* inst, uint8_t id, etcp_recv_fn callback) { + if (!inst) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_bind: NULL instance"); + return -1; } - memset(g_bindings, 0, sizeof(g_bindings)); - g_api_initialized = 0; - - DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "ETCP API deinitialized"); -} - -int etcp_bind(uint8_t id, etcp_recv_fn callback) { - if (!g_api_initialized) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_bind: API not initialized"); + if (id >= ETCP_MAX_BINDINGS) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_bind: Invalid ID %u (max=%d)", id, ETCP_MAX_BINDINGS); return -1; } @@ -58,32 +30,36 @@ int etcp_bind(uint8_t id, etcp_recv_fn callback) { return -1; } - if (g_bindings[id].active) { + if (inst->api_bindings.callbacks[id] != NULL) { DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_bind: ID %u already bound, overwriting", id); } - g_bindings[id].callback = callback; - g_bindings[id].active = 1; + inst->api_bindings.callbacks[id] = callback; - DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_bind: Bound ID %u to callback %p", id, (void*)callback); + DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_bind: Bound ID %u to callback %p for instance %p", + id, (void*)callback, (void*)inst); return 0; } -int etcp_unbind(uint8_t id) { - if (!g_api_initialized) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: API not initialized"); +int etcp_unbind(struct UTUN_INSTANCE* inst, uint8_t id) { + if (!inst) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: NULL instance"); + return -1; + } + + if (id >= ETCP_MAX_BINDINGS) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: Invalid ID %u", id); return -1; } - if (!g_bindings[id].active) { + if (inst->api_bindings.callbacks[id] == NULL) { DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: ID %u not bound", id); return -1; } - g_bindings[id].callback = NULL; - g_bindings[id].active = 0; + inst->api_bindings.callbacks[id] = NULL; - DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: Unbound ID %u", id); + DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: Unbound ID %u for instance %p", id, (void*)inst); return 0; } @@ -119,7 +95,7 @@ int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry) { return -1; } - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_send: Packet queued for sending"); + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_send: Packet queued for sending to [%s]", conn->log_name); return 0; } @@ -152,20 +128,30 @@ void etcp_int_recv(struct ll_queue* queue, void* arg) { // Получаем ID пакета (первый байт кодограммы) uint8_t id = entry->dgram[0]; - DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Received packet with ID %u from conn %p", - id, (void*)conn); + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Received packet with ID %u from conn [%s] %p", + id, conn->log_name, (void*)conn); + + // Получаем instance из соединения + struct UTUN_INSTANCE* inst = conn->instance; + if (!inst) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Connection has no instance"); + queue_entry_free(entry); + queue_dgram_free(entry); + queue_resume_callback(queue); + return; + } - // Ищем binding для этого ID - if (id < ETCP_MAX_BINDINGS && g_bindings[id].active && g_bindings[id].callback) { + // Ищем binding для этого ID в instance + if (id < ETCP_MAX_BINDINGS && inst->api_bindings.callbacks[id] != NULL) { // Вызываем коллбэк DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Delivering packet ID %u to callback %p", - id, (void*)g_bindings[id].callback); - g_bindings[id].callback(conn, entry); + id, (void*)inst->api_bindings.callbacks[id]); + inst->api_bindings.callbacks[id](conn, entry); } else { // Нет binding для этого ID - по умолчанию считаем что это данные (ID=0) - if (g_bindings[0].active && g_bindings[0].callback) { + if (inst->api_bindings.callbacks[0] != NULL) { DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_recv: No binding for ID %u, routing to default (ID=0)", id); - g_bindings[0].callback(conn, entry); + inst->api_bindings.callbacks[0](conn, entry); } else { // Нет default binding - освобождаем пакет DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_recv: No binding for ID %u and no default handler, dropping packet", id); diff --git a/src/etcp_api.h b/src/etcp_api.h index 2a0dce7c..eb55e3c4 100644 --- a/src/etcp_api.h +++ b/src/etcp_api.h @@ -1,11 +1,11 @@ /** * @file etcp_api.h - * @brief API для приёма-передачи пакетов через ETCP + * @brief API для приёма-передачи пакетов через ETCP (per-instance bindings) * * Основные функции: * - etcp_send() - отправить пакет в очередь normalizer * - etcp_bind() - подписаться на пакеты с определенным ID - * - etcp_recv() - коллбэк для сбора пакетов из всех подключений + * - etcp_int_recv() - коллбэк для сбора пакетов из всех подключений * * Формат кодограмм: * cmd = 0 - пакет для передачи адресату @@ -19,8 +19,11 @@ #include #include "../lib/ll_queue.h" +#define ETCP_MAX_BINDINGS 256 + // Forward declarations struct ETCP_CONN; +struct UTUN_INSTANCE; /** * @brief Тип коллбэка для приёма пакетов @@ -32,6 +35,13 @@ struct ETCP_CONN; */ typedef void (*etcp_recv_fn)(struct ETCP_CONN* conn, struct ll_entry* entry); +/** + * @brief Структура bindings для ETCP API (per-instance) + */ +struct ETCP_BINDINGS { + etcp_recv_fn callbacks[ETCP_MAX_BINDINGS]; /**< Массив callbacks, NULL = не установлен */ +}; + /** * @brief Отправить пакет в очередь normalizer * @@ -45,24 +55,26 @@ typedef void (*etcp_recv_fn)(struct ETCP_CONN* conn, struct ll_entry* entry); int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry); /** - * @brief Подписаться на все пакеты с указанным ID + * @brief Подписаться на все пакеты с указанным ID для данного instance * + * @param inst UTUN instance * @param id Идентификатор пакета (первый байт кодограммы, cmd) * @param callback Коллбэк для обработки пакетов с этим ID - * @return 0 при успехе, -1 при ошибке (например, нет свободных слотов) + * @return 0 при успехе, -1 при ошибке * * @note ID=0 зарезервирован для обычных пакетов данных - * @note Можно зарегистрировать только один коллбэк на каждый ID + * @note Можно зарегистрировать только один коллбэк на каждый ID в пределах instance */ -int etcp_bind(uint8_t id, etcp_recv_fn callback); +int etcp_bind(struct UTUN_INSTANCE* inst, uint8_t id, etcp_recv_fn callback); /** - * @brief Отписаться от пакетов с указанным ID + * @brief Отписаться от пакетов с указанным ID для данного instance * + * @param inst UTUN instance * @param id Идентификатор пакета * @return 0 при успехе, -1 если binding не найден */ -int etcp_unbind(uint8_t id); +int etcp_unbind(struct UTUN_INSTANCE* inst, uint8_t id); /** * @brief Коллбэк для очередей output ll_queue normalizer @@ -78,18 +90,4 @@ int etcp_unbind(uint8_t id); */ void etcp_int_recv(struct ll_queue* queue, void* arg); -/** - * @brief Инициализация API - * - * Должна быть вызвана один раз перед использованием API - */ -void etcp_api_init(void); - -/** - * @brief Деинициализация API - * - * Очищает все bindings - */ -void etcp_api_deinit(void); - #endif // ETCP_API_H diff --git a/src/utun_instance.h b/src/utun_instance.h index ab848394..b27b10fa 100644 --- a/src/utun_instance.h +++ b/src/utun_instance.h @@ -6,6 +6,7 @@ #include #include "../lib/memory_pool.h" #include "secure_channel.h" +#include "etcp_api.h" // Forward declarations struct utun_config; @@ -14,6 +15,7 @@ struct ROUTE_TABLE; struct ETCP_CONN; struct ETCP_SOCKET; struct tun_if; +struct ETCP_BINDINGS; // uTun instance configuration struct UTUN_INSTANCE { @@ -47,6 +49,8 @@ struct UTUN_INSTANCE { // Active sockets struct ETCP_SOCKET* etcp_sockets;// linked-list + // ETCP API bindings (per-instance) + struct ETCP_BINDINGS api_bindings; }; // Functions diff --git a/tests/test_etcp_api.c b/tests/test_etcp_api.c index d8cd5a51..76c4366f 100644 --- a/tests/test_etcp_api.c +++ b/tests/test_etcp_api.c @@ -143,11 +143,12 @@ static int verify_packet_data(uint8_t* buffer, int size, int expected_seq) { return 1; } -// Универсальный callback для получения пакетов -// Определяет направление по соединению (client_conn vs server_conn) -static void recv_callback(struct ETCP_CONN* conn, struct ll_entry* entry) { +// Callback для получения пакетов на сервере (forward direction) +static void server_recv_callback(struct ETCP_CONN* conn, struct ll_entry* entry) { + (void)conn; + if (!entry || !entry->dgram || entry->len < PACKET_HEADER_SIZE + 1) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Received invalid packet"); + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Server received invalid packet"); if (entry) { queue_entry_free(entry); queue_dgram_free(entry); @@ -155,30 +156,39 @@ static void recv_callback(struct ETCP_CONN* conn, struct ll_entry* entry) { return; } - // Определяем направление по соединению - if (conn == server_conn) { - // Пакет пришел на сервер (forward direction: client->server) - if (verify_packet_data(entry->dgram, entry->len, packets_received_fwd)) { - packets_received_fwd++; - } else { - int seq = entry->dgram[1] | (entry->dgram[2] << 8); - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Server packet verification failed, seq=%d, expected=%d", - seq, packets_received_fwd); - } - } else if (conn == client_conn) { - // Пакет пришел на клиент (backward direction: server->client) - if (verify_packet_data(entry->dgram, entry->len, packets_received_back)) { - packets_received_back++; - } else { - int seq = entry->dgram[1] | (entry->dgram[2] << 8); - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Client packet verification failed, seq=%d, expected=%d", - seq, packets_received_back); + if (verify_packet_data(entry->dgram, entry->len, packets_received_fwd)) { + packets_received_fwd++; + } else { + int seq = entry->dgram[1] | (entry->dgram[2] << 8); + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Server packet verification failed, seq=%d, expected=%d", + seq, packets_received_fwd); + } + + queue_entry_free(entry); + queue_dgram_free(entry); +} + +// Callback для получения пакетов на клиенте (backward direction) +static void client_recv_callback(struct ETCP_CONN* conn, struct ll_entry* entry) { + (void)conn; + + if (!entry || !entry->dgram || entry->len < PACKET_HEADER_SIZE + 1) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Client received invalid packet"); + if (entry) { + queue_entry_free(entry); + queue_dgram_free(entry); } + return; + } + + if (verify_packet_data(entry->dgram, entry->len, packets_received_back)) { + packets_received_back++; } else { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Unknown connection %p", (void*)conn); + int seq = entry->dgram[1] | (entry->dgram[2] << 8); + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Client packet verification failed, seq=%d, expected=%d", + seq, packets_received_back); } - // Освобождаем entry и dgram queue_entry_free(entry); queue_dgram_free(entry); } @@ -456,26 +466,12 @@ int main() { debug_set_categories(DEBUG_CATEGORY_ALL); utun_instance_set_tun_init_enabled(0); - - // Инициализация ETCP API - etcp_api_init(); - - // Регистрируем callback для получения пакетов (один на все соединения) - if (etcp_bind(DATA_PACKET_ID, recv_callback) != 0) { - printf("Failed to bind callback\n"); - etcp_api_deinit(); - return 1; - } - - printf("Callback registered for ID=%d\n", DATA_PACKET_ID); printf("Creating server...\n"); ua = uasync_create(); server_instance = utun_instance_create(ua, "test_pkt_normalizer_etcp_server.conf"); if (!server_instance || init_connections(server_instance) < 0) { printf("Failed to create server\n"); - etcp_unbind(DATA_PACKET_ID); - etcp_api_deinit(); return 1; } printf("Server created, waiting for connection...\n\n"); @@ -484,11 +480,23 @@ int main() { client_instance = utun_instance_create(ua, "test_pkt_normalizer_etcp_client.conf"); if (!client_instance || init_connections(client_instance) < 0) { printf("Failed to create client\n"); - etcp_unbind(DATA_PACKET_ID); - etcp_api_deinit(); return 1; } printf("Client created\n\n"); + + // Регистрируем callbacks для получения пакетов (per-instance) + if (etcp_bind(server_instance, DATA_PACKET_ID, server_recv_callback) != 0) { + printf("Failed to bind server callback\n"); + return 1; + } + + if (etcp_bind(client_instance, DATA_PACKET_ID, client_recv_callback) != 0) { + printf("Failed to bind client callback\n"); + return 1; + } + + printf("Callbacks registered for ID=%d (server=%p, client=%p)\n", + DATA_PACKET_ID, (void*)server_recv_callback, (void*)client_recv_callback); printf("Sending %d packets in each direction via etcp_send...\n", TOTAL_PACKETS); packet_timeout_id = uasync_set_timeout(ua, 1000, NULL, monitor_and_send); @@ -499,16 +507,18 @@ int main() { } printf("\nCleaning up...\n"); - + if (packet_timeout_id) uasync_cancel_timeout(ua, packet_timeout_id); if (global_timeout_id) uasync_cancel_timeout(ua, global_timeout_id); - + // Отписываемся от callbacks - etcp_unbind(DATA_PACKET_ID); - - // Деинициализация ETCP API - etcp_api_deinit(); - + if (server_instance) { + etcp_unbind(server_instance, DATA_PACKET_ID); + } + if (client_instance) { + etcp_unbind(client_instance, DATA_PACKET_ID); + } + if (server_instance) { server_instance->running = 0; utun_instance_destroy(server_instance);