- etcp_link_update_inflight_lim: self-contained method with clamping, send_blocked_inflight check, loadbalancer_link_ready on cwnd increase - etcp_conn_on_inflight_lim_changed: recalc optimal_inflight + resume input - etcp_ack_recv: use etcp_link_update_inflight_lim instead of manual set - etcp_request_pkt: fix line 928 check old link (inf_pkt->last_link) not new - etcp_link_close: recalc optimal_inflight on link removal (both branches) - etcp_socket_remove: fix dangling pointer loop (nullify after close) - uasync_print_resources: show timer names, remain time, deleted entries - diagnostics in utun_instance_destroy and test_etcp_reconnect
715 lines
28 KiB
C
715 lines
28 KiB
C
// utun_instance.c - Root instance implementation
|
|
#include "utun_instance.h"
|
|
#include "etcp_router.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>
|
|
#ifndef _WIN32
|
|
#include <unistd.h>
|
|
#endif
|
|
#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");
|
|
}
|
|
|
|
static int local_sockaddr_equal(const struct sockaddr_storage *a, const struct sockaddr_storage *b) {
|
|
if (!a || !b || a->ss_family != b->ss_family) return 0;
|
|
if (a->ss_family == AF_INET) {
|
|
const struct sockaddr_in *ia = (const struct sockaddr_in *)a;
|
|
const struct sockaddr_in *ib = (const struct sockaddr_in *)b;
|
|
return ia->sin_addr.s_addr == ib->sin_addr.s_addr && ia->sin_port == ib->sin_port;
|
|
}
|
|
return 0; // IPv6 stub
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
int socket_result = init_sockets(instance);
|
|
instance->socket_init_status = socket_result;
|
|
if (socket_result < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize sockets");
|
|
return -1;
|
|
}
|
|
if (socket_result == 1) {
|
|
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "Partial socket initialization - some servers failed to bind");
|
|
}
|
|
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);
|
|
}
|
|
|
|
// etcp_router — сервисная маршрутизация (после BGP, до TCP proxy/NAT/DATA)
|
|
if (etcp_router_init(instance) != 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize etcp_router");
|
|
return -1;
|
|
}
|
|
|
|
// Bind DATA handler via etcp_router (after etcp_router_init)
|
|
if (routing_bind(instance) != 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to bind DATA via etcp_router");
|
|
return -1;
|
|
}
|
|
|
|
// TCP proxy server (exit node, optional) — must be before tcp_proxy_client so
|
|
// tcp_proxy_client_create can overwrite the handler if it has remote mappings
|
|
if (tcp_proxy_server_init(instance) != 0) {
|
|
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "Failed to initialize tcp_proxy_server (non-fatal)");
|
|
}
|
|
|
|
// TCP proxy client (from [tcp_proxy] config section)
|
|
if (config->global.tcp_proxy_client_enabled) {
|
|
const char* tun_name = config->global.tcp_proxy_client_tun_name[0] ? config->global.tcp_proxy_client_tun_name : "tun_tcp";
|
|
const char* tun_ip = config->global.tcp_proxy_client_tun_ip[0] ? config->global.tcp_proxy_client_tun_ip : "10.99.0.1";
|
|
int mtu = config->global.tcp_proxy_client_mtu > 0 ? config->global.tcp_proxy_client_mtu : 1500;
|
|
instance->tcp_proxy_client = tcp_proxy_client_create(instance, ua, tun_name, tun_ip, mtu, g_tun_init_enabled ? 0 : 1,
|
|
config->global.tcp_proxy_client_mappings, config->global.tcp_proxy_client_mapping_count,
|
|
config->global.tcp_proxy_client_via_node_id);
|
|
if (instance->tcp_proxy_client) {
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy client enabled: TUN=%s IP=%s MTU=%d mappings=%d",
|
|
tun_name, tun_ip, mtu, config->global.tcp_proxy_client_mapping_count);
|
|
} else {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create TCP proxy client");
|
|
return -1;
|
|
}
|
|
} else {
|
|
instance->tcp_proxy_client = NULL;
|
|
}
|
|
|
|
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[0]) {
|
|
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;
|
|
}
|
|
|
|
// Create instance from config text string (writes to temp file, parses, cleans up)
|
|
struct UTUN_INSTANCE* utun_instance_create_from_str(struct UASYNC* ua, const char* config_text) {
|
|
if (!config_text) return NULL;
|
|
char tmp_path[] = "/tmp/utun_cfg_XXXXXX";
|
|
int fd = mkstemp(tmp_path);
|
|
if (fd < 0) return NULL;
|
|
size_t len = strlen(config_text);
|
|
if (write(fd, config_text, len) != (ssize_t)len) { close(fd); unlink(tmp_path); return NULL; }
|
|
close(fd);
|
|
struct UTUN_INSTANCE* inst = utun_instance_create(ua, tmp_path);
|
|
unlink(tmp_path);
|
|
return inst;
|
|
}
|
|
|
|
// 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");
|
|
if (instance->ua) uasync_print_resources(instance->ua, "INSTANCE_DESTROY_BEFORE");
|
|
|
|
// 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");
|
|
|
|
struct PING_CONTEXT* p = instance->pending_pings;
|
|
while (p) {
|
|
struct PING_CONTEXT* next = p->next;
|
|
if (p->timeout_timer) uasync_cancel_timeout(instance->ua, p->timeout_timer);
|
|
u_free(p);
|
|
p = next;
|
|
}
|
|
instance->pending_pings = NULL;
|
|
|
|
// 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 TCP proxy client module
|
|
if (instance->tcp_proxy_client) {
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Destroying TCP proxy client module");
|
|
tcp_proxy_client_destroy(instance->tcp_proxy_client);
|
|
instance->tcp_proxy_client = NULL;
|
|
}
|
|
|
|
// Cleanup TCP proxy server
|
|
tcp_proxy_server_destroy(instance);
|
|
|
|
// Cleanup routing module (unbinds from etcp_router before etcp_router_destroy)
|
|
routing_destroy(instance);
|
|
|
|
// Cleanup NAT (unbinds from etcp_router before etcp_router_destroy)
|
|
if (instance->nat_tr.initialized) {
|
|
nat_transport_destroy(instance);
|
|
}
|
|
|
|
// Cleanup etcp_router
|
|
etcp_router_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;
|
|
}
|
|
|
|
if (instance->ua) uasync_print_resources(instance->ua, "INSTANCE_DESTROY_AFTER");
|
|
|
|
// 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");
|
|
}
|
|
|
|
// Initialize NAT (after routing_set_tun, before connections)
|
|
if (instance->config->global.nat_enabled) {
|
|
int nat_ret = nat_transport_init(instance);
|
|
if (nat_ret != 0) {
|
|
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT transport init failed (non-fatal)");
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
/* Variant B: selective reload. Compares settings for [server:], [client:], link=. Unchanged untouched (no close, no timers, no queues). Changed/deleted: target only that object. New: add. Routing/fw always. BGP self-restarts. */
|
|
struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struct UASYNC *ua, const char *config_file) {
|
|
if (!instance || !ua || !config_file) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "utun_instance_reload: invalid arguments");
|
|
return NULL;
|
|
}
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "SIGHUP selective reload from %s", config_file);
|
|
debug_reopen_log();
|
|
struct utun_config *new_config = parse_config(config_file);
|
|
if (!new_config) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to parse new config");
|
|
return instance;
|
|
}
|
|
|
|
// Reset + apply debug categories from [debug] section (always, for partial and full paths).
|
|
// Mirrors utun_instance_create exactly. Reset ensures removed categories fall back to global.
|
|
for (int i = 0; i < DEBUG_CATEGORY_COUNT; i++) {
|
|
g_debug_config.category_levels[i] = DEBUG_LEVEL_NONE;
|
|
}
|
|
if (new_config->global.debug_level[0]) {
|
|
debug_apply_global_level(new_config->global.debug_level);
|
|
}
|
|
for (int i = 0; i < new_config->global.debug_levels.count; i++) {
|
|
debug_apply_category_config(
|
|
new_config->global.debug_levels.category[i],
|
|
new_config->global.debug_levels.level[i]
|
|
);
|
|
}
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Applied [debug] section (%d categories)", new_config->global.debug_levels.count);
|
|
|
|
int needs_full = 0;
|
|
if (strcmp(instance->config->global.my_public_key_hex, new_config->global.my_public_key_hex) != 0 ||
|
|
strcmp(instance->config->global.my_private_key_hex, new_config->global.my_private_key_hex) != 0 ||
|
|
instance->config->global.my_node_id != new_config->global.my_node_id ||
|
|
strcmp(instance->config->global.tun_ifname, new_config->global.tun_ifname) != 0) {
|
|
needs_full = 1;
|
|
}
|
|
if (needs_full) {
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Incompatible global - full reload");
|
|
utun_instance_destroy(instance);
|
|
struct UTUN_INSTANCE *new_instance = utun_instance_create(ua, config_file);
|
|
if (!new_instance || utun_instance_init(new_instance) < 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Full reload failed");
|
|
if (new_instance) utun_instance_destroy(new_instance);
|
|
free_config(new_config);
|
|
return NULL;
|
|
}
|
|
new_instance->running = 1;
|
|
free_config(new_config);
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Full reload completed");
|
|
return new_instance;
|
|
}
|
|
// Selective Variant B (settings compare, only affected updated)
|
|
// Servers [server:]
|
|
for (struct CFG_SERVER *ns = new_config->servers; ns; ns = ns->next) {
|
|
int matched = 0;
|
|
for (struct ETCP_SOCKET *os = instance->etcp_sockets; os; os = os->next) {
|
|
if (strcmp(ns->name, os->name) == 0 && local_sockaddr_equal(&ns->ip, &os->local_addr)) {
|
|
matched = 1;
|
|
if (ns->mtu != os->mtu) {
|
|
os->mtu = ns->mtu;
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Updated socket settings for %s", ns->name);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!matched) {
|
|
etcp_socket_add(instance, ns);
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Added new socket %s", ns->name);
|
|
}
|
|
}
|
|
struct ETCP_SOCKET *os = instance->etcp_sockets;
|
|
while (os) {
|
|
struct ETCP_SOCKET *next = os->next;
|
|
int found = 0;
|
|
for (struct CFG_SERVER *ns = new_config->servers; ns; ns = ns->next) {
|
|
if (strcmp(ns->name, os->name) == 0) {
|
|
found = 1; break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
etcp_socket_remove(os);
|
|
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Removed deleted socket %s", os->name);
|
|
}
|
|
os = next;
|
|
}
|
|
// Clients [client:] and links (link=)
|
|
for (struct CFG_CLIENT *nc = new_config->clients; nc; nc = nc->next) {
|
|
struct ETCP_CONN *conn = NULL;
|
|
for (struct ETCP_CONN *c = instance->connections; c; c = c->next) {
|
|
if (strcmp(nc->name, c->name) == 0) {
|
|
conn = c; break;
|
|
}
|
|
}
|
|
if (!conn) {
|
|
conn = etcp_connection_create(instance, nc->name);
|
|
if (conn) {
|
|
sc_set_peer_public_key(&conn->crypto_ctx, (const uint8_t*)nc->peer_public_key_hex, 1);
|
|
if (random_bytes((uint8_t*)&conn->session_id, sizeof(conn->session_id)) != 0) {
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "Failed to generate session_id for client %s", nc->name);
|
|
conn->session_id = 0;
|
|
}
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added new client %s session_id=%08x", nc->name, conn->session_id);
|
|
}
|
|
}
|
|
// links
|
|
for (struct CFG_CLIENT_LINK *nl = nc->links; nl; nl = nl->next) {
|
|
struct ETCP_LINK *link = NULL;
|
|
for (struct ETCP_LINK *l = conn->links; l; l = l->next) {
|
|
if (local_sockaddr_equal(&nl->remote_addr, &l->remote_addr)) {
|
|
link = l; break;
|
|
}
|
|
}
|
|
if (link) {
|
|
// in-place for unchanged link (no tear)
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Link for %s unchanged - untouched", nc->name);
|
|
continue;
|
|
}
|
|
struct ETCP_SOCKET *sock = NULL;
|
|
for (struct ETCP_SOCKET *s = instance->etcp_sockets; s; s = s->next) {
|
|
if (strcmp(nl->server_name, s->name) == 0) {
|
|
sock = s; break;
|
|
}
|
|
}
|
|
if (sock) {
|
|
etcp_link_new(conn, sock, &nl->remote_addr, 0);
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added new link for %s", nc->name);
|
|
}
|
|
}
|
|
// remove deleted links (safe with next)
|
|
struct ETCP_LINK *l = conn->links;
|
|
while (l) {
|
|
struct ETCP_LINK *next_l = l->next;
|
|
int found = 0;
|
|
for (struct CFG_CLIENT_LINK *nl = nc->links; nl; nl = nl->next) {
|
|
if (local_sockaddr_equal(&nl->remote_addr, &l->remote_addr)) {
|
|
found = 1; break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
etcp_link_close(l);
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Removed deleted link for %s", nc->name);
|
|
}
|
|
l = next_l;
|
|
}
|
|
}
|
|
fw_free(&instance->fw);
|
|
fw_init(&instance->fw);
|
|
fw_load_rules(&instance->fw, &new_config->global);
|
|
// routing_destroy(instance); // commented per request - BGP self-restarts on conn events
|
|
// routing_create(instance); // commented per request
|
|
if (instance->config) free_config(instance->config);
|
|
instance->config = new_config;
|
|
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Selective partial reload completed (only changed updated, unchanged untouched, [debug] applied)");
|
|
return instance;
|
|
}
|