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.
 
 
 
 
 
 

449 lines
17 KiB

// 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#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);
}