/** * @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 #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); }