// utun_instance.c - Root instance implementation #include "utun_instance.h" #include "config_parser.h" #include "config_updater.h" #include "tun_if.h" #include "tun_route.h" #include "route_node.h" #include "route_lib.h" #include "routing.h" #include "route_bgp.h" #include "etcp_connections.h" #include "etcp.h" #include "control_server.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" #include #include #include #include #include #include "../lib/platform_compat.h" #include "../lib/mem.h" // 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; // Global flag to control TUN initialization (disabled by default) static int g_tun_init_enabled = 0; // Function to control TUN initialization void utun_instance_set_tun_init_enabled(int enabled) { g_tun_init_enabled = enabled ? 1 : 0; DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN initialization %s", enabled ? "enabled" : "disabled"); } // Common initialization function (called by both create functions) // Returns 0 on success, -1 on error (instance is NOT freed on error - caller must handle) static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* ua, struct utun_config* config) { // Initialize basic fields instance->running = 0; instance->ua = ua; instance->config = config; // Set name from config if (config->global.name[0] != '\0') { strncpy(instance->name, config->global.name, sizeof(instance->name) - 1); instance->name[sizeof(instance->name) - 1] = '\0'; } else { instance->name[0] = '\0'; } // Set node_id from config instance->node_id = config->global.my_node_id; // Set my keys if (sc_init_local_keys(&instance->my_keys, config->global.my_public_key_hex, config->global.my_private_key_hex) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to initialize local keys"); return -1; } // Create memory pools instance->ack_pool = memory_pool_init(sizeof(struct ACK_PACKET)); instance->data_pool = memory_pool_init(PACKET_DATA_SIZE); instance->pkt_pool = memory_pool_init(sizeof(struct ETCP_DGRAM) + PACKET_DATA_SIZE); // Create routing module if (routing_create(instance) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to create routing module"); return -1; } DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing module created"); if (g_tun_init_enabled) { instance->tun = tun_init(ua, config); if (!instance->tun) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to initialize TUN device"); return -1; } DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN interface initialized: %s", instance->tun->ifname); if (config->route_subnets) { int added = tun_route_add_all(instance->tun->ifindex, instance->tun->ifname, config->route_subnets); DEBUG_INFO(DEBUG_CATEGORY_TUN, "Added %d system routes for TUN interface", added); instance->route_subnets = config->route_subnets; } } else { DEBUG_INFO(DEBUG_CATEGORY_TUN, "TUN initialization disabled - skipping TUN device setup"); instance->tun = NULL; } if (init_sockets(instance) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize sockets"); return -1; } instance->bgp = route_bgp_init(instance); if (!instance->bgp) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to initialize BGP module"); } else { DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized"); if (instance->rt && instance->bgp->local_node) { route_bgp_update_my_nodeinfo(instance,instance->bgp); if (route_insert(instance->rt,instance->bgp->local_node)) DEBUG_INFO(DEBUG_CATEGORY_ROUTING,"Added local routes"); else DEBUG_WARN(DEBUG_CATEGORY_ROUTING,"Failed to add local routes"); } } // Initialize firewall fw_init(&instance->fw); if (fw_load_rules(&instance->fw, &config->global) != 0) { DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "Failed to load firewall rules"); } else { DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Firewall initialized: %d rules, bypass_all=%d", instance->fw.count, instance->fw.bypass_all); } return 0; } // Create and initialize root instance from config file struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char *config_file) { // Ensure keys and node_id exist in config (generates them if missing) 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); return NULL; } // Load configuration struct utun_config* config = parse_config(config_file); if (!config) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to load config from %s", config_file); return NULL; } // Open log file only if not using global debug system output if (config->global.log_file) { debug_set_output_file(config->global.log_file); } // Apply debug level from config if (config->global.debug_level[0]) { debug_apply_global_level(config->global.debug_level); } // Apply per-category debug levels from [debug] section for (int i = 0; i < config->global.debug_levels.count; i++) { debug_apply_category_config( config->global.debug_levels.category[i], config->global.debug_levels.level[i] ); } // Allocate instance struct UTUN_INSTANCE *instance = u_calloc(1, sizeof(struct UTUN_INSTANCE)); if (!instance) { free_config(config); return NULL; } // Initialize using common function if (instance_init_common(instance, ua, config) != 0) { // Cleanup on error if (instance->config) free_config(instance->config); u_free(instance); return NULL; } // Log instance info if (instance->name[0] != '\0') { DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "uTun instance '%s' created, node_id=0x%llx", instance->name, (unsigned long long)instance->node_id); } else { DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "uTun instance created, node_id=0x%llx", (unsigned long long)instance->node_id); } return instance; } // Create instance from existing config structure (config ownership transfers to instance) struct UTUN_INSTANCE* utun_instance_create_from_config(struct UASYNC* ua, struct utun_config* config) { if (!config) { DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "utun_instance_create_from_config: NULL config"); return NULL; } // Allocate instance struct UTUN_INSTANCE *instance = u_calloc(1, sizeof(struct UTUN_INSTANCE)); if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to allocate UTUN_INSTANCE"); return NULL; } // Initialize using common function (config ownership transferred to instance) if (instance_init_common(instance, ua, config) != 0) { // Cleanup on error - caller still owns config since we failed u_free(instance); 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; // Shutdown control server first if (instance->control_srv) { DEBUG_INFO(DEBUG_CATEGORY_CONTROL, "Shutting down control server"); control_server_shutdown(instance->control_srv); u_free(instance->control_srv); instance->control_srv = NULL; } // 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"); // Safe cleanup of ETCP connections with NULL pointer checks 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); if (conn) { // Дополнительная проверка на случай поврежденного списка etcp_connection_close(conn); // Закрыть соединение (с проверкой NULL внутри) } conn = next; } instance->connections = NULL; DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] ETCP connections cleanup complete"); // Cleanup TUN if (instance->tun) { DEBUG_INFO(DEBUG_CATEGORY_TUN, "Closing TUN interface: %s", instance->tun->ifname); // Delete system routes added at startup if (instance->tun->ifindex && instance->route_subnets) { int deleted = tun_route_del_all(instance->tun->ifindex, instance->tun->ifname, instance->route_subnets); DEBUG_INFO(DEBUG_CATEGORY_TUN, "Deleted %d system routes for TUN interface", deleted); } tun_close(instance->tun); instance->tun = NULL; } // Cleanup routing module routing_destroy(instance); // Cleanup BGP module if (instance->bgp) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "Destroying BGP module"); route_bgp_destroy(instance); instance->bgp = NULL; } // Cleanup firewall fw_free(&instance->fw); // Cleanup config if (instance->config) { DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Freeing configuration"); free_config(instance->config); instance->config = 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; } // Cleanup packet pool (ensure no leak if stop wasn't called) if (instance->ack_pool) { DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Destroying ack pool"); memory_pool_destroy(instance->ack_pool); instance->ack_pool = NULL; } // Cleanup data pool if (instance->data_pool) { DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Destroying data pool"); memory_pool_destroy(instance->data_pool); instance->data_pool = NULL; } // Note: uasync is NOT destroyed here - caller must destroy it separately // This allows sharing uasync between multiple instances 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"); u_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; // Set TUN interface in routing module if (instance->tun) { routing_set_tun(instance); DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "TUN interface registered in routing module"); } // Note: TUN socket is already registered in tun_init() // Initialize connections DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "utun_instance_init() calling init_connections() for instance %p", instance); int conn_result = init_connections(instance); DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "init_connections() returned: %d", conn_result); if (conn_result < 0) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize connections, error=%d", conn_result); return -1; } DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Connections initialized successfully, count=%d", instance->connections_count); // Initialize control server if configured if (instance->config->global.control_sock.ss_family != 0) { instance->control_srv = (struct control_server*)u_calloc(1, sizeof(struct control_server)); if (instance->control_srv) { if (control_server_init(instance->control_srv, instance->ua, instance, &instance->config->global.control_sock, 8) != 0) { DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Failed to initialize control server, continuing without monitoring"); u_free(instance->control_srv); instance->control_srv = NULL; } else { DEBUG_INFO(DEBUG_CATEGORY_CONTROL, "Control server initialized successfully"); } } } // Start the main loop instance->running = 1; DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "Connections initialized successfully"); 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; } DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[LEAK DIAGNOSIS] Phase: %s", phase); DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "Instance: %p, Node ID: %llu, UA: %p, Running: %d", instance, (unsigned long long)instance->node_id, instance->ua, instance->running); DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "STRUCTURE COUNTS - ETCP Sockets: %d, ETCP Connections: %d, ETCP Links: %d", report.etcp_sockets_count, report.etcp_connections_count, report.etcp_links_count); DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "RESOURCE STATUS - Memory Pool: %s, TUN: %s, TUN FD: %d", instance->pkt_pool ? "ALLOCATED" : "NULL", instance->tun ? instance->tun->ifname : "NULL", instance->tun ? instance->tun->fd : -1); if (instance->pkt_pool) { DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "POTENTIAL LEAK: Memory Pool not freed"); } if (instance->tun) { DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "POTENTIAL LEAK: TUN interface not closed"); } if (report.etcp_sockets_count > 0) { DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "POTENTIAL LEAK: %d ETCP sockets still allocated", report.etcp_sockets_count); } if (report.etcp_connections_count > 0) { DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "POTENTIAL LEAK: %d ETCP connections still allocated", report.etcp_connections_count); } if (report.etcp_links_count > 0) { DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "POTENTIAL LEAK: %d ETCP links still allocated", report.etcp_links_count); } DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[LEAK DIAGNOSIS] Recommendations: pkt_pool=%d, tun=%d, sockets=%d, connections=%d", instance->pkt_pool ? 1 : 0, instance->tun ? 1 : 0, report.etcp_sockets_count, report.etcp_connections_count); }