|
|
// utun_instance.c - Root instance implementation |
|
|
#include "utun_instance.h" |
|
|
#include "config_parser.h" |
|
|
#include "config_updater.h" |
|
|
#include "tun_if.h" |
|
|
#include "routing.h" |
|
|
#include "etcp_connections.h" |
|
|
#include "etcp.h" |
|
|
#include "../lib/u_async.h" |
|
|
#include "../lib/debug_config.h" |
|
|
#include <stdlib.h> |
|
|
#include <stdio.h> |
|
|
#include <string.h> |
|
|
#include <errno.h> |
|
|
#include <unistd.h> |
|
|
#include <arpa/inet.h> |
|
|
|
|
|
// Forward declarations |
|
|
static void tun_read_callback(int fd, void* user_arg); |
|
|
static uint32_t get_dest_ip(const uint8_t *packet, size_t len); |
|
|
|
|
|
// Global instance for signal handlers |
|
|
static struct UTUN_INSTANCE *g_instance = NULL; |
|
|
|
|
|
// Create and initialize root instance |
|
|
struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char *config_file, const char *log_file) { |
|
|
struct UTUN_INSTANCE *instance = calloc(1, sizeof(struct UTUN_INSTANCE)); |
|
|
if (!instance) return NULL; |
|
|
|
|
|
// Initialize basic fields |
|
|
instance->running = 0; |
|
|
instance->log_fp = NULL; |
|
|
instance->ua = ua; |
|
|
|
|
|
// Ensure keys and node_id exist in config |
|
|
if (config_ensure_keys_and_node_id(config_file) < 0) { |
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to ensure keys and node_id in config: %s", config_file); |
|
|
free(instance); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
// Load configuration |
|
|
instance->config = parse_config(config_file); |
|
|
if (!instance->config) { |
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to load config from %s", config_file); |
|
|
free(instance); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
// Open log file |
|
|
if (log_file) { |
|
|
instance->log_fp = fopen(log_file, "a"); |
|
|
if (!instance->log_fp) { |
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to open log file %s: %s", log_file, strerror(errno)); |
|
|
} |
|
|
} |
|
|
// Set node_id from config |
|
|
instance->node_id = instance->config->global.my_node_id; |
|
|
|
|
|
// Set my keys |
|
|
if (sc_init_local_keys(&instance->my_keys, instance->config->global.my_public_key_hex, instance->config->global.my_private_key_hex)) { |
|
|
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to initialize local keys"); |
|
|
} |
|
|
|
|
|
|
|
|
instance->pkt_pool=memory_pool_init(PACKET_DATA_SIZE+100); |
|
|
/* |
|
|
// Initialize TUN device |
|
|
if (tun_create(&instance->tun) < 0) { |
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create TUN device"); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
// Configure TUN device |
|
|
if (tun_set_ip(instance->tun.ifname, instance->config->global.tun_ip) < 0) { |
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to set TUN IP"); |
|
|
tun_close(&instance->tun); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
if (instance->config->global.mtu > 0) { |
|
|
if (tun_set_mtu(instance->tun.ifname, instance->config->global.mtu) < 0) { |
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to set TUN MTU"); |
|
|
tun_close(&instance->tun); |
|
|
return -1; |
|
|
} |
|
|
} |
|
|
|
|
|
if (tun_set_up(instance->tun.ifname) < 0) { |
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to bring up TUN interface"); |
|
|
tun_close(&instance->tun); |
|
|
return -1; |
|
|
} |
|
|
|
|
|
// Create routing table |
|
|
instance->routing_table = routing_table_create(); |
|
|
if (!instance->routing_table) { |
|
|
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to create routing table"); |
|
|
return -1; |
|
|
} |
|
|
*/ |
|
|
// Initialize connections from configuration - moved to utun_instance_init |
|
|
// to avoid double initialization |
|
|
/* |
|
|
if (init_connections(instance) < 0) { |
|
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize connections"); |
|
|
// Cleanup will be handled by utun_instance_destroy |
|
|
return NULL; |
|
|
} |
|
|
*/ |
|
|
|
|
|
return instance; |
|
|
} |
|
|
|
|
|
// Destroy instance and cleanup resources |
|
|
void utun_instance_destroy(struct UTUN_INSTANCE *instance) { |
|
|
if (!instance) return; |
|
|
|
|
|
printf("[INSTANCE_DESTROY] Starting cleanup for instance %p\n", instance); |
|
|
|
|
|
// Диагностика ресурсов ДО cleanup |
|
|
utun_instance_diagnose_leaks(instance, "BEFORE_CLEANUP"); |
|
|
|
|
|
// Stop running if not already |
|
|
instance->running = 0; |
|
|
|
|
|
// Cleanup ETCP sockets and connections FIRST (before destroying uasync) |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Cleaning up ETCP sockets and connections"); |
|
|
struct ETCP_SOCKET* sock = instance->etcp_sockets; |
|
|
while (sock) { |
|
|
struct ETCP_SOCKET* next = sock->next; |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Removing socket %p, fd=%d", sock, sock->fd); |
|
|
etcp_socket_remove(sock); // Полный cleanup сокета |
|
|
sock = next; |
|
|
} |
|
|
instance->etcp_sockets = NULL; |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP sockets cleanup complete"); |
|
|
|
|
|
struct ETCP_CONN* conn = instance->connections; |
|
|
while (conn) { |
|
|
struct ETCP_CONN* next = conn->next; |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Closing connection %p", conn); |
|
|
etcp_connection_close(conn); // Закрыть соединение |
|
|
conn = next; |
|
|
} |
|
|
instance->connections = NULL; |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP connections cleanup complete"); |
|
|
|
|
|
// Cleanup other components |
|
|
if (instance->routing_table) { |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Destroying routing table"); |
|
|
routing_table_destroy(instance->routing_table); |
|
|
instance->routing_table = NULL; |
|
|
} |
|
|
|
|
|
// Cleanup TUN |
|
|
if (instance->tun.fd >= 0) { |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Closing TUN interface"); |
|
|
tun_close(&instance->tun); |
|
|
} |
|
|
|
|
|
// Cleanup config |
|
|
if (instance->config) { |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Freeing configuration"); |
|
|
free_config(instance->config); |
|
|
instance->config = NULL; |
|
|
} |
|
|
|
|
|
// Close log file |
|
|
if (instance->log_fp) { |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Closing log file"); |
|
|
fclose(instance->log_fp); |
|
|
instance->log_fp = NULL; |
|
|
} |
|
|
|
|
|
// Cleanup packet pool (ensure no leak if stop wasn't called) |
|
|
if (instance->pkt_pool) { |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Destroying packet pool"); |
|
|
memory_pool_destroy(instance->pkt_pool); |
|
|
instance->pkt_pool = NULL; |
|
|
} |
|
|
|
|
|
// FINALLY destroy uasync (after all resources are cleaned up) |
|
|
if (instance->ua) { |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Destroying uasync instance"); |
|
|
uasync_destroy(instance->ua); |
|
|
instance->ua = NULL; |
|
|
} |
|
|
|
|
|
// Clear global instance |
|
|
if (g_instance == instance) { |
|
|
g_instance = NULL; |
|
|
} |
|
|
|
|
|
// Free the instance memory |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Freeing instance memory"); |
|
|
free(instance); |
|
|
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Instance destroyed completely"); |
|
|
} |
|
|
|
|
|
// Stop instance |
|
|
void utun_instance_stop(struct UTUN_INSTANCE *instance) { |
|
|
if (!instance) return; |
|
|
instance->running = 0; |
|
|
// Wakeup main loop using built-in uasync wakeup |
|
|
if (instance->ua) { |
|
|
memory_pool_destroy(instance->pkt_pool); |
|
|
uasync_wakeup(instance->ua); |
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
int utun_instance_init(struct UTUN_INSTANCE *instance) { |
|
|
if (!instance) return -1; |
|
|
|
|
|
// Initialize connections |
|
|
if (init_connections(instance) < 0) { |
|
|
return -1; |
|
|
} |
|
|
|
|
|
return 0; |
|
|
} |
|
|
|
|
|
// Диагностическая функция для анализа утечек |
|
|
void utun_instance_diagnose_leaks(struct UTUN_INSTANCE *instance, const char *phase) { |
|
|
if (!instance) { |
|
|
printf("[DIAGNOSE] NULL instance for phase: %s\n", phase); |
|
|
return; |
|
|
} |
|
|
|
|
|
struct { |
|
|
int etcp_sockets_count; |
|
|
int etcp_connections_count; |
|
|
int etcp_links_count; |
|
|
} report = {0}; |
|
|
|
|
|
// Подсчёт ETCP сокетов |
|
|
struct ETCP_SOCKET *sock = instance->etcp_sockets; |
|
|
while (sock) { |
|
|
report.etcp_sockets_count++; |
|
|
// Подсчёт линков в каждом сокете |
|
|
for (size_t i = 0; i < sock->num_channels; i++) { |
|
|
if (sock->links[i]) { |
|
|
report.etcp_links_count++; |
|
|
} |
|
|
} |
|
|
sock = sock->next; |
|
|
} |
|
|
|
|
|
// Подсчёт ETCP соединений |
|
|
struct ETCP_CONN *conn = instance->connections; |
|
|
while (conn) { |
|
|
report.etcp_connections_count++; |
|
|
// Подсчёт линков в соединениях |
|
|
struct ETCP_LINK *link = conn->links; |
|
|
while (link) { |
|
|
report.etcp_links_count++; |
|
|
link = link->next; |
|
|
} |
|
|
conn = conn->next; |
|
|
} |
|
|
|
|
|
printf("\n🔍 [UTUN_INSTANCE LEAK DIAGNOSIS] Phase: %s\n", phase); |
|
|
printf(" Instance: %p\n", instance); |
|
|
printf(" Node ID: %llu\n", (unsigned long long)instance->node_id); |
|
|
printf(" UA instance: %p\n", instance->ua); |
|
|
printf(" Running: %d\n", instance->running); |
|
|
|
|
|
printf("\n📊 STRUCTURE COUNTS:\n"); |
|
|
printf(" ETCP Sockets: %d active\n", report.etcp_sockets_count); |
|
|
printf(" ETCP Connections: %d active\n", report.etcp_connections_count); |
|
|
printf(" ETCP Links: %d total\n", report.etcp_links_count); |
|
|
|
|
|
printf("\n🔧 RESOURCE STATUS:\n"); |
|
|
printf(" Memory Pool: %s\n", instance->pkt_pool ? "ALLOCATED" : "NULL"); |
|
|
printf(" TUN Socket ID: %p\n", instance->tun_socket_id); |
|
|
printf(" TUN FD: %d\n", instance->tun.fd); |
|
|
printf(" Connections list: %p\n", instance->connections); |
|
|
printf(" ETCP Sockets list: %p\n", instance->etcp_sockets); |
|
|
|
|
|
printf("\n⚠️ POTENTIAL LEAKS:\n"); |
|
|
if (instance->pkt_pool) { |
|
|
printf(" ❌ Memory Pool not freed\n"); |
|
|
} |
|
|
if (instance->tun_socket_id) { |
|
|
printf(" ❌ TUN socket not unregistered from uasync\n"); |
|
|
} |
|
|
if (report.etcp_sockets_count > 0) { |
|
|
printf(" ❌ %d ETCP sockets still allocated\n", report.etcp_sockets_count); |
|
|
} |
|
|
if (report.etcp_connections_count > 0) { |
|
|
printf(" ❌ %d ETCP connections still allocated\n", report.etcp_connections_count); |
|
|
} |
|
|
if (report.etcp_links_count > 0) { |
|
|
printf(" ❌ %d ETCP links still allocated\n", report.etcp_links_count); |
|
|
} |
|
|
|
|
|
printf("\n📋 RECOMMENDATIONS:\n"); |
|
|
if (instance->pkt_pool) { |
|
|
printf(" → Call memory_pool_destroy() before freeing instance\n"); |
|
|
} |
|
|
if (instance->tun_socket_id) { |
|
|
printf(" → Call uasync_remove_socket() for TUN socket\n"); |
|
|
} |
|
|
if (report.etcp_sockets_count > 0) { |
|
|
printf(" → Iterate and call etcp_socket_remove() for each socket\n"); |
|
|
} |
|
|
if (report.etcp_connections_count > 0) { |
|
|
printf(" → Iterate and call etcp_connection_close() for each connection\n"); |
|
|
} |
|
|
|
|
|
printf("\n"); |
|
|
}
|
|
|
|