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.
477 lines
19 KiB
477 lines
19 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_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 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"); |
|
|
|
// Add local subnets from config as static routes |
|
struct CFG_ROUTE_ENTRY* subnet = config->my_subnets; |
|
while (subnet) { |
|
struct ROUTE_ENTRY entry = {0}; |
|
entry.network = ntohl(subnet->ip.addr.v4.s_addr); // Convert network byte order to host |
|
entry.prefix_length = subnet->netmask; |
|
|
|
if (route_insert(instance->rt, &entry, NULL, 0, instance->node_id, NULL, 0)) { |
|
struct in_addr addr; |
|
addr.s_addr = htonl(entry.network); |
|
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Added local route: %s/%d", |
|
ip_to_str(&addr, AF_INET).str, entry.prefix_length); |
|
} else { |
|
struct in_addr addr; |
|
addr.s_addr = htonl(entry.network); |
|
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Failed to add local route: %s/%d (skipping)", |
|
ip_to_str(&addr, AF_INET).str, entry.prefix_length); |
|
} |
|
|
|
subnet = subnet->next; |
|
} |
|
|
|
// Initialize TUN device if enabled |
|
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); |
|
|
|
// Add system routes for route_subnets |
|
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; |
|
} |
|
|
|
// Initialize BGP module for route exchange |
|
instance->bgp = route_bgp_init(instance); |
|
if (!instance->bgp) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Failed to initialize BGP module"); |
|
// Non-fatal: BGP is optional for basic operation |
|
} else { |
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP module initialized"); |
|
} |
|
|
|
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_level_t level = DEBUG_LEVEL_INFO; |
|
if (strcmp(config->global.debug_level, "error") == 0) level = DEBUG_LEVEL_ERROR; |
|
else if (strcmp(config->global.debug_level, "warn") == 0) level = DEBUG_LEVEL_WARN; |
|
else if (strcmp(config->global.debug_level, "info") == 0) level = DEBUG_LEVEL_INFO; |
|
else if (strcmp(config->global.debug_level, "debug") == 0) level = DEBUG_LEVEL_DEBUG; |
|
else if (strcmp(config->global.debug_level, "trace") == 0) level = DEBUG_LEVEL_TRACE; |
|
debug_set_level(level); |
|
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Applied debug_level from config: %s", config->global.debug_level); |
|
} |
|
|
|
// Apply per-category debug levels from [debug] section |
|
for (int i = 0; i < config->global.debug_levels.count; i++) { |
|
const char* cat_name = config->global.debug_levels.category[i]; |
|
const char* lvl_str = config->global.debug_levels.level[i]; |
|
|
|
debug_category_t cat = 0; |
|
// Map category name to bitmask |
|
if (strcmp(cat_name, "uasync") == 0) cat = DEBUG_CATEGORY_UASYNC; |
|
else if (strcmp(cat_name, "ll_queue") == 0) cat = DEBUG_CATEGORY_LL_QUEUE; |
|
else if (strcmp(cat_name, "connection") == 0) cat = DEBUG_CATEGORY_CONNECTION; |
|
else if (strcmp(cat_name, "etcp") == 0) cat = DEBUG_CATEGORY_ETCP; |
|
else if (strcmp(cat_name, "crypto") == 0) cat = DEBUG_CATEGORY_CRYPTO; |
|
else if (strcmp(cat_name, "memory") == 0) cat = DEBUG_CATEGORY_MEMORY; |
|
else if (strcmp(cat_name, "timing") == 0) cat = DEBUG_CATEGORY_TIMING; |
|
else if (strcmp(cat_name, "config") == 0) cat = DEBUG_CATEGORY_CONFIG; |
|
else if (strcmp(cat_name, "tun") == 0) cat = DEBUG_CATEGORY_TUN; |
|
else if (strcmp(cat_name, "routing") == 0) cat = DEBUG_CATEGORY_ROUTING; |
|
else if (strcmp(cat_name, "timers") == 0) cat = DEBUG_CATEGORY_TIMERS; |
|
else if (strcmp(cat_name, "normalizer") == 0) cat = DEBUG_CATEGORY_NORMALIZER; |
|
else if (strcmp(cat_name, "bgp") == 0) cat = DEBUG_CATEGORY_BGP; |
|
else if (strcmp(cat_name, "socket") == 0) cat = DEBUG_CATEGORY_SOCKET; |
|
else if (strcmp(cat_name, "control") == 0) cat = DEBUG_CATEGORY_CONTROL; |
|
else if (strcmp(cat_name, "dump") == 0) cat = DEBUG_CATEGORY_DUMP; |
|
else if (strcmp(cat_name, "traffic") == 0) cat = DEBUG_CATEGORY_TRAFFIC; |
|
|
|
if (cat) { |
|
debug_level_t lvl = DEBUG_LEVEL_NONE; |
|
if (strcmp(lvl_str, "error") == 0) lvl = DEBUG_LEVEL_ERROR; |
|
else if (strcmp(lvl_str, "warn") == 0) lvl = DEBUG_LEVEL_WARN; |
|
else if (strcmp(lvl_str, "info") == 0) lvl = DEBUG_LEVEL_INFO; |
|
else if (strcmp(lvl_str, "debug") == 0) lvl = DEBUG_LEVEL_DEBUG; |
|
else if (strcmp(lvl_str, "trace") == 0) lvl = DEBUG_LEVEL_TRACE; |
|
|
|
debug_set_category_level(cat, lvl); |
|
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Applied per-category debug: %s=%s", cat_name, lvl_str); |
|
} |
|
} |
|
|
|
// 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; |
|
} |
|
|
|
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 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); |
|
}
|
|
|