Add etcp_api.c/h - ETCP packet send/receive API

Implemented:
- etcp_send(conn, entry) - отправляет пакет в очередь normalizer
- etcp_bind(id, callback) - подписка на пакеты с определенным ID
- etcp_unbind(id) - отписка от пакетов
- etcp_recv(queue, arg) - коллбэк для маршрутизации пакетов по ID
- etcp_api_init/deinit - инициализация API

Integration:
- pn_init() теперь устанавливает etcp_recv как callback для pn->output
- Добавлены etcp_api.c/h в src/Makefile.am
- Добавлен etcp_api.o в тестовые зависимости

API использует первый байт кодограммы (cmd) как ID для маршрутизации.
ID=0 используется как default handler если нет специфичного binding.
This commit is contained in:
Evgeny committed 2026-02-13 19:15:16 +03:00
1 parent 5dfb67a4e3
commit ffaf7a25b8
11 files changed
+282 -7

No files matched your search

+2 -1
View File
@@ -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 = \
+179
View File
@@ -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 <string.h>
#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);
}
+93 -3
View File
@@ -1,5 +1,95 @@
/* api для приёма-передачи пакетов через etcp
/**
* @file etcp_api.h
* @brief API для приёма-передачи пакетов через ETCP
*
* Основные функции:
* - etcp_send() - отправить пакет в очередь normalizer
* - etcp_bind() - подписаться на пакеты с определенным ID
* - etcp_recv() - коллбэк для сбора пакетов из всех подключений
*
* Формат кодограмм: <cmd 1 byte> <data ... n bytes>
* cmd = 0 - пакет для передачи адресату
* cmd = 1 - элемент роутинг-таблицы
* cmd = 2 - запрос роутинг-таблицы
*/
1.
#ifndef ETCP_API_H
#define ETCP_API_H
*/
#include <stdint.h>
#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
+5 -1
View File
@@ -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;