// 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 // Обработчик 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); }