You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
5.8 KiB
165 lines
5.8 KiB
/** |
|
* @file etcp_api.c |
|
* @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> |
|
|
|
#define DEBUG_CATEGORY_ETCP_API 1 |
|
|
|
#define ETCP_MAX_BINDINGS 256 /**< Максимальное количество bindings (по одному на каждый ID 0-255) */ |
|
|
|
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; |
|
} |
|
|
|
if (id >= ETCP_MAX_BINDINGS) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_bind: Invalid ID %u (max=%d)", id, ETCP_MAX_BINDINGS); |
|
return -1; |
|
} |
|
|
|
if (!callback) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_bind: NULL callback for id=%u", id); |
|
return -1; |
|
} |
|
|
|
if (inst->api_bindings.callbacks[id] != NULL) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_bind: ID %u already bound, overwriting", id); |
|
} |
|
|
|
inst->api_bindings.callbacks[id] = 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(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 (inst->api_bindings.callbacks[id] == NULL) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: ID %u not bound", id); |
|
return -1; |
|
} |
|
|
|
inst->api_bindings.callbacks[id] = NULL; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP_API, "etcp_unbind: Unbound ID %u for instance %p", id, (void*)inst); |
|
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 to [%s]", conn->log_name); |
|
return 0; |
|
} |
|
|
|
void etcp_int_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 [%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 в 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*)inst->api_bindings.callbacks[id]); |
|
inst->api_bindings.callbacks[id](conn, entry); |
|
} else { |
|
// Нет binding для этого ID - по умолчанию считаем что это данные (ID=0) |
|
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); |
|
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); |
|
queue_entry_free(entry); |
|
queue_dgram_free(entry); |
|
} |
|
} |
|
|
|
// Возобновляем коллбэк для получения следующего пакета |
|
queue_resume_callback(queue); |
|
}
|
|
|