Browse Source

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.
nodeinfo-routing-update
Evgeny 8 months ago
parent
commit
d694c92b1f
  1. 98
      src/etcp_api.c
  2. 42
      src/etcp_api.h
  3. 4
      src/utun_instance.h
  4. 76
      tests/test_etcp_api.c

98
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 <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;
}
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(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;
}
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);

42
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 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
src/utun_instance.h

@ -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

76
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,9 +156,6 @@ 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 {
@ -165,8 +163,24 @@ static void recv_callback(struct ETCP_CONN* conn, struct ll_entry* entry) {
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)
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 {
@ -174,11 +188,7 @@ static void recv_callback(struct ETCP_CONN* conn, struct ll_entry* entry) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Client packet verification failed, seq=%d, expected=%d",
seq, packets_received_back);
}
} else {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Unknown connection %p", (void*)conn);
}
// Освобождаем entry и dgram
queue_entry_free(entry);
queue_dgram_free(entry);
}
@ -457,25 +467,11 @@ int main() {
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,12 +480,24 @@ 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);
void* global_timeout_id = uasync_set_timeout(ua, TEST_TIMEOUT_MS*10, NULL, test_timeout);
@ -504,10 +512,12 @@ int main() {
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;

Loading…
Cancel
Save