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.
150 lines
7.8 KiB
150 lines
7.8 KiB
// etcp_router.c — Сервисный слой маршрутизации поверх ETCP |
|
// Маршрутизирует сервисные пакеты до целевой ноды, на промежуточных нодах ретранслирует |
|
#include "etcp_router.h" |
|
#include "etcp.h" |
|
#include "utun_instance.h" |
|
#include "route_bgp.h" |
|
#include "../lib/debug_config.h" |
|
#include "../lib/mem.h" |
|
#include "../lib/ll_queue.h" |
|
#include <string.h> |
|
|
|
// Обработчик ETCP_ID_SVC_ROUTE — вызывается etcp_int_recv на каждом узле |
|
static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) { |
|
if (!conn || !entry) { |
|
if (entry) { queue_entry_free(entry); queue_dgram_free(entry); } |
|
return; |
|
} |
|
struct UTUN_INSTANCE* inst = conn->instance; |
|
if (!inst || entry->len < SVC_ROUTE_HDR_SIZE) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_router: invalid packet inst=%p len=%zu min=%d", |
|
(void*)inst, entry ? entry->len : 0, SVC_ROUTE_HDR_SIZE); |
|
queue_entry_free(entry); queue_dgram_free(entry); |
|
return; |
|
} |
|
uint8_t svc_id = entry->dgram[1]; |
|
uint64_t dst_node_id = 0; memcpy(&dst_node_id, entry->dgram + 2, 8); |
|
if (dst_node_id == inst->node_id) { |
|
if (svc_id >= SVC_ROUTE_MAX_BINDINGS || !inst->router_bindings.callbacks[svc_id]) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_router: no handler for svc_id=%u dst=%016llx self=%016llx", |
|
svc_id, (unsigned long long)dst_node_id, (unsigned long long)inst->node_id); |
|
queue_entry_free(entry); queue_dgram_free(entry); |
|
return; |
|
} |
|
size_t payload_len = entry->len - SVC_ROUTE_HDR_SIZE; |
|
struct ll_entry* svc_entry = queue_entry_new(0); |
|
if (!svc_entry) { queue_entry_free(entry); queue_dgram_free(entry); return; } |
|
svc_entry->len = 1 + payload_len; |
|
svc_entry->dgram = u_malloc(svc_entry->len); |
|
if (!svc_entry->dgram) { queue_entry_free(svc_entry); queue_entry_free(entry); queue_dgram_free(entry); return; } |
|
svc_entry->dgram[0] = svc_id; |
|
if (payload_len > 0) memcpy(svc_entry->dgram + 1, entry->dgram + SVC_ROUTE_HDR_SIZE, payload_len); |
|
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_router: delivering svc_id=%u len=%zu to handler %p src=%016llx", |
|
svc_id, payload_len, (void*)inst->router_bindings.callbacks[svc_id], |
|
(unsigned long long)(*(uint64_t*)(entry->dgram + 10))); |
|
queue_entry_free(entry); queue_dgram_free(entry); |
|
inst->router_bindings.callbacks[svc_id](conn, svc_entry); |
|
} else { |
|
struct ETCP_CONN* next = route_bgp_find_conn_for_node(inst->bgp, dst_node_id); |
|
if (!next) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_router: no route to %016llx svc_id=%u, dropping", |
|
(unsigned long long)dst_node_id, svc_id); |
|
queue_entry_free(entry); queue_dgram_free(entry); |
|
return; |
|
} |
|
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_router: forwarding svc_id=%u → %016llx via %s", |
|
svc_id, (unsigned long long)dst_node_id, next->log_name); |
|
etcp_send(next, entry); |
|
} |
|
} |
|
|
|
// ==================================================================== |
|
// Public API |
|
// ==================================================================== |
|
|
|
int etcp_router_init(struct UTUN_INSTANCE* inst) { |
|
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_router_init: NULL instance"); return -1; } |
|
memset(&inst->router_bindings, 0, sizeof(inst->router_bindings)); |
|
int ret = etcp_bind(inst, ETCP_ID_SVC_ROUTE, etcp_router_recv_cb); |
|
if (ret != 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_router_init: etcp_bind failed, ret=%d", ret); return -1; } |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_router initialized for node %016llx", (unsigned long long)inst->node_id); |
|
return 0; |
|
} |
|
|
|
void etcp_router_destroy(struct UTUN_INSTANCE* inst) { |
|
if (!inst) return; |
|
etcp_unbind(inst, ETCP_ID_SVC_ROUTE); |
|
memset(&inst->router_bindings, 0, sizeof(inst->router_bindings)); |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_router destroyed for node %016llx", (unsigned long long)inst->node_id); |
|
} |
|
|
|
int etcp_router_bind(struct UTUN_INSTANCE* inst, uint8_t svc_id, etcp_recv_fn callback) { |
|
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_router_bind: NULL instance"); return -1; } |
|
if (svc_id >= SVC_ROUTE_MAX_BINDINGS) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_router_bind: invalid svc_id %u", svc_id); return -1; } |
|
if (!callback) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_router_bind: NULL callback for svc_id=%u", svc_id); return -1; } |
|
if (inst->router_bindings.callbacks[svc_id]) DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_router_bind: overwriting svc_id=%u", svc_id); |
|
inst->router_bindings.callbacks[svc_id] = callback; |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_router_bind: svc_id=%u → cb=%p", svc_id, (void*)callback); |
|
return 0; |
|
} |
|
|
|
int etcp_router_unbind(struct UTUN_INSTANCE* inst, uint8_t svc_id) { |
|
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_router_unbind: NULL instance"); return -1; } |
|
if (svc_id >= SVC_ROUTE_MAX_BINDINGS) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_router_unbind: invalid svc_id %u", svc_id); return -1; } |
|
if (!inst->router_bindings.callbacks[svc_id]) { DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_router_unbind: svc_id=%u not bound", svc_id); return -1; } |
|
inst->router_bindings.callbacks[svc_id] = NULL; |
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_router_unbind: svc_id=%u", svc_id); |
|
return 0; |
|
} |
|
|
|
int etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t dst_node_id, struct ll_entry* entry) { |
|
if (!inst || !entry) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_route_send: NULL inst=%p entry=%p", (void*)inst, (void*)entry); |
|
if (entry) { queue_entry_free(entry); queue_dgram_free(entry); } |
|
return -1; |
|
} |
|
if (!entry->dgram || entry->len < 1) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_route_send: empty entry"); |
|
queue_entry_free(entry); queue_dgram_free(entry); |
|
return -1; |
|
} |
|
uint8_t svc_id = entry->dgram[0]; |
|
size_t payload_len = entry->len - 1; |
|
|
|
// Loopback — dispatch прямо локально |
|
if (dst_node_id == inst->node_id) { |
|
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_route_send: loopback svc_id=%u len=%zu", svc_id, payload_len); |
|
if (svc_id < SVC_ROUTE_MAX_BINDINGS && inst->router_bindings.callbacks[svc_id]) |
|
inst->router_bindings.callbacks[svc_id](NULL, entry); |
|
else { queue_entry_free(entry); queue_dgram_free(entry); } |
|
return 0; |
|
} |
|
|
|
// Упаковываем в routing header |
|
size_t total_len = SVC_ROUTE_HDR_SIZE + payload_len; |
|
uint8_t* new_dgram = u_malloc(total_len); |
|
if (!new_dgram) { queue_entry_free(entry); queue_dgram_free(entry); return -1; } |
|
new_dgram[0] = ETCP_ID_SVC_ROUTE; |
|
new_dgram[1] = svc_id; |
|
memcpy(new_dgram + 2, &dst_node_id, 8); |
|
memcpy(new_dgram + 10, &inst->node_id, 8); |
|
if (payload_len > 0) memcpy(new_dgram + SVC_ROUTE_HDR_SIZE, entry->dgram + 1, payload_len); |
|
|
|
struct ll_entry* new_entry = queue_entry_new(0); |
|
if (!new_entry) { u_free(new_dgram); queue_entry_free(entry); queue_dgram_free(entry); return -1; } |
|
new_entry->dgram = new_dgram; |
|
new_entry->len = total_len; |
|
|
|
queue_entry_free(entry); queue_dgram_free(entry); |
|
|
|
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, dst_node_id); |
|
if (!conn) { |
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_route_send: no route to %016llx svc_id=%u, dropping", |
|
(unsigned long long)dst_node_id, svc_id); |
|
queue_entry_free(new_entry); queue_dgram_free(new_entry); |
|
return -1; |
|
} |
|
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_route_send: svc_id=%u → %016llx via %s len=%zu", |
|
svc_id, (unsigned long long)dst_node_id, conn->log_name, total_len); |
|
return etcp_send(conn, new_entry); |
|
}
|
|
|