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.
This commit is contained in:
Evgeny committed 2026-02-14 01:39:55 +03:00
1 parent 1a7356e923
commit d694c92b1f
4 files changed
+124 -126

No files matched your search

+42 -56
View File
@@ -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 <string.h>
@@ -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;
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 = 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");
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 (!g_bindings[id].active) {
if (id >= ETCP_MAX_BINDINGS) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: Invalid ID %u", id);
return -1;
}
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);
// Ищем binding для этого ID
if (id < ETCP_MAX_BINDINGS && g_bindings[id].active && g_bindings[id].callback) {
// Получаем 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 в 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);
+20 -22
View File
@@ -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 1 byte> <data ... n bytes>
* cmd = 0 - пакет для передачи адресату
@@ -19,8 +19,11 @@
#include <stdint.h>
#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
+4
View File
@@ -6,6 +6,7 @@
#include <stdio.h>
#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