// 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 "utun_test_hooks.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" #include #include #include #include #include #include // Forward declarations 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 with extended flags struct UTUN_INSTANCE* utun_instance_create_ex(struct UASYNC* ua, const char *config_file, const char *log_file, uint32_t flags) { 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; // Note: Global debug system is initialized from command line arguments if provided // If not initialized via command line, instance-specific logging can be set up here // The first initialization wins - either global (from main) or instance-specific // 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 only if not using global debug system output if (log_file && !g_debug_config.output_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); // Conditional TUN initialization based on flags if (!(flags & UTUN_CREATE_NO_TUN)) { int tun_result; // Use test hook if available and in test mode if ((flags & UTUN_CREATE_TEST_MODE) && g_utun_test_hooks && g_utun_test_hooks->tun_create_override) { tun_result = g_utun_test_hooks->tun_create_override(&instance->tun); DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN creation using test hook: result=%d", tun_result); } else { tun_result = tun_create(&instance->tun); } if (tun_result < 0) { if (flags & UTUN_CREATE_ALLOW_TUN_FAILURE) { DEBUG_WARN(DEBUG_CATEGORY_TUN, "TUN creation failed, continuing without TUN (ALLOW_TUN_FAILURE flag)"); instance->tun.fd = -1; // Mark as disabled } else { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create TUN device"); free(instance); return NULL; } } else { // TUN created successfully, configure it // Configure TUN device IP (BSD: use config IP as-is, Linux/Win: IP/32) char tun_ip_str[64]; char ip_buffer[64]; // Convert struct IP to string using inet_ntop if (instance->config->global.tun_ip.family == AF_INET) { inet_ntop(AF_INET, &instance->config->global.tun_ip.addr.v4, ip_buffer, sizeof(ip_buffer)); } else { inet_ntop(AF_INET6, &instance->config->global.tun_ip.addr.v6, ip_buffer, sizeof(ip_buffer)); } #ifdef __linux__ snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) // BSD systems: use config IP as-is, peer IP will be 192.0.2.1 snprintf(tun_ip_str, sizeof(tun_ip_str), "%s", ip_buffer); #else snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); // Default to /32 #endif if (tun_set_ip(instance->tun.ifname, tun_ip_str) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to set TUN IP: %s", tun_ip_str); tun_close(&instance->tun); free(instance); return NULL; } // Set MTU (default 1500) int mtu = instance->config->global.mtu > 0 ? instance->config->global.mtu : 1500; if (tun_set_mtu(instance->tun.ifname, mtu) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to set TUN MTU: %d", mtu); tun_close(&instance->tun); free(instance); return NULL; } if (tun_set_up(instance->tun.ifname) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to bring up TUN interface"); tun_close(&instance->tun); free(instance); return NULL; } DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface initialized: %s with IP %s", instance->tun.ifname, tun_ip_str); } } else { DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN initialization skipped (NO_TUN flag)"); instance->tun.fd = -1; // Explicitly disabled } // Create routing table instance->routing_table = routing_table_create(); if (!instance->routing_table) { DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to create routing table"); tun_close(&instance->tun); free(instance); return NULL; } // 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; DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Starting cleanup for instance %p", instance); // Диагностика ресурсов ДО cleanup utun_instance_diagnose_leaks(instance, "BEFORE_CLEANUP"); // Stop running if not already instance->running = 0; // Unregister all sockets from uasync BEFORE destroying ETCP components DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Unregistering sockets from uasync"); utun_instance_unregister_sockets(instance); DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Socket unregistration complete"); // 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_TUN, "Closing TUN interface: %s", instance->tun.ifname); 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; // Register TUN socket with uasync if (instance->tun.fd >= 0) { instance->tun_socket_id = uasync_add_socket(instance->ua, instance->tun.fd, tun_read_callback, NULL, NULL, instance); if (!instance->tun_socket_id) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to register TUN socket with uasync"); return -1; } DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface registered: %s (fd=%d)", instance->tun.ifname, instance->tun.fd); } // 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) { DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "[DIAGNOSE] NULL instance for phase: %s", 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"); }