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.
204 lines
8.0 KiB
204 lines
8.0 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> |
|
#include "route_lib.h" |
|
#include "route_bgp.h" |
|
|
|
#define DEBUG_CATEGORY_ETCP_API 1 |
|
|
|
#define ETCP_MAX_BINDINGS 256 /**< Максимальное количество bindings (по одному на каждый ID 0-255) */ |
|
|
|
// после создания подключения надо выждать conn_ready. только после этого можно передавать сообщения. Иначе сообщения могут потеряться. |
|
// conn_ready может вызваться несколько раз (после каждого переподключения - например когда удаленный узел перегрузился или возникла неустранимая ошибка). |
|
void etcp_conn_set_ready_cbk(struct ETCP_CONN* etcp, etcp_cbk_fn callback_fn, void* arg) { |
|
if (!etcp) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "NULL conn"); return; } |
|
etcp->ready_cbk = callback_fn; |
|
etcp->ready_arg = arg; |
|
DEBUG_TRACE(DEBUG_CATEGORY_ETCP_API, "conn=%s cbk=%p arg=%p", etcp->log_name, (void*)callback_fn, arg); |
|
} |
|
|
|
void etcp_conn_set_up_cbk(struct ETCP_CONN* etcp, etcp_cbk_fn callback_fn, void* arg) { |
|
if (!etcp) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "NULL conn"); return; } |
|
etcp->up_cbk = callback_fn; |
|
etcp->up_arg = arg; |
|
DEBUG_TRACE(DEBUG_CATEGORY_ETCP_API, "conn=%s cbk=%p arg=%p", etcp->log_name, (void*)callback_fn, arg); |
|
} |
|
|
|
void etcp_conn_set_down_cbk(struct ETCP_CONN* etcp, etcp_cbk_fn callback_fn, void* arg) { |
|
if (!etcp) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "NULL conn"); return; } |
|
etcp->down_cbk = callback_fn; |
|
etcp->down_arg = arg; |
|
DEBUG_TRACE(DEBUG_CATEGORY_ETCP_API, "conn=%s cbk=%p arg=%p", etcp->log_name, (void*)callback_fn, arg); |
|
} |
|
|
|
// Установить callback при создании нового ETCP соединения |
|
void etcp_set_new_conn_cbk(struct UTUN_INSTANCE* instance, etcp_cbk_fn callback_fn, void* arg) { |
|
if (!instance) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_API, "etcp_set_new_conn_cbk: NULL instance"); |
|
return; |
|
} |
|
instance->etcp_new_conn_cbk = callback_fn; |
|
instance->etcp_new_conn_arg = arg; |
|
DEBUG_TRACE(DEBUG_CATEGORY_ETCP_API, "etcp_set_new_conn_cbk: instance=%p cbk=%p arg=%p", |
|
(void*)instance, (void*)callback_fn, arg); |
|
} |
|
|
|
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 |
|
// DEBUG_TRACE(DEBUG_CATEGORY_NORMALIZER, "Before put to input"); |
|
int result = queue_data_put(pn->input, entry, 0); |
|
DEBUG_TRACE(DEBUG_CATEGORY_NORMALIZER, "After put to input"); |
|
|
|
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); |
|
}
|
|
|