diff --git a/src/Makefile.am b/src/Makefile.am index a519e253..7c055510 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -14,7 +14,8 @@ utun_CORE_SOURCES = \ etcp_loadbalancer.c \ secure_channel.c \ crc32.c \ - pkt_normalizer.c + pkt_normalizer.c \ + etcp_api.c # TinyCrypt sources (only used without OpenSSL) utun_TINYCRYPT_SOURCES = \ diff --git a/src/etcp_api.c b/src/etcp_api.c new file mode 100644 index 00000000..61b23f65 --- /dev/null +++ b/src/etcp_api.c @@ -0,0 +1,179 @@ +/** + * @file etcp_api.c + * @brief Реализация API для приёма-передачи пакетов через ETCP + */ + +#include "etcp_api.h" +#include "etcp.h" +#include "pkt_normalizer.h" +#include "../lib/debug_config.h" +#include + +#define DEBUG_CATEGORY_ETCP_API 1 + +#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; + } + + 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"); + return -1; + } + + if (!callback) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_bind: NULL callback for id=%u", id); + return -1; + } + + if (g_bindings[id].active) { + DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_bind: ID %u already bound, overwriting", id); + } + + g_bindings[id].callback = callback; + g_bindings[id].active = 1; + + DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_bind: Bound ID %u to callback %p", id, (void*)callback); + return 0; +} + +int etcp_unbind(uint8_t id) { + if (!g_api_initialized) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: API not initialized"); + return -1; + } + + if (!g_bindings[id].active) { + 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; + + DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: Unbound ID %u", id); + return 0; +} + +int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry) { + if (!conn) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: NULL connection"); + return -1; + } + + if (!entry) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: NULL entry"); + return -1; + } + + if (!conn->normalizer) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: Connection has no normalizer"); + return -1; + } + + struct PKTNORM* pn = conn->normalizer; + + if (!pn->input) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_send: Normalizer has no input queue"); + return -1; + } + + // Помещаем entry в очередь input normalizer + // queue_data_put забирает ownership entry + int result = queue_data_put(pn->input, entry, 0); + + if (result != 0) { + DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_send: queue_data_put failed (queue full?)"); + return -1; + } + + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP_API, "etcp_send: Packet queued for sending"); + return 0; +} + +void etcp_recv(struct ll_queue* queue, void* arg) { + struct ETCP_CONN* conn = (struct ETCP_CONN*)arg; + + if (!queue || !conn) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Invalid arguments (queue=%p, conn=%p)", + (void*)queue, (void*)conn); + return; + } + + // Получаем элемент из очереди + struct ll_entry* entry = queue_data_get(queue); + if (!entry) { + // Очередь пуста - это нормально, просто возобновляем коллбэк + queue_resume_callback(queue); + return; + } + + // Проверяем что есть данные + if (!entry->dgram || entry->len == 0) { + DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_recv: Empty packet received"); + queue_entry_free(entry); + queue_dgram_free(entry); + queue_resume_callback(queue); + return; + } + + // Получаем 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); + + // Ищем binding для этого ID + if (id < ETCP_MAX_BINDINGS && g_bindings[id].active && g_bindings[id].callback) { + // Вызываем коллбэк + 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); + } else { + // Нет binding для этого ID - по умолчанию считаем что это данные (ID=0) + if (g_bindings[0].active && g_bindings[0].callback) { + 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); + } else { + // Нет default binding - освобождаем пакет + DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_recv: No binding for ID %u and no default handler, dropping packet", id); + queue_entry_free(entry); + queue_dgram_free(entry); + } + } + + // Возобновляем коллбэк для получения следующего пакета + queue_resume_callback(queue); +} diff --git a/src/etcp_api.h b/src/etcp_api.h index 0c671c0c..2b285663 100644 --- a/src/etcp_api.h +++ b/src/etcp_api.h @@ -1,5 +1,95 @@ -/* api для приёма-передачи пакетов через etcp +/** + * @file etcp_api.h + * @brief API для приёма-передачи пакетов через ETCP + * + * Основные функции: + * - etcp_send() - отправить пакет в очередь normalizer + * - etcp_bind() - подписаться на пакеты с определенным ID + * - etcp_recv() - коллбэк для сбора пакетов из всех подключений + * + * Формат кодограмм: + * cmd = 0 - пакет для передачи адресату + * cmd = 1 - элемент роутинг-таблицы + * cmd = 2 - запрос роутинг-таблицы + */ -1. +#ifndef ETCP_API_H +#define ETCP_API_H -*/ \ No newline at end of file +#include +#include "../lib/ll_queue.h" + +// Forward declarations +struct ETCP_CONN; + +/** + * @brief Тип коллбэка для приёма пакетов + * @param conn ETCP соединение от которого получен пакет + * @param entry Элемент очереди с данными пакета + * + * @note Коллбэк должен освободить entry через queue_entry_free() + * и dgram через queue_dgram_free() после обработки + */ +typedef void (*etcp_recv_fn)(struct ETCP_CONN* conn, struct ll_entry* entry); + +/** + * @brief Отправить пакет в очередь normalizer + * + * @param conn ETCP соединение + * @param entry Элемент очереди с данными для отправки + * @return 0 при успехе, -1 при ошибке + * + * @note Функция забирает ownership entry - вызывающий код не должен + * освобождать entry после вызова + */ +int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry); + +/** + * @brief Подписаться на все пакеты с указанным ID + * + * @param id Идентификатор пакета (первый байт кодограммы, cmd) + * @param callback Коллбэк для обработки пакетов с этим ID + * @return 0 при успехе, -1 при ошибке (например, нет свободных слотов) + * + * @note ID=0 зарезервирован для обычных пакетов данных + * @note Можно зарегистрировать только один коллбэк на каждый ID + */ +int etcp_bind(uint8_t id, etcp_recv_fn callback); + +/** + * @brief Отписаться от пакетов с указанным ID + * + * @param id Идентификатор пакета + * @return 0 при успехе, -1 если binding не найден + */ +int etcp_unbind(uint8_t id); + +/** + * @brief Коллбэк для очередей output ll_queue normalizer + * + * Собирает пакеты из всех подключений и отправляет в bind->cbk + * по ID (первый байт кодограммы). + * + * @param queue Очередь из которой получен пакет + * @param arg Аргумент - указатель на ETCP_CONN + * + * @note Этот коллбэк устанавливается автоматически при создании + * ETCP подключения через pn_init() + */ +void etcp_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/pkt_normalizer.c b/src/pkt_normalizer.c index 471d35c9..239209dd 100644 --- a/src/pkt_normalizer.c +++ b/src/pkt_normalizer.c @@ -1,6 +1,7 @@ // pkt_normalizer.c - Implementation of packet normalizer for ETCP #include "pkt_normalizer.h" #include "etcp.h" // For ETCP_CONN and related structures +#include "etcp_api.h" // For etcp_recv callback #include "routing.h" // For routing_add_conn/routing_del_conn #include "utun_instance.h" // For UTUN_INSTANCE #include "ll_queue.h" // For queue operations @@ -47,7 +48,10 @@ struct PKTNORM* pn_init(struct ETCP_CONN* etcp) { } queue_set_callback(pn->input, packer_cb, pn); - queue_set_callback(etcp->output_queue, pn_unpacker_cb, pn); + + // Setup etcp_recv callback for output queue - handles all received packets + // according to etcp_api protocol (routes by first byte ID) + queue_set_callback(pn->output, etcp_recv, etcp); pn->data = NULL; pn->recvpart = NULL; diff --git a/tests/Makefile.am b/tests/Makefile.am index bb270b75..d0aa41ee 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -55,7 +55,8 @@ ETCP_CORE_OBJS = \ $(SRC_OBJDIR)/utun-etcp.o \ $(SRC_OBJDIR)/utun-etcp_connections.o \ $(SRC_OBJDIR)/utun-etcp_loadbalancer.o \ - $(SRC_OBJDIR)/utun-pkt_normalizer.o + $(SRC_OBJDIR)/utun-pkt_normalizer.o \ + $(SRC_OBJDIR)/utun-etcp_api.o # Full ETCP objects ETCP_FULL_OBJS = \ @@ -139,7 +140,7 @@ test_pkt_normalizer_etcp_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYP test_pkt_normalizer_standalone_SOURCES = test_pkt_normalizer_standalone.c test_pkt_normalizer_standalone_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source -test_pkt_normalizer_standalone_LDADD = $(SRC_OBJDIR)/utun-pkt_normalizer.o $(SRC_OBJDIR)/utun-route_lib.o $(SRC_OBJDIR)/utun-routing.o $(TINYCRYPT_OBJS) $(COMMON_LIBS) +test_pkt_normalizer_standalone_LDADD = $(SRC_OBJDIR)/utun-pkt_normalizer.o $(SRC_OBJDIR)/utun-route_lib.o $(SRC_OBJDIR)/utun-routing.o $(SRC_OBJDIR)/utun-etcp_api.o $(TINYCRYPT_OBJS) $(COMMON_LIBS) test_ll_queue_SOURCES = test_ll_queue.c test_ll_queue_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib diff --git a/tests/test_etcp_100_packets b/tests/test_etcp_100_packets index 72a68281..97e05091 100755 Binary files a/tests/test_etcp_100_packets and b/tests/test_etcp_100_packets differ diff --git a/tests/test_etcp_minimal b/tests/test_etcp_minimal index 57b18794..f9061340 100755 Binary files a/tests/test_etcp_minimal and b/tests/test_etcp_minimal differ diff --git a/tests/test_etcp_simple_traffic b/tests/test_etcp_simple_traffic index aff92cba..de83a0a6 100755 Binary files a/tests/test_etcp_simple_traffic and b/tests/test_etcp_simple_traffic differ diff --git a/tests/test_etcp_two_instances b/tests/test_etcp_two_instances index 28dc72af..96175c6e 100755 Binary files a/tests/test_etcp_two_instances and b/tests/test_etcp_two_instances differ diff --git a/tests/test_pkt_normalizer_etcp b/tests/test_pkt_normalizer_etcp index 520198f3..a8c06aa0 100755 Binary files a/tests/test_pkt_normalizer_etcp and b/tests/test_pkt_normalizer_etcp differ diff --git a/tests/test_pkt_normalizer_standalone b/tests/test_pkt_normalizer_standalone index 3cfb5661..f09ac338 100755 Binary files a/tests/test_pkt_normalizer_standalone and b/tests/test_pkt_normalizer_standalone differ