Browse Source

Fix: routing initialization timing and test callbacks

- Fixed race condition: routing_add_conn called before etcp->normalizer was assigned

- Moved routing_add_conn from pn_init to etcp_connection_create after normalizer init

- Added routing.h include to etcp.c

- Fixed tests: disable routing callback on output_queue to keep packets for test verification

All 19 tests now pass.
nodeinfo-routing-update
Evgeny 8 months ago
parent
commit
277ab872bf
  1. 45
      !/Makefile.am
  2. 586
      !/config_parser.c
  3. 89
      !/config_parser.h
  4. 427
      !/config_updater.c
  5. 23
      !/config_updater.h
  6. 66
      !/crc32.c
  7. 19
      !/crc32.h
  8. 1112
      !/etcp.c
  9. 1147
      !/etcp.c1
  10. 1161
      !/etcp.c2
  11. 489
      !/etcp.c_
  12. 148
      !/etcp.h
  13. 846
      !/etcp_connections.c
  14. 770
      !/etcp_connections.c1
  15. 100
      !/etcp_connections.h
  16. 164
      !/etcp_loadbalancer.c
  17. 20
      !/etcp_loadbalancer.h
  18. 10
      !/fix.patch
  19. 42
      !/packet_dump.h
  20. 604
      !/pkt_normalizer.c
  21. 75
      !/pkt_normalizer.h
  22. 87
      !/routing.h
  23. 388
      !/secure_channel.c
  24. 68
      !/secure_channel.h
  25. 342
      !/test_udp_socket.c
  26. 101
      !/test_udp_socket.h
  27. 190
      !/test_virtual_tun.c
  28. 55
      !/test_virtual_tun.h
  29. 339
      !/tun_if.c
  30. 107
      !/tun_if.h
  31. 321
      !/utun.c
  32. 42
      !/utun.conf
  33. 388
      !/utun_instance.c
  34. 314
      !/utun_instance.c.backup
  35. 230
      !/utun_instance.c1
  36. 72
      !/utun_instance.h
  37. 586
      !/utun_test_framework.c
  38. 114
      !/utun_test_framework.h
  39. 30
      !/utun_test_hooks.c
  40. 126
      !/utun_test_hooks.h
  41. 117
      !/utun_test_socket_api.c
  42. 52
      !/utun_test_socket_api.h
  43. 1
      src/Makefile.am
  44. 32
      src/etcp.c
  45. 2
      src/etcp_connections.c
  46. 13
      src/pkt_normalizer.c
  47. 2
      src/pkt_normalizer.h
  48. 73
      src/route_lib.c
  49. 200
      src/route_lib.h
  50. 0
      src/route_lib.txt
  51. 504
      src/routing.c
  52. 201
      src/routing.h
  53. 7
      src/routing.txt
  54. 73
      src/tun_if.c
  55. 19
      src/tun_if.h
  56. 2
      src/utun.c
  57. 48
      src/utun_instance.c
  58. 5
      src/utun_instance.h
  59. 5
      tests/Makefile.am
  60. BIN
      tests/test_etcp_100_packets
  61. 8
      tests/test_etcp_100_packets.c
  62. BIN
      tests/test_etcp_minimal
  63. BIN
      tests/test_etcp_simple_traffic
  64. 5
      tests/test_etcp_simple_traffic.c
  65. BIN
      tests/test_etcp_two_instances
  66. BIN
      tests/test_pkt_normalizer_etcp
  67. 4
      tests/test_pkt_normalizer_etcp.c
  68. BIN
      tests/test_pkt_normalizer_standalone
  69. 2
      tests/test_pkt_normalizer_standalone.c

45
!/Makefile.am

@ -1,45 +0,0 @@
bin_PROGRAMS = utun
utun_SOURCES = \
utun.c \
utun_instance.c \
config_parser.c \
config_updater.c \
routing.c \
tun_if.c \
etcp.c \
etcp_connections.c \
etcp_loadbalancer.c \
secure_channel.c \
crc32.c \
pkt_normalizer.c \
utun_test_hooks.c \
$(top_srcdir)/tinycrypt/lib/source/aes_encrypt.c \
$(top_srcdir)/tinycrypt/lib/source/aes_decrypt.c \
$(top_srcdir)/tinycrypt/lib/source/cbc_mode.c \
$(top_srcdir)/tinycrypt/lib/source/ccm_mode.c \
$(top_srcdir)/tinycrypt/lib/source/cmac_mode.c \
$(top_srcdir)/tinycrypt/lib/source/ctr_mode.c \
$(top_srcdir)/tinycrypt/lib/source/ecc.c \
$(top_srcdir)/tinycrypt/lib/source/ecc_dh.c \
$(top_srcdir)/tinycrypt/lib/source/ecc_dsa.c \
$(top_srcdir)/tinycrypt/lib/source/ecc_platform_specific.c \
$(top_srcdir)/tinycrypt/lib/source/hmac.c \
$(top_srcdir)/tinycrypt/lib/source/sha256.c \
$(top_srcdir)/tinycrypt/lib/source/utils.c
utun_CFLAGS = \
-I$(top_srcdir)/lib \
-I$(top_srcdir)/tinycrypt/lib/include \
-I$(top_srcdir)/tinycrypt/lib/source \
$(DEBUG_FLAGS)
utun_LDADD = \
$(top_builddir)/lib/libuasync.a \
-lpthread \
-lm
# Install directories
install-exec-hook:
$(MKDIR_P) $(DESTDIR)$(bindir)
$(MKDIR_P) $(DESTDIR)$(sysconfdir)/utun

586
!/config_parser.c

@ -1,586 +0,0 @@
// config_parser.c - Configuration parser for utun application (updated for new structures)
#define _POSIX_C_SOURCE 200809L
#include "config_parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "../lib/debug_config.h"
#include <ctype.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <net/if.h>
#define MAX_LINE_LEN 1024
#define INITIAL_ARRAY_CAPACITY 8
typedef enum {
SECTION_UNKNOWN,
SECTION_GLOBAL,
SECTION_SERVER,
SECTION_CLIENT,
SECTION_ROUTING
} section_type_t;
static char* trim(char *str) {
if (!str) return NULL;
while (isspace((unsigned char)*str)) str++;
char *end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) end--;
*(end + 1) = '\0';
return str;
}
static int parse_key_value(const char *line, char *key, size_t key_len, char *value, size_t value_len) {
char *equal = strchr(line, '=');
if (!equal) return -1;
size_t key_size = equal - line;
if (key_size >= key_len) return -1;
strncpy(key, line, key_size);
key[key_size] = '\0';
trim(key);
const char *val_start = equal + 1;
size_t val_len = strlen(val_start);
if (val_len >= value_len) return -1;
strcpy(value, val_start);
trim(value);
return 0;
}
static int assign_string(char *dest, size_t dest_size, const char *src) {
if (!dest || !src || strlen(src) >= dest_size) return -1;
strcpy(dest, src);
return 0;
}
static int parse_ip_with_netmask(const char *str, struct IP *ip, uint8_t *netmask) {
char ip_str[64];
strncpy(ip_str, str, sizeof(ip_str) - 1);
ip_str[sizeof(ip_str) - 1] = '\0';
char *slash = strchr(ip_str, '/');
if (slash) {
*slash = '\0';
*netmask = atoi(slash + 1);
} else {
*netmask = 32; // Default IPv4 netmask
}
// Try IPv4 first
struct in_addr addr4;
if (inet_pton(AF_INET, ip_str, &addr4) == 1) {
ip->family = AF_INET;
ip->addr.v4 = addr4;
return 0;
}
// Try IPv6
struct in6_addr addr6;
if (inet_pton(AF_INET6, ip_str, &addr6) == 1) {
ip->family = AF_INET6;
ip->addr.v6 = addr6;
if (*netmask == 32) *netmask = 128; // Default IPv6 netmask
return 0;
}
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "parse_ip_with_netmask: invalid IP address format: %s", str);
return -1;
}
static int parse_sockaddr(const char *addr_str, const char *port_str, struct sockaddr_storage *sockaddr) {
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
struct addrinfo *result;
if (getaddrinfo(addr_str, port_str, &hints, &result) != 0) {
return -1;
}
memcpy(sockaddr, result->ai_addr, result->ai_addrlen);
freeaddrinfo(result);
return 0;
}
static int parse_address_and_port(const char *str, struct sockaddr_storage *sockaddr) {
char addr_copy[MAX_ADDR_LEN];
if (strlen(str) >= sizeof(addr_copy)) return -1;
strcpy(addr_copy, str);
char *port_str = strrchr(addr_copy, ':');
if (!port_str) return -1;
*port_str = '\0';
port_str++;
return parse_sockaddr(addr_copy, port_str, sockaddr);
}
static uint32_t get_netif_index(const char *ifname) {
if (!ifname || strlen(ifname) == 0) return 0;
return if_nametoindex(ifname);
}
static struct CFG_CLIENT_LINK* create_client_link(struct CFG_SERVER* local_srv, const char *remote_addr) {
struct CFG_CLIENT_LINK *link = calloc(1, sizeof(struct CFG_CLIENT_LINK));
if (!link) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "create_client_link: failed to allocate memory for client link");
return NULL;
}
link->local_srv = local_srv;
if (parse_address_and_port(remote_addr, &link->remote_addr) < 0) {
free(link);
return NULL;
}
link->next = NULL;
return link;
}
static void free_cfg_client_links(struct CFG_CLIENT_LINK *links) {
while (links) {
struct CFG_CLIENT_LINK *next = links->next;
free(links);
links = next;
}
}
static struct CFG_ROUTE_ENTRY* create_route_entry(const char *subnet_str) {
struct CFG_ROUTE_ENTRY *entry = calloc(1, sizeof(struct CFG_ROUTE_ENTRY));
if (!entry) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "create_route_entry: failed to allocate memory for route entry");
return NULL;
}
if (parse_ip_with_netmask(subnet_str, &entry->ip, &entry->netmask) < 0) {
free(entry);
return NULL;
}
entry->next = NULL;
return entry;
}
static void free_route_entries(struct CFG_ROUTE_ENTRY *entries) {
while (entries) {
struct CFG_ROUTE_ENTRY *next = entries->next;
free(entries);
entries = next;
}
}
static int add_route_entry(struct CFG_ROUTE_ENTRY **list, const char *subnet_str) {
struct CFG_ROUTE_ENTRY *new_entry = create_route_entry(subnet_str);
if (!new_entry) return -1;
// Add to head of list
new_entry->next = *list;
*list = new_entry;
return 0;
}
static struct CFG_SERVER* find_server_by_name(struct CFG_SERVER *servers, const char *name) {
struct CFG_SERVER *srv = servers;
while (srv) {
if (strcmp(srv->name, name) == 0) {
return srv;
}
srv = srv->next;
}
return NULL;
}
static int parse_global(const char *key, const char *value, struct global_config *global) {
if (strcmp(key, "my_private_key") == 0) {
return assign_string(global->my_private_key_hex, MAX_KEY_LEN, value);
}
if (strcmp(key, "my_public_key") == 0) {
return assign_string(global->my_public_key_hex, MAX_KEY_LEN, value);
}
if (strcmp(key, "my_node_id") == 0) {
global->my_node_id = strtoull(value, NULL, 16);
return 0;
}
if (strcmp(key, "tun_ifname") == 0) {
snprintf(global->tun_ifname, sizeof(global->tun_ifname), "%s", value);
return 0;
}
if (strcmp(key, "tun_ip") == 0) {
uint8_t netmask;
return parse_ip_with_netmask(value, &global->tun_ip, &netmask);
}
if (strcmp(key, "mtu") == 0) {
global->mtu = atoi(value);
return 0;
}
if (strcmp(key, "control_ip") == 0) {
// Store for later processing with control_port
return 0; // We'll handle this when we see control_port
}
if (strcmp(key, "control_port") == 0) {
// This is tricky - we need to get control_ip from previous parsing
// For now, we'll use a simple approach
struct global_config temp_global = *global;
// Assume we stored control_ip somewhere or use default
char control_ip[MAX_ADDR_LEN] = "127.0.0.1"; // Default
char port_str[16];
snprintf(port_str, sizeof(port_str), "%s", value);
parse_sockaddr(control_ip, port_str, &global->control_sock);
return 0;
}
if (strcmp(key, "net_debug") == 0) {
global->net_debug = atoi(value);
return 0;
}
return 0;
}
static int parse_server(const char *key, const char *value, struct CFG_SERVER *srv) {
if (strcmp(key, "addr") == 0) {
return parse_address_and_port(value, &srv->ip);
}
if (strcmp(key, "so_mark") == 0) {
srv->so_mark = atoi(value);
return 0;
}
if (strcmp(key, "netif") == 0) {
srv->netif_index = get_netif_index(value);
return 0;
}
if (strcmp(key, "type") == 0) {
if (strcmp(value, "public") == 0) {
srv->type = CFG_SERVER_TYPE_PUBLIC;
} else if (strcmp(value, "nat") == 0) {
srv->type = CFG_SERVER_TYPE_NAT;
} else if (strcmp(value, "private") == 0) {
srv->type = CFG_SERVER_TYPE_PRIVATE;
} else {
srv->type = CFG_SERVER_TYPE_UNKNOWN;
}
return 0;
}
return 0;
}
static int parse_client(const char *key, const char *value, struct CFG_CLIENT *cli, struct CFG_SERVER *servers) {
if (strcmp(key, "link") == 0) {
char link_copy[MAX_CONN_NAME_LEN + MAX_ADDR_LEN];
if (strlen(value) >= sizeof(link_copy)) return -1;
strcpy(link_copy, value);
// Find first colon (separator between server and ip:port)
char *first_colon = strchr(link_copy, ':');
if (!first_colon) return -1;
*first_colon = '\0';
// Find server by name
struct CFG_SERVER *local_srv = find_server_by_name(servers, link_copy);
if (!local_srv) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "parse_client: server '%s' not found for client link", link_copy);
return -1;
}
struct CFG_CLIENT_LINK *new_link = create_client_link(local_srv, first_colon + 1);
if (!new_link) return -1;
// Add to linked list (prepend)
new_link->next = cli->links;
cli->links = new_link;
return 0;
}
if (strcmp(key, "peer_public_key") == 0) {
return assign_string(cli->peer_public_key_hex, MAX_KEY_LEN, value);
}
if (strcmp(key, "keepalive") == 0) {
cli->keepalive = atoi(value);
return 0;
}
return 0;
}
static section_type_t parse_section_header(const char *line, char *name, size_t name_len) {
if (line[0] != '[') return SECTION_UNKNOWN;
size_t line_len = strlen(line);
if (line[line_len - 1] != ']') return SECTION_UNKNOWN;
char section[128];
if (line_len - 2 >= sizeof(section)) return SECTION_UNKNOWN;
strncpy(section, line + 1, line_len - 2);
section[line_len - 2] = '\0';
trim(section);
if (strcasecmp(section, "global") == 0) return SECTION_GLOBAL;
if (strcasecmp(section, "routing") == 0) return SECTION_ROUTING;
char *colon = strchr(section, ':');
if (!colon) return SECTION_UNKNOWN;
*colon = '\0';
char *type = trim(section);
char *n = trim(colon + 1);
if (strlen(n) >= name_len) return SECTION_UNKNOWN;
strcpy(name, n);
if (strcasecmp(type, "server") == 0) return SECTION_SERVER;
if (strcasecmp(type, "client") == 0) return SECTION_CLIENT;
return SECTION_UNKNOWN;
}
static struct utun_config* parse_config_internal(FILE *fp, const char *filename) {
struct utun_config *cfg = calloc(1, sizeof(struct utun_config));
if (!cfg) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "parse_config_internal: failed to allocate memory for config structure");
return NULL;
}
section_type_t cur_section = SECTION_UNKNOWN;
struct CFG_SERVER *cur_server = NULL;
struct CFG_CLIENT *cur_client = NULL;
char line[MAX_LINE_LEN];
int line_num = 0;
while (fgets(line, sizeof(line), fp)) {
line_num++;
char *trimmed = trim(line);
if (trimmed[0] == '\0' || trimmed[0] == '#') continue;
if (trimmed[0] == '[') {
// Handle previous section
if (cur_section == SECTION_SERVER && cur_server) {
// Add server to linked list
cur_server->next = cfg->servers;
cfg->servers = cur_server;
cur_server = NULL;
}
if (cur_section == SECTION_CLIENT && cur_client) {
// Add client to linked list
cur_client->next = cfg->clients;
cfg->clients = cur_client;
cur_client = NULL;
}
char name[MAX_CONN_NAME_LEN];
cur_section = parse_section_header(trimmed, name, sizeof(name));
if (cur_section == SECTION_SERVER) {
cur_server = calloc(1, sizeof(struct CFG_SERVER));
if (!cur_server) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "parse_config_internal: failed to allocate memory for server %s", name);
goto error;
}
strcpy(cur_server->name, name);
} else if (cur_section == SECTION_CLIENT) {
cur_client = calloc(1, sizeof(struct CFG_CLIENT));
if (!cur_client) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "parse_config_internal: failed to allocate memory for client %s", name);
goto error;
}
strcpy(cur_client->name, name);
}
continue;
}
char key[MAX_LINE_LEN], value[MAX_LINE_LEN];
if (parse_key_value(trimmed, key, sizeof(key), value, sizeof(value)) < 0) {
printf( "%s:%d: Invalid key=value format", filename, line_num);
continue;
}
switch (cur_section) {
case SECTION_GLOBAL:
if (parse_global(key, value, &cfg->global) < 0) {
printf( "%s:%d: Invalid global key '%s'", filename, line_num, key);
}
break;
case SECTION_SERVER:
if (cur_server && parse_server(key, value, cur_server) < 0) {
printf( "%s:%d: Invalid server key '%s'", filename, line_num, key);
}
break;
case SECTION_CLIENT:
if (cur_client && parse_client(key, value, cur_client, cfg->servers) < 0) {
printf( "%s:%d: Invalid client key '%s'", filename, line_num, key);
}
break;
case SECTION_ROUTING:
if (strcmp(key, "allowed_subnet") == 0) {
add_route_entry(&cfg->allowed_subnets, value);
} else if (strcmp(key, "my_subnet") == 0) {
add_route_entry(&cfg->my_subnets, value);
}
break;
default:
printf( "%s:%d: Key outside section: %s", filename, line_num, key);
break;
}
}
// Handle final sections
if (cur_section == SECTION_SERVER && cur_server) {
cur_server->next = cfg->servers;
cfg->servers = cur_server;
}
if (cur_section == SECTION_CLIENT && cur_client) {
cur_client->next = cfg->clients;
cfg->clients = cur_client;
}
// Set default TUN interface name if not provided
if (cfg->global.tun_ifname[0] == '\0') {
snprintf(cfg->global.tun_ifname, sizeof(cfg->global.tun_ifname), "tun0");
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Auto-generated TUN interface name: %s", cfg->global.tun_ifname);
}
return cfg;
error:
if (cur_server) free(cur_server);
if (cur_client) {
free_cfg_client_links(cur_client->links);
free(cur_client);
}
free_config(cfg);
return NULL;
}
struct utun_config* parse_config(const char *filename) {
printf("[CONFIG DEBUG] Opening config file: %s\n", filename);
FILE *fp = fopen(filename, "r");
if (!fp) {
printf("[CONFIG ERROR] Failed to open config file: %s (errno=%d)\n", filename, errno);
return NULL;
}
printf("[CONFIG DEBUG] Successfully opened config file\n");
struct utun_config *config = parse_config_internal(fp, filename);
fclose(fp);
return config;
}
void free_config(struct utun_config *config) {
if (!config) return;
// Free servers
struct CFG_SERVER *server = config->servers;
while (server) {
struct CFG_SERVER *next = server->next;
free(server);
server = next;
}
// Free clients and their links
struct CFG_CLIENT *client = config->clients;
while (client) {
struct CFG_CLIENT *next = client->next;
free_cfg_client_links(client->links);
free(client);
client = next;
}
// Free route entries
free_route_entries(config->allowed_subnets);
free_route_entries(config->my_subnets);
free(config);
}
static const char* ip_to_string(const struct IP *ip, char *buffer, size_t buffer_size) {
if (ip->family == AF_INET) {
inet_ntop(AF_INET, &ip->addr.v4, buffer, buffer_size);
} else if (ip->family == AF_INET6) {
inet_ntop(AF_INET6, &ip->addr.v6, buffer, buffer_size);
} else {
snprintf(buffer, buffer_size, "invalid");
}
return buffer;
}
void print_config(const struct utun_config *cfg) {
if (!cfg) return;
const struct global_config *g = &cfg->global;
char ip_buffer[64];
printf("Global:\n");
printf(" Private key: %s\n", g->my_private_key_hex);
printf(" Public key: %s\n", g->my_public_key_hex);
printf(" Node ID: %llx\n", (unsigned long long)g->my_node_id);
printf(" TUN interface: %s\n", g->tun_ifname[0] ? g->tun_ifname : "auto");
printf(" TUN IP: %s\n", ip_to_string(&g->tun_ip, ip_buffer, sizeof(ip_buffer)));
printf(" MTU: %d\n", g->mtu);
printf(" Net debug: %d\n", g->net_debug);
printf("\nServers:\n");
struct CFG_SERVER *s = cfg->servers;
while (s) {
char addr_buffer[64];
struct sockaddr_in *sin = (struct sockaddr_in *)&s->ip;
inet_ntop(AF_INET, &sin->sin_addr, addr_buffer, sizeof(addr_buffer));
printf(" %s: %s:%d (mark=%d, netif=%u, type=%u)\n",
s->name, addr_buffer, ntohs(sin->sin_port),
s->so_mark, s->netif_index, s->type);
s = s->next;
}
printf("\nClients:\n");
struct CFG_CLIENT *c = cfg->clients;
while (c) {
printf(" %s:\n", c->name);
struct CFG_CLIENT_LINK *link = c->links;
int link_num = 1;
while (link) {
char addr_buffer[64];
struct sockaddr_in *sin = (struct sockaddr_in *)&link->remote_addr;
inet_ntop(AF_INET, &sin->sin_addr, addr_buffer, sizeof(addr_buffer));
printf(" Link %d: %s:%d (via %s)\n", link_num++, addr_buffer, ntohs(sin->sin_port), link->local_srv->name);
link = link->next;
}
printf(" Peer key: %s\n", c->peer_public_key_hex);
printf(" Keepalive: %d\n", c->keepalive);
c = c->next;
}
printf("\nAllowed Subnets:\n");
struct CFG_ROUTE_ENTRY *allowed = cfg->allowed_subnets;
while (allowed) {
printf(" %s/%d\n", ip_to_string(&allowed->ip, ip_buffer, sizeof(ip_buffer)), allowed->netmask);
allowed = allowed->next;
}
printf("\nMy Subnets:\n");
struct CFG_ROUTE_ENTRY *my = cfg->my_subnets;
while (my) {
printf(" %s/%d\n", ip_to_string(&my->ip, ip_buffer, sizeof(ip_buffer)), my->netmask);
my = my->next;
}
}
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key) {
// Minimal implementation: just append to file
FILE *fp = fopen(filename, "a");
if (!fp) return -1;
fprintf(fp, "\n[global]\n");
fprintf(fp, "my_private_key=%s\n", priv_key);
fprintf(fp, "my_public_key=%s\n", pub_key);
fclose(fp);
return 0;
}

89
!/config_parser.h

@ -1,89 +0,0 @@
// config_parser.h - Configuration parser for utun application
#ifndef CONFIG_PARSER_H
#define CONFIG_PARSER_H
#include <stdint.h>
#include <stddef.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MAX_CONN_NAME_LEN 64
#define MAX_KEY_LEN 256
#define MAX_ADDR_LEN 64
struct IP {
sa_family_t family;
union {
struct in_addr v4;
struct in6_addr v6;
} addr;
};
#define CFG_SERVER_TYPE_UNKNOWN 0
#define CFG_SERVER_TYPE_PUBLIC 1
#define CFG_SERVER_TYPE_NAT 2
#define CFG_SERVER_TYPE_PRIVATE 3
struct CFG_SERVER {
struct CFG_SERVER* next;
char name[MAX_CONN_NAME_LEN];
struct sockaddr_storage ip; // ip:port
uint32_t netif_index;// if_nameindex, 0 - no interface specified
int so_mark;
uint8_t type; // public/nat/private
};
struct CFG_CLIENT_LINK {
struct CFG_CLIENT_LINK *next; // Next link in linked list
struct CFG_SERVER* local_srv;
struct sockaddr_storage remote_addr; // ip:port
};
struct CFG_CLIENT {
char name[MAX_CONN_NAME_LEN];
char peer_public_key_hex[MAX_KEY_LEN];
int keepalive;
struct CFG_CLIENT_LINK *links; // Linked list of links
struct CFG_CLIENT *next; // Next client in linked list
};
struct CFG_ROUTE_ENTRY {
struct CFG_ROUTE_ENTRY* next;
struct IP ip;
uint8_t netmask;
};
struct global_config {
char my_private_key_hex[MAX_KEY_LEN];
char my_public_key_hex[MAX_KEY_LEN];
uint64_t my_node_id;
char tun_ifname[16]; // TUN interface name (e.g., "tun12")
struct IP tun_ip;
int mtu;
struct sockaddr_storage control_sock;
int net_debug;
};
struct utun_config {
struct global_config global;
struct CFG_SERVER* servers;
struct CFG_CLIENT* clients;
struct CFG_ROUTE_ENTRY* allowed_subnets;
struct CFG_ROUTE_ENTRY* my_subnets;
};
struct utun_config* parse_config(const char *filename);
void free_config(struct utun_config *config);
void print_config(const struct utun_config *config);
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key);
#ifdef __cplusplus
}
#endif
#endif

427
!/config_updater.c

@ -1,427 +0,0 @@
// config_updater.c - Configuration file updater implementation
#define _POSIX_C_SOURCE 200809L
#include "config_updater.h"
#include "config_parser.h"
#include "secure_channel.h"
#include "debug_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#define PRIV_HEXKEY_LEN 65 // 32 bytes * 2 hex chars + null
#define PUB_HEXKEY_LEN 129 // 64 bytes * 2 hex chars + null
#define HEXNODEID_LEN 17 // 8 bytes * 2 hex chars + null
#define MAX_LINE_LEN 1024
void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex_str, size_t hex_len) {
if (!bytes || !hex_str || hex_len < len * 2 + 1) {
if (hex_str && hex_len > 0) hex_str[0] = '\0';
return;
}
for (size_t i = 0; i < len; i++) {
snprintf(hex_str + i * 2, hex_len - i * 2, "%02x", bytes[i]);
}
hex_str[len * 2] = '\0';
}
static int is_valid_priv_key(const char *key) {
if (!key || strlen(key) != 64) return 0;
for (int i = 0; i < 64; i++) {
if (!isxdigit((unsigned char)key[i])) return 0;
}
return 1;
}
static int is_valid_pub_key(const char *key) {
if (!key || strlen(key) != 128) return 0;
for (int i = 0; i < 128; i++) {
if (!isxdigit((unsigned char)key[i])) return 0;
}
return 1;
}
static int is_valid_node_id(uint64_t node_id) {
return node_id != 0;
}
static int read_file_to_mem(const char *filename, char **buffer, size_t *size) {
FILE *fp = fopen(filename, "r");
if (!fp) return -1;
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
if (file_size < 0) {
fclose(fp);
return -1;
}
fseek(fp, 0, SEEK_SET);
*buffer = malloc(file_size + 1);
if (!*buffer) {
fclose(fp);
return -1;
}
size_t read_size = fread(*buffer, 1, file_size, fp);
if (read_size != (size_t)file_size) {
free(*buffer);
fclose(fp);
return -1;
}
(*buffer)[file_size] = '\0';
*size = file_size;
fclose(fp);
return 0;
}
static int write_mem_to_file(const char *filename, const char *buffer, size_t size) {
FILE *fp = fopen(filename, "w");
if (!fp) return -1;
size_t written = fwrite(buffer, 1, size, fp);
if (written != size) {
fclose(fp);
return -1;
}
fclose(fp);
return 0;
}
static char* find_section_global(char *buf, size_t buf_len) {
char *p = buf;
while (p < buf + buf_len) {
if (p[0] == '[' && strncmp(p + 1, "global", 6) == 0) {
char *end = strchr(p, ']');
if (end) return end + 1;
}
p = strchr(p, '\n');
if (!p) break;
p++; // skip '\n'
}
return NULL;
}
static char* find_option(char *buf, size_t buf_len, const char *option) {
char *p = buf;
while (p < buf + buf_len) {
if (strncmp(p, option, strlen(option)) == 0) {
char *after_key = p + strlen(option);
if (after_key[0] == '=') return p;
}
p = strchr(p, '\n');
if (!p) break;
p++;
}
return NULL;
}
static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_capacity, const char *option, const char *value) {
if (!buf || !*buf || !buf_len || !buf_capacity || !option || !value) return -1;
// Check if option already exists
char *opt_pos = find_option(*buf, *buf_len, option);
if (opt_pos) {
// Find option line end
char *line_end = strchr(opt_pos, '\n');
if (!line_end) line_end = *buf + *buf_len;
// Prepare new line
char new_line[MAX_LINE_LEN];
int new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value);
if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1;
// Calculate length difference
size_t old_line_len = line_end - opt_pos + 1;
long len_diff = new_len - old_line_len;
// Ensure buffer capacity
if (*buf_len + len_diff + 1 > *buf_capacity) {
*buf_capacity = *buf_len + len_diff + 1024;
char *new_buf = realloc(*buf, *buf_capacity);
if (!new_buf) return -1;
*buf = new_buf;
// Recalculate positions after realloc
opt_pos = find_option(*buf, *buf_len, option);
if (!opt_pos) return -1;
line_end = strchr(opt_pos, '\n');
if (!line_end) line_end = *buf + *buf_len;
}
// Move content and insert new line
memmove(opt_pos + new_len, line_end, *buf_len - (line_end - *buf) + 1);
memcpy(opt_pos, new_line, new_len);
*buf_len += len_diff;
} else {
// Insert after [global] section
char *section_end = find_section_global(*buf, *buf_len);
if (!section_end) return -1;
// Find end of global section (next [ or end of buffer)
char *insert_pos = strchr(section_end, '[');
if (!insert_pos) insert_pos = *buf + *buf_len;
else {
// Move to beginning of next line
char *prev_nl = insert_pos;
while (prev_nl > *buf && *prev_nl != '\n') prev_nl--;
if (*prev_nl == '\n') insert_pos = prev_nl + 1;
}
// Prepare new line
char new_line[MAX_LINE_LEN];
int new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value);
if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1;
// Ensure buffer capacity
if (*buf_len + new_len + 1 > *buf_capacity) {
*buf_capacity = *buf_len + new_len + 1024;
char *new_buf = realloc(*buf, *buf_capacity);
if (!new_buf) return -1;
*buf = new_buf;
// Recalculate insert position after realloc
section_end = find_section_global(*buf, *buf_len);
if (!section_end) return -1;
insert_pos = strchr(section_end, '[');
if (!insert_pos) insert_pos = *buf + *buf_len;
else {
char *prev_nl = insert_pos;
while (prev_nl > *buf && *prev_nl != '\n') prev_nl--;
if (*prev_nl == '\n') insert_pos = prev_nl + 1;
}
}
// Move content and insert new line
memmove(insert_pos + new_len, insert_pos, *buf_len - (insert_pos - *buf) + 1);
memcpy(insert_pos, new_line, new_len);
*buf_len += new_len;
}
return 0;
}
int config_ensure_keys_and_node_id(const char *filename) {
struct utun_config *config = parse_config(filename);
if (!config) {
printf("[CONFIG ERROR] Failed to parse config: %s\n", filename);
return -1;
}
struct global_config *global = &config->global;
// Debug: print what we found
printf("[CONFIG DEBUG] Checking config - priv_key='%s' (len=%zu), pub_key='%s' (len=%zu), node_id=%llu\n",
global->my_private_key_hex ? global->my_private_key_hex : "NULL",
global->my_private_key_hex ? strlen(global->my_private_key_hex) : 0,
global->my_public_key_hex ? global->my_public_key_hex : "NULL",
global->my_public_key_hex ? strlen(global->my_public_key_hex) : 0,
(unsigned long long)global->my_node_id);
// Check if we need to generate anything
int need_priv_key = !is_valid_priv_key(global->my_private_key_hex);
int need_pub_key = !is_valid_pub_key(global->my_public_key_hex);
int need_node_id = !is_valid_node_id(global->my_node_id);
printf("[CONFIG DEBUG] Validation results - need_priv_key=%d, need_pub_key=%d, need_node_id=%d\n",
need_priv_key, need_pub_key, need_node_id);
if (!need_priv_key && !need_pub_key && !need_node_id) {
free_config(config);
return 0;
}
// Generate keys if needed
char new_priv_key[PRIV_HEXKEY_LEN] = {0};
char new_pub_key[PUB_HEXKEY_LEN] = {0};
uint64_t new_node_id = 0;
if (need_priv_key) {
// Generate new keypair if private key is invalid
struct SC_MYKEYS mykeys;
if (sc_generate_keypair(&mykeys) != SC_OK) {
printf( "Failed to generate keypair");
free_config(config);
return -1;
}
bytes_to_hex(mykeys.private_key, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
bytes_to_hex(mykeys.public_key, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key));
} else if (need_pub_key) {
// Compute public key from existing private key
uint8_t priv_bin[SC_PRIVKEY_SIZE];
uint8_t pub_bin[SC_PUBKEY_SIZE];
// Convert private key from hex to binary
for (int i = 0; i < SC_PRIVKEY_SIZE; i++) {
unsigned int byte;
if (sscanf(global->my_private_key_hex + i * 2, "%2x", &byte) != 1) {
printf( "Invalid private key hex format");
free_config(config);
return -1;
}
priv_bin[i] = (uint8_t)byte;
}
// Compute public key
if (sc_compute_public_key_from_private(priv_bin, pub_bin) != SC_OK) {
printf( "Failed to compute public key from private key");
free_config(config);
return -1;
}
// Convert to hex
bytes_to_hex(priv_bin, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
bytes_to_hex(pub_bin, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key));
}
if (need_node_id) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
printf( "Failed to open /dev/urandom");
free_config(config);
return -1;
}
if (read(fd, &new_node_id, sizeof(new_node_id)) != sizeof(new_node_id)) {
close(fd);
printf( "Failed to read random bytes for node_id");
free_config(config);
return -1;
}
close(fd);
new_node_id &= 0x7FFFFFFFFFFFFFFF;
}
free_config(config);
// Read entire config file to memory
char *file_buf = NULL;
size_t file_size = 0;
if (read_file_to_mem(filename, &file_buf, &file_size) < 0) {
printf( "Failed to read config file: %s", filename);
return -1;
}
// Update config file
size_t buf_capacity = file_size + 1024;
char *work_buf = malloc(buf_capacity);
if (!work_buf) {
free(file_buf);
return -1;
}
memcpy(work_buf, file_buf, file_size);
size_t work_len = file_size;
int ret = 0;
if (need_node_id) {
char node_id_hex[HEXNODEID_LEN + 1];
snprintf(node_id_hex, sizeof(node_id_hex), "%llx", (unsigned long long)new_node_id);
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_node_id", node_id_hex) < 0) {
ret = -1;
}
}
if (need_priv_key && ret == 0) {
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_private_key", new_priv_key) < 0) {
ret = -1;
}
}
if (need_pub_key && ret == 0) {
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_public_key", new_pub_key) < 0) {
ret = -1;
}
}
if (ret == 0) {
printf("[CONFIG DEBUG] Writing updated config file, work_len=%zu\n", work_len);
if (write_mem_to_file(filename, work_buf, work_len) < 0) {
printf( "Failed to write updated config file: %s", filename);
ret = -1;
} else {
printf("[CONFIG DEBUG] Successfully updated config file: %s\n", filename);
}
}
free(file_buf);
free(work_buf);
return ret;
}

23
!/config_updater.h

@ -1,23 +0,0 @@
// config_updater.h - Configuration file updater
#ifndef CONFIG_UPDATER_H
#define CONFIG_UPDATER_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// Ensures config file has valid my_private_key, my_public_key, and my_node_id
// If any are missing or invalid, generates and updates them
// Returns 0 on success, -1 on error
int config_ensure_keys_and_node_id(const char *filename);
void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex_str, size_t hex_len);
#ifdef __cplusplus
}
#endif
#endif // CONFIG_UPDATER_H

66
!/crc32.c

@ -1,66 +0,0 @@
// crc32.c - CRC32 checksum implementation
#include "crc32.h"
#include <string.h>
static uint32_t crc32_table[256];
static int crc32_table_initialized = 0;
static void init_crc32_table(void) {
if (crc32_table_initialized) return;
for (uint32_t i = 0; i < 256; i++) {
uint32_t crc = i;
for (int j = 0; j < 8; j++) {
if (crc & 1) {
crc = (crc >> 1) ^ 0xEDB88320;
} else {
crc >>= 1;
}
}
crc32_table[i] = crc;
}
crc32_table_initialized = 1;
}
void crc32_init(void) {
init_crc32_table();
}
uint32_t crc32_calc(const uint8_t *data, size_t len) {
if (!crc32_table_initialized) {
init_crc32_table();
}
if (!data || len == 0) return 0xFFFFFFFF;
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < len; i++) {
uint8_t byte = data[i];
uint32_t table_index = (crc ^ byte) & 0xFF;
crc = (crc >> 8) ^ crc32_table[table_index];
}
return ~crc;
}
uint32_t crc32_calc_ex(const uint8_t *data, size_t len, uint32_t initial_crc) {
if (!crc32_table_initialized) {
init_crc32_table();
}
if (!data || len == 0) return initial_crc;
uint32_t crc = initial_crc;
for (size_t i = 0; i < len; i++) {
uint8_t byte = data[i];
uint32_t table_index = (crc ^ byte) & 0xFF;
crc = (crc >> 8) ^ crc32_table[table_index];
}
return crc;
}
uint32_t crc32_update(uint32_t crc, const uint8_t *data, size_t len) {
return crc32_calc_ex(data, len, crc);
}

19
!/crc32.h

@ -1,19 +0,0 @@
#ifndef CRC32_H
#define CRC32_H
#include <stdint.h>
#include <stddef.h>
// Initialize CRC32 table (called automatically on first use)
void crc32_init(void);
// Calculate CRC32 checksum
uint32_t crc32_calc(const uint8_t *data, size_t len);
// Calculate CRC32 with initial value
uint32_t crc32_calc_ex(const uint8_t *data, size_t len, uint32_t initial_crc);
// Update CRC32 incrementally
uint32_t crc32_update(uint32_t crc, const uint8_t *data, size_t len);
#endif // CRC32_H

1112
!/etcp.c

File diff suppressed because it is too large Load Diff

1147
!/etcp.c1

File diff suppressed because it is too large Load Diff

1161
!/etcp.c2

File diff suppressed because it is too large Load Diff

489
!/etcp.c_

@ -1,489 +0,0 @@
// etcp.c - ETCP Protocol Implementation
#include "../lib/u_async.h"
#include "utun_instance.h"
#include "etcp.h"
#include "../lib/debug_config.h"
#include "../lib/ll_queue.h"
#include "crc32.h"
#include "secure_channel.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
// Service packet headers (from protocol, but reset/init handled elsewhere)
#define ETCP_ACK_HEADER 0x01
#define ETCP_RETRANS_HEADER_BASE 0x10 // 0x10 to 0x2F for retrans requests
#define ETCP_PAYLOAD_HEADER 0x00
#define ETCP_INIT_RESPONSE 0x03
// Forward declarations of internal functions (adapted from etcp_master)
static void retransmit_check(struct ETCP_CONN* etcp);
static void update_metrics(struct ETCP_CONN* etcp, uint16_t rtt);
static uint16_t timestamp_diff(uint16_t t1, uint16_t t2);
static int id_compare(uint16_t id1, uint16_t id2);
static void retransmit_packet(struct ETCP_CONN* etcp, uint16_t id);
// Timer callbacks
static void tx_timer_callback(void* arg);
static void retransmit_timer_callback(void* arg);
// Internal queue callbacks
static void tx_queue_callback(struct ll_queue* q, struct ll_entry* entry, void* arg);
static void schedule_ack_timer(struct ETCP_CONN* etcp);
static void request_retransmission_for_gaps(struct ETCP_CONN* etcp);
static void queue_clear(struct ll_queue* q) {
struct ll_entry* entry;
while ((entry = queue_entry_get(q)) != NULL) {
queue_entry_free(entry);
}
}
static void etcp_update_window(struct ETCP_CONN* etcp) {
etcp->window_size = 65536; // Initial window size
etcp->retrans_timer_period = 20; // Default retransmit period
}
// Reset connection (adapted from etcp_reset in master, without packet sending)
void etcp_conn_reset(struct ETCP_CONN* etcp) {
if (!etcp) return;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_conn_reset: resetting instance");
// Cancel timers
if (etcp->next_tx_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->next_tx_timer);
etcp->next_tx_timer = NULL;
}
if (etcp->retransmit_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->retransmit_timer);
etcp->retransmit_timer = NULL;
}
// Clear queues (but keep them)
queue_clear(etcp->input_queue);
queue_clear(etcp->output_queue);
// Clear lists
rx_packet_t* rx = etcp->rx_list;
while (rx) {
rx_packet_t* next = rx->next;
if (rx->data) free(rx->data);
free(rx);
rx = next;
}
etcp->rx_list = NULL;
sent_packet_t* sent = etcp->sent_list;
while (sent) {
sent_packet_t* next = sent->next;
if (sent->data) free(sent->data);
free(sent);
sent = next;
}
etcp->sent_list = NULL;
// Reset metrics and stats
etcp->rtt_last = 0;
etcp->rtt_avg_10 = 0;
etcp->rtt_avg_100 = 0;
etcp->jitter = 0;
etcp->bytes_sent_total = 0;
etcp->retransmissions_count = 0;
etcp->ack_packets_count = 0;
etcp->control_packets_count = 0;
etcp->total_packets_sent = 0;
etcp->unique_packets_sent = 0;
etcp->bytes_received_total = 0;
// Reset IDs
etcp->next_tx_id = 1;
etcp->last_sent_id = 0;
etcp->last_rx_id = 0;
etcp->last_delivered_id = 0;
// Reset history
etcp->rtt_history_idx = 0;
etcp->rtt_history_count = 0;
// Reset pending
etcp->pending_ack_count = 0;
etcp->pending_retransmit_count = 0;
// Reset window
etcp->unacked_bytes = 0;
etcp->last_acked_id = 0;
etcp->last_rx_ack_id = 0;
etcp->next_retrans_time = 0;
etcp->window_blocked = 0;
// Reset forward progress
etcp->oldest_missing_id = 0;
etcp->missing_since_time = 0;
etcp_update_window(etcp);
}
// Creating ETCP instance
// после создания надо добавить peer bublic key.
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance) {
if (!instance) return NULL;
struct ETCP_CONN* etcp = calloc(1, sizeof(struct ETCP_CONN));
if (!etcp) return NULL;
etcp->mtu = 1500; // Default MTU
etcp->instance = instance;
// Initialize crypto context
if (sc_init_ctx(&etcp->crypto_ctx, &etcp->instance->my_keys) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connection_create: failed to initialize crypto context for node %llu", (unsigned long long)instance->node_id);
free(etcp);
return NULL;
}
// Initialize queues (tx_queue is input_queue)
etcp->input_queue = queue_new(instance->ua,NULL);
if (!etcp->input_queue) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "etcp_connection_create: failed to create input queue for node %llu", (unsigned long long)instance->node_id);
free(etcp);
return NULL;
}
etcp->output_queue = queue_new(instance->ua,NULL);
if (!etcp->output_queue) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "etcp_connection_create: failed to create output queue for node %llu", (unsigned long long)instance->node_id);
queue_free(etcp->input_queue);
free(etcp);
return NULL;
}
// Set callback for input_queue (tx_queue)
queue_set_callback(etcp->input_queue, tx_queue_callback, etcp);
// Initialize state (from master)
etcp->bandwidth = 10000; // Default: 10000 bytes per timebase (0.1us)
etcp->last_sent_timestamp = get_current_timestamp();
etcp->bytes_allowed = 0;
// Reset all other fields to initial state
etcp_conn_reset(etcp);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_connection_create: created instance with node_id=%llu, mtu=%d",
(unsigned long long)etcp->instance->node_id, etcp->mtu);
etcp->next=instance->connections;
instance->connections=etcp;
instance->connections_count++;
return etcp;
}
// Destroying ETCP instance
void etcp_connection_close(struct ETCP_CONN* etcp) {
if (!etcp) return;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_connection_close: destroying instance");
// Cancel timers
if (etcp->next_tx_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->next_tx_timer);
etcp->next_tx_timer = NULL;
}
if (etcp->retransmit_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->retransmit_timer);
etcp->retransmit_timer = NULL;
}
// Free queues
if (etcp->input_queue) queue_free(etcp->input_queue);
if (etcp->output_queue) queue_free(etcp->output_queue);
// Free rx_list
rx_packet_t* rx = etcp->rx_list;
while (rx) {
rx_packet_t* next = rx->next;
if (rx->data) free(rx->data);
free(rx);
rx = next;
}
// Free sent_list
sent_packet_t* sent = etcp->sent_list;
while (sent) {
sent_packet_t* next = sent->next;
if (sent->data) free(sent->data);
free(sent);
sent = next;
}
free(etcp);
}
// Process incoming packet (partial, truncated in original)
void etcp_conn_input(struct ETCP_DGRAM* pkt) {
if (!pkt) return;
struct ETCP_CONN* etcp=pkt->link->etcp;
if (!etcp) return;
uint8_t* data = pkt->data;
size_t len = pkt->data_len;
if (len < 4) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_conn_input: packet too short (%zu bytes) from node %llu", len, (unsigned long long)etcp->instance->node_id);
return; // Min header
}
uint16_t id = (data[0] << 8) | data[1];
uint16_t timestamp = (data[2] << 8) | data[3];
// Check for INIT_RESPONSE packet (special control packet)
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[ETCP DEBUG] etcp_conn_input: id=%u, len=%zu, first bytes: %02x %02x %02x %02x %02x",
id, len, data[0], data[1], data[2], data[3], data[4]);
data += 4; len -= 4; // Skip header for regular packet processing
int has_payload = 0;
uint8_t* payload_data = NULL;
size_t payload_len = 0;
int is_duplicate = 0;
while (len > 0) {
uint8_t section_header = *data++;
len--;
if (section_header == ETCP_ACK_HEADER) {
// ACK section
uint8_t count = *data++;
len--;
for (uint8_t i = 0; i < count; i++) {
uint16_t ack_id = (data[0] << 8) | data[1];
uint16_t ack_ts = (data[2] << 8) | data[3];
data += 4; len -= 4;
// Process ACK: remove from sent_list, update window
sent_packet_t** ptr = &etcp->sent_list;
while (*ptr) {
if ((*ptr)->id == ack_id) {
sent_packet_t* to_free = *ptr;
*ptr = to_free->next;
etcp->unacked_bytes -= to_free->payload_len;
update_metrics(etcp, timestamp_diff(get_current_timestamp(), ack_ts));
free(to_free->data);
free(to_free);
break;
}
ptr = &(*ptr)->next;
}
}
// last_delivered and last_rx
uint16_t last_delivered = (data[0] << 8) | data[1];
uint16_t last_rx = (data[2] << 8) | data[3];
data += 4; len -= 4;
etcp->last_rx_ack_id = last_rx;
} else if ((section_header & 0xF0) == ETCP_RETRANS_HEADER_BASE) {
// Retrans request
uint8_t count = (section_header & 0x0F) + 1;
for (uint8_t i = 0; i < count; i++) {
uint16_t retrans_id = (data[0] << 8) | data[1];
data += 2;
len -= 2;
retransmit_packet(etcp, retrans_id);
}
// last_delivered and last_rx
uint16_t last_delivered = (data[0] << 8) | data[1];
uint16_t last_rx = (data[2] << 8) | data[3];
data += 4; len -= 4;
} else if (section_header == ETCP_PAYLOAD_HEADER) {
// Payload section
has_payload = 1;
payload_data = data;
payload_len = len;
break; // Payload is last
}
}
// Check for duplicate
rx_packet_t* rx = etcp->rx_list;
while (rx) {
if (rx->id == id) {
is_duplicate = 1;
break;
}
rx = rx->next;
}
if (is_duplicate) {
// Add to pending ACKs
if (etcp->pending_ack_count < 32) {
etcp->pending_ack_ids[etcp->pending_ack_count] = id;
etcp->pending_ack_timestamps[etcp->pending_ack_count] = timestamp;
etcp->pending_ack_count++;
}
schedule_ack_timer(etcp);
return;
}
// Insert into rx_list (sorted)
rx_packet_t* new_rx = calloc(1, sizeof(rx_packet_t));
if (!new_rx) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "etcp_conn_input: failed to allocate memory for rx packet from node %llu", (unsigned long long)etcp->instance->node_id);
return;
}
new_rx->id = id;
new_rx->timestamp = timestamp;
new_rx->has_payload = has_payload;
if (has_payload) {
new_rx->data = malloc(payload_len);
if (!new_rx->data) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "etcp_conn_input: failed to allocate %zu bytes for payload data from node %llu", payload_len, (unsigned long long)etcp->instance->node_id);
free(new_rx);
return;
}
memcpy(new_rx->data, payload_data, payload_len);
new_rx->data_len = payload_len;
etcp->bytes_received_total += payload_len;
}
// Insert sorted
rx_packet_t** ptr = &etcp->rx_list;
while (*ptr && id_compare((*ptr)->id, id) < 0) {
ptr = &(*ptr)->next;
}
new_rx->next = *ptr;
*ptr = new_rx;
// Update last_rx_id
if (id_compare(id, etcp->last_rx_id) > 0) {
etcp->last_rx_id = id;
}
// Add to pending ACKs
if (etcp->pending_ack_count < 32) {
etcp->pending_ack_ids[etcp->pending_ack_count] = id;
etcp->pending_ack_timestamps[etcp->pending_ack_count] = timestamp;
etcp->pending_ack_count++;
}
// Request retrans for gaps and deliver contiguous
request_retransmission_for_gaps(etcp);
schedule_ack_timer(etcp);
}
// Getting statistics (extended with master stats)
void etcp_get_stats(struct ETCP_CONN* etcp, size_t* packets_sent, size_t* packets_recv,
size_t* pool_allocs, size_t* pool_reuse) {
if (!etcp) return;
if (packets_sent) *packets_sent = etcp->total_packets_sent;
if (packets_recv) *packets_recv = etcp->bytes_received_total / 100; // Approximate
if (pool_allocs || pool_reuse) {
memory_pool_get_stats(etcp->instance->pkt_pool, pool_allocs, pool_reuse);
}
}
// ... (rest of functions from master, adapted to ETCP_CONN and using etcp_link_send for tx)
// For tx: assume primary channel (etcp->channels), or iterate if multi-path
static void send_etcp_packet(struct ETCP_CONN* etcp, uint8_t* pkt, uint16_t len) {
if (!etcp->links) {
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "send_etcp_packet: no links available for node %llu", (unsigned long long)etcp->instance->node_id);
return; // No links
}
// Send to primary link (or iterate for multi-path)
char dg_mem[1600];
struct ETCP_DGRAM* dg=(struct ETCP_DGRAM*)&dg_mem;
dg->data_len=len;
dg->noencrypt_len=0;
dg->link=etcp->links;// first link TODO: добавить отправку по нескольким линкам
etcp_encrypt_send(dg);
}
static void update_metrics(struct ETCP_CONN* etcp, uint16_t rtt) {
etcp->rtt_last = rtt;
// Update averages, jitter, etc.
// Placeholder: etcp->rtt_avg_10 = (etcp->rtt_avg_10 * 9 + rtt) / 10;
// etcp->rtt_avg_100 = (etcp->rtt_avg_100 * 99 + rtt) / 100;
}
uint64_t get_current_time_units() {
struct timeval tv;
gettimeofday(&tv, NULL);
return ((uint64_t)tv.tv_sec * 10000ULL) + (tv.tv_usec / 100);
}
uint16_t get_current_timestamp() {
return (uint16_t)get_current_time_units();
}
static uint16_t timestamp_diff(uint16_t t1, uint16_t t2) {
if (t1 >= t2) return t1 - t2;
return (0xFFFF - t2) + t1 + 1; // Wrap around
}
static int id_compare(uint16_t id1, uint16_t id2) {
int16_t diff = id1 - id2;
if (diff == 0) return 0;
return (diff > 0) ? 1 : -1; // Simple for now, add wrap logic if needed
}
static void retransmit_packet(struct ETCP_CONN* etcp, uint16_t id) {
sent_packet_t* sent = etcp->sent_list;
while (sent) {
if (sent->id == id) {
send_etcp_packet(etcp, sent->data, sent->data_len);
etcp->retransmissions_count++;
break;
}
sent = sent->next;
}
}
static void tx_queue_callback(struct ll_queue* q, struct ll_entry* entry, void* arg) {
(void)q;
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
// Process entry to send packet
// Placeholder: uint8_t* data = ll_entry_data(entry);
// size_t len = ll_entry_size(entry);
// // Build packet and send
// send_etcp_packet(etcp, data, len);
queue_resume_callback(q);
}
static void schedule_ack_timer(struct ETCP_CONN* etcp) {
// Placeholder: set timeout to send ACKs
}
static void request_retransmission_for_gaps(struct ETCP_CONN* etcp) {
// Detect gaps in rx_list and add to pending_retransmit
}
static void retransmit_check(struct ETCP_CONN* etcp) {
// Check sent_list for timeouts
}
static void tx_timer_callback(void* arg) {
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
// Handle next tx
}
static void retransmit_timer_callback(void* arg) {
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
retransmit_check(etcp);
}

148
!/etcp.h

@ -1,148 +0,0 @@
// etcp.h - ETCP Protocol Header (refactored based on etcp_protocol.txt)
#ifndef ETCP_H
#define ETCP_H
#include "etcp_connections.h"
#include "secure_channel.h"
#include "../lib/ll_queue.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// Forward declarations
struct UTUN_INSTANCE;
struct UASYNC;
// ETCP packet section types (from protocol spec)
#define ETCP_SECTION_PAYLOAD 0x00 // Data payload
#define ETCP_SECTION_ACK 0x01 // ACK section
#define ETCP_SECTION_RETRANS 0x10 // Retransmission request base (0x10-0x2F)
#define ETCP_SECTION_TIMESTAMP 0x06 // Channel timestamp (example, adjust if needed)
#define ETCP_SECTION_MEAS_TS 0x07 // Measurement timestamp for bandwidth
#define ETCP_SECTION_MEAS_RESP 0x08 // Measurement response
// Inflight packet states
#define INFLIGHT_STATE_WAIT_ACK 0
#define INFLIGHT_STATE_WAIT_SEND 1
// Inflight packet structure
typedef struct inflight_packet {
struct inflight_packet* next;
uint16_t id; // Packet ID
struct ETCP_LINK* last_link; // Last sent link
uint16_t last_timestamp; // Last send timestamp
uint8_t send_count; // Number of sends
uint8_t retrans_req_count; // Number of retrans requests
uint8_t state; // WAIT_ACK or WAIT_SEND
uint8_t need_retrans; // Flag for forced retrans
uint8_t* data; // Packet data
uint16_t data_len; // Data length
uint16_t payload_len; // Payload length for window
} inflight_packet_t;
// RX packet for assembly linked list
typedef struct rx_packet {
struct rx_packet* next;
uint16_t id;
uint16_t timestamp;
uint8_t* data; // Assembled data (payload only)
uint16_t data_len;
} rx_packet_t;
// ETCP connection structure (refactored)
struct ETCP_CONN {
struct ETCP_CONN* next;
int mtu;
struct UTUN_INSTANCE* instance;
// Links (channels) - linked list
struct ETCP_LINK* links;
// Crypto and state
struct secure_channel crypto_ctx;
// Peer info
uint64_t peer_node_id; // Peer node ID
// Queues
struct ll_queue* input_queue; // Incoming packets to send
struct ll_queue* output_queue; // Assembled outgoing packets
// Inflight lists (two lists as per spec)
inflight_packet_t* wait_ack_list; // Waiting for ACK
inflight_packet_t* wait_send_list; // Waiting for send (retrans)
// RX assembly list
rx_packet_t* rx_list; // Sorted by ID for gap detection
// IDs and state
uint16_t next_tx_id; // Next TX ID
uint16_t last_rx_id; // Last received ID
uint16_t last_delivered_id; // Last delivered to output_queue
// Pending ACKs and retrans
uint16_t pending_ack_ids[32];
uint16_t pending_ack_ts[32]; // Timestamps for ACKs
uint8_t pending_ack_count;
uint16_t pending_retrans_ids[32];
uint8_t pending_retrans_count;
// Metrics (RTT, jitter, etc.)
uint16_t rtt_last;
uint16_t rtt_avg_10;
uint16_t rtt_avg_100;
uint16_t jitter;
uint32_t bytes_sent_total;
uint32_t bytes_received_total;
uint32_t retransmissions_count;
// Window and inflight management
uint32_t unacked_bytes; // Current inflight bytes
uint32_t window_size; // Receive window
uint32_t optimal_inflight; // Sum over links
// Timers
void* retrans_timer; // Retrans check timer
void* ack_timer; // ACK send timer
// Bandwidth measurement state
uint8_t burst_in_progress; // Burst transmission flag
uint16_t burst_start_id; // Start ID for burst
// ... (add more for meas_ts, meas_resp)
// Statistics counters
uint32_t ack_packets_count; // Count of ACK packets received
uint16_t last_rx_ack_id; // Last ACK ID received
uint16_t rtt_history[10]; // RTT history for jitter calculation (RTT_HISTORY_SIZE=10)
uint8_t rtt_history_idx; // Current index in RTT history
uint32_t total_packets_sent; // Total packets sent counter
// Flags
uint8_t wait_timeout_active; // In wait timeout state
};
// Functions
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance);
void etcp_connection_close(struct ETCP_CONN* etcp);
void etcp_conn_reset(struct ETCP_CONN* etcp);
// Input from etcp_connections (decrypted packet)
void etcp_conn_input(struct ETCP_DGRAM* pkt);
// Request next packet for load balancer
struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp);
// Get stats
void etcp_get_stats(struct ETCP_CONN* etcp, size_t* packets_sent, size_t* packets_recv,
size_t* pool_allocs, size_t* pool_reuse);
uint16_t get_current_timestamp(void);
#ifdef __cplusplus
}
#endif
#endif // ETCP_H

846
!/etcp_connections.c

@ -1,846 +0,0 @@
#include "etcp_connections.h"
#include <arpa/inet.h>
#include <net/if.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "routing.h"
#include "utun_instance.h"
#include "utun_test_hooks.h"
#include "config_parser.h"
#include "crc32.h"
#include "etcp.h"
#include "../lib/memory_pool.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#include <stdlib.h>
// Packet dump configuration
#define ETCP_PACKET_DUMP_COMPACT 1 // Use compact single-line format
#define ETCP_PACKET_DUMP_FULL 0 // Use full multi-line format (legacy)
#include <time.h>
// Simple debug macros to replace missing debug_config.h
#define DEBUG_CATEGORY_CONNECTION 1
#define DEBUG_CATEGORY_ETCP 2
#define DEBUG_CATEGORY_CRYPTO 3
#define DEBUG_CATEGORY_CONFIG 4
#define DEBUG_CATEGORY_MEMORY 5
// Forward declaration
static void etcp_connections_read_callback(int fd, void* arg);
// Single-line packet dump for debug output
static void dump_packet_bytes(const char* prefix, const uint8_t* data, size_t len) {
// Build packet data as hex string for single-line output
char hex_buf[513]; // 256 bytes * 2 chars + 1 for null terminator
size_t hex_len = 0;
size_t show_len = (len > 128) ? 128 : len; // Show max 128 bytes
for (size_t i = 0; i < show_len && hex_len < 512 - 3; i++) {
hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "%02x", data[i]);
if (i < show_len - 1 && (i + 1) % 32 == 0) { // Add space every 32 bytes
hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, " ");
}
}
if (len > 128) {
hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "...");
}
// Single-line debug output with packet info
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] %s: len=%zu hex=%s", prefix, len, hex_buf);
// Additional debug info for first few bytes
if (len >= 2) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[ETCP] %s: first_bytes=%02x%02x last_bytes=%02x%02x",
prefix, data[0], data[1], data[len-2], data[len-1]);
}
}
// Compact packet info for high-frequency logging
static void log_packet_compact(const char* prefix, const uint8_t* data, size_t len, struct ETCP_LINK* link) {
if (!data || len == 0) return;
// Extract packet type from first byte
uint8_t pkt_type = data[0];
uint16_t timestamp = 0;
if (len >= 3) {
// Extract timestamp from bytes 1-2 (big endian)
timestamp = (data[1] << 8) | data[2];
}
// Single line debug with key packet info
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] %s: link=%p type=0x%02x ts=%u len=%zu",
prefix, link, pkt_type, timestamp, len);
// Trace level for detailed hex dump
if (len <= 64) { // Only for small packets to avoid spam
char hex_buf[133]; // 64 bytes * 2 + 1
size_t hex_len = 0;
for (size_t i = 0; i < len && hex_len < sizeof(hex_buf) - 3; i++) {
hex_len += snprintf(hex_buf + hex_len, sizeof(hex_buf) - hex_len, "%02x", data[i]);
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[ETCP] %s: data=%s", prefix, hex_buf);
}
}
// Unified packet dump function - uses configured format
static void packet_dump(const char* prefix, const uint8_t* data, size_t len, struct ETCP_LINK* link) {
if (!data || len == 0) return;
#if ETCP_PACKET_DUMP_COMPACT
// Compact single-line format
log_packet_compact(prefix, data, len, link);
#else
// Full multi-line format (legacy)
dump_packet_bytes(prefix, data, len);
#endif
}
// Legacy multi-line dump for compatibility (kept but not used)
static void dump_packet_bytes_multiline(const char* prefix, const uint8_t* data, size_t len) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP DUMP] %s: len=%zu; dump: ", prefix, len);
size_t show = len < 160 ? len : 160;
for (size_t i = 0; i < show; i++) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "%02x ", data[i]);
}
if (len > 160) DEBUG_INFO(DEBUG_CATEGORY_ETCP, "...");
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "\n");
}
// Forward declarations for missing functions
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance);
// CONNECTION MANAGEMENT (!!!это всё должно быть static!!!)
static void etcp_link_remove_from_connections(struct ETCP_SOCKET* conn, struct ETCP_LINK* link);
// Отправка кодограмм протокола (!!!это всё должно быть static!!!)
static void etcp_link_send_init(struct ETCP_LINK* link);
static int etcp_link_send_reset(struct ETCP_LINK* link);
static void etcp_link_init_timer_cbk(void* arg);
#define INIT_TIMEOUT_INITIAL 500
#define INIT_TIMEOUT_MAX 50000
static void etcp_link_send_init(struct ETCP_LINK* link) {
if (!link || !link->etcp || !link->etcp->instance) return;
struct ETCP_DGRAM* dgram = malloc(sizeof(struct ETCP_DGRAM) + 100);
if (!dgram) return;
dgram->link = link;
dgram->noencrypt_len = SC_PUBKEY_SIZE;
size_t offset = 0;
dgram->data[offset++] = ETCP_INIT_REQUEST;
uint64_t node_id = link->etcp->instance->node_id;
dgram->data[offset++] = (node_id >> 56) & 0xFF;
dgram->data[offset++] = (node_id >> 48) & 0xFF;
dgram->data[offset++] = (node_id >> 40) & 0xFF;
dgram->data[offset++] = (node_id >> 32) & 0xFF;
dgram->data[offset++] = (node_id >> 24) & 0xFF;
dgram->data[offset++] = (node_id >> 16) & 0xFF;
dgram->data[offset++] = (node_id >> 8) & 0xFF;
dgram->data[offset++] = node_id & 0xFF;
dgram->data[offset++] = (link->mtu >> 8) & 0xFF;
dgram->data[offset++] = link->mtu & 0xFF;
dgram->data[offset++] = (link->keepalive_interval >> 8) & 0xFF;
dgram->data[offset++] = link->keepalive_interval & 0xFF;
memcpy(dgram->data + offset, link->etcp->instance->my_keys.public_key, SC_PUBKEY_SIZE);
dgram->data_len = offset + SC_PUBKEY_SIZE;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Sending INIT request to link, node_id=%llu, retry=%d", (unsigned long long)node_id, link->init_retry_count);
// Debug: print remote address before sending
if (link->remote_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&link->remote_addr;
char addr_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] INIT sending to %s:%d, link=%p, conn_fd=%d", addr_str, ntohs(sin->sin_port), link, link->conn->fd);
}
etcp_encrypt_send(dgram);
free(dgram);
link->init_retry_count++;
if (!link->init_timer && link->is_server == 0) {
link->init_timeout = INIT_TIMEOUT_INITIAL;
link->init_timer = uasync_set_timeout(link->etcp->instance->ua, link->init_timeout, link, etcp_link_init_timer_cbk);
} else if (link->init_timer) {
if ((link->init_retry_count % 10) == 0 && link->init_timeout < INIT_TIMEOUT_MAX) {
link->init_timeout *= 2;
if (link->init_timeout > INIT_TIMEOUT_MAX) link->init_timeout = INIT_TIMEOUT_MAX;
}
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer);
link->init_timer = uasync_set_timeout(link->etcp->instance->ua, link->init_timeout, link, etcp_link_init_timer_cbk);
}
}
static void etcp_link_init_timer_cbk(void* arg) {
struct ETCP_LINK* link = (struct ETCP_LINK*)arg;
if (!link || link->initialized || link->is_server != 0) return;
link->init_timer = NULL;
etcp_link_send_init(link);
}
static int etcp_link_send_reset(struct ETCP_LINK* link) {
if (!link) return -1;
struct ETCP_DGRAM* dgram = malloc(sizeof(struct ETCP_DGRAM) + 1);
if (!dgram) return -1;
dgram->link = link;
dgram->data_len = 1;
dgram->noencrypt_len = 0;
dgram->data[0] = 0x06;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Sending RESET to link");
int ret = etcp_encrypt_send(dgram);
free(dgram);
return ret;
}
static uint32_t sockaddr_hash(struct sockaddr_storage* addr) {
socklen_t addr_len = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
return crc32_calc((void*)addr, addr_len);
}
// Бинарный поиск линка по ip_port_hash
static int find_link_index(struct ETCP_SOCKET* e_sock, uint32_t hash) {
if (!e_sock || e_sock->num_channels == 0) return -1;
int left = 0;
int right = e_sock->num_channels - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (e_sock->links[mid]->ip_port_hash == hash) {
return mid;
} else if (e_sock->links[mid]->ip_port_hash < hash) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -(left + 1);
}
// Реалокация массива линков с увеличением в 2 раза
static int realloc_links(struct ETCP_SOCKET* e_sock) {
size_t new_max = e_sock->max_channels == 0 ? 8 : e_sock->max_channels * 2;
struct ETCP_LINK** new_links = realloc(e_sock->links, new_max * sizeof(struct ETCP_LINK*));
if (!new_links) return -1;
e_sock->links = new_links;
e_sock->max_channels = new_max;
return 0;
}
// Вставка линка в отсортированный массив
static int insert_link(struct ETCP_SOCKET* e_sock, struct ETCP_LINK* link) {
if (!e_sock || !link) return -1;
if (e_sock->num_channels >= e_sock->max_channels) {
if (realloc_links(e_sock) < 0) return -1;
}
int idx = find_link_index(e_sock, link->ip_port_hash);
if (idx >= 0) return -1;
idx = -(idx + 1);
if (idx < (int)e_sock->num_channels) {
memmove(&e_sock->links[idx + 1], &e_sock->links[idx],
(e_sock->num_channels - idx) * sizeof(struct ETCP_LINK*));
}
e_sock->links[idx] = link;
e_sock->num_channels++;
return 0;
}
// Удаление линка из массива
static void remove_link(struct ETCP_SOCKET* e_sock, uint32_t hash) {
if (!e_sock || e_sock->num_channels == 0) return;
int idx = find_link_index(e_sock, hash);
if (idx < 0) return;
if (idx < (int)e_sock->num_channels - 1) {
memmove(&e_sock->links[idx], &e_sock->links[idx + 1],
(e_sock->num_channels - idx - 1) * sizeof(struct ETCP_LINK*));
}
e_sock->num_channels--;
}
// надо править, используй sockaddr_hash
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_SOCKET* e_sock, struct sockaddr_storage* addr) {
if (!e_sock || !addr) return NULL;
int idx = find_link_index(e_sock, sockaddr_hash(addr));
if (idx < 0) return NULL;
return e_sock->links[idx];
}
// ===============================
struct ETCP_SOCKET* etcp_socket_add_ex(struct UTUN_INSTANCE* instance, struct sockaddr_storage* ip, uint32_t netif_index, int so_mark, uint8_t type, uint32_t flags) {
if (!instance) return NULL;
struct ETCP_SOCKET* e_sock = calloc(1, sizeof(struct ETCP_SOCKET));
if (!e_sock) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to allocate connection");
return NULL;
}
int family = AF_INET;
if (ip) {
family = ip->ss_family;
if (family != AF_INET && family != AF_INET6) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Unsupported address family: %d", family);
free(e_sock);
return NULL;
}
}
// Use test hook if available and in test mode
if ((flags & UTUN_CREATE_TEST_MODE) && g_utun_test_hooks && g_utun_test_hooks->socket_create_override) {
e_sock->fd = g_utun_test_hooks->socket_create_override(family, SOCK_DGRAM, 0,
g_utun_test_hooks->test_context);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Socket creation using test hook: fd=%d", e_sock->fd);
} else {
e_sock->fd = socket(family, SOCK_DGRAM, 0);
}
if (e_sock->fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to create socket: %s", strerror(errno));
free(e_sock);
return NULL;
}
int flags_fcntl = fcntl(e_sock->fd, F_GETFL, 0);
fcntl(e_sock->fd, F_SETFL, flags_fcntl | O_NONBLOCK);
// Set socket mark if specified
if (so_mark > 0) {
#ifdef SO_MARK
if (setsockopt(e_sock->fd, SOL_SOCKET, SO_MARK, &so_mark, sizeof(so_mark)) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to set SO_MARK: %s", strerror(errno));
}
#endif
}
// Bind to interface if specified
if (netif_index > 0) {
#ifdef SO_BINDTODEVICE
char ifname[IF_NAMESIZE];
if (if_indextoname(netif_index, ifname)) {
if (setsockopt(e_sock->fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to bind to interface %s: %s", ifname, strerror(errno));
}
}
#endif
}
// Store the local address and bind socket if provided
if (ip) {
memcpy(&e_sock->local_addr, ip, sizeof(struct sockaddr_storage));
// Skip binding if requested (for test injection)
if (!(flags & UTUN_CREATE_NO_SOCKET_BIND)) {
// CRITICAL: Actually bind the socket to the address - this was missing!
socklen_t addr_len = (ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
if (bind(e_sock->fd, (struct sockaddr*)ip, addr_len) < 0) {
perror("bind");
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] Failed to bind socket to address family %d", ip->ss_family);
if (ip->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)ip;
char addr_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN);
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] Failed to bind to %s:%d", addr_str, ntohs(sin->sin_port));
}
close(e_sock->fd);
free(e_sock);
return NULL;
}
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Successfully bound socket to local address, family=%d", ip->ss_family);
} else {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Socket binding skipped (NO_SOCKET_BIND flag)");
}
}
e_sock->instance = instance;
e_sock->errorcode = 0;
e_sock->pkt_format_errors = 0;
// Add to instance's socket list
e_sock->next = instance->etcp_sockets;
instance->etcp_sockets = e_sock;
// Register socket with uasync for receiving packets
e_sock->socket_id = uasync_add_socket(instance->ua, e_sock->fd,
etcp_connections_read_callback,
NULL, NULL, e_sock);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Registered ETCP socket with uasync (fd=%d)", e_sock->fd);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Socket %p (fd=%d) registered and active", e_sock, e_sock->fd);
return e_sock;
}
void etcp_socket_remove(struct ETCP_SOCKET* conn) {
if (!conn) return;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Removing socket %p, fd=%d, socket_id=%p", conn, conn->fd, conn->socket_id);
// Remove from uasync if registered
if (conn->socket_id) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Removing socket from uasync, instance=%p, ua=%p", conn->instance, conn->instance->ua);
uasync_remove_socket(conn->instance->ua, conn->socket_id);
conn->socket_id = NULL;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Unregistered socket from uasync");
}
if (conn->fd >= 0) {
close(conn->fd);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Closed fd=%d", conn->fd);
}
for (size_t i = 0; i < conn->num_channels; i++) {
etcp_link_close(conn->links[i]);
}
free(conn->links);
free(conn);
}
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn, struct sockaddr_storage* remote_addr, uint8_t is_server) {
if (!remote_addr) return NULL;
struct ETCP_LINK* link = calloc(1, sizeof(struct ETCP_LINK));
if (!link) return NULL;
link->conn = conn;
link->etcp = etcp;
link->is_server = is_server;
link->mtu = 1500;
link->keepalive_interval = 30;
link->initialized = 0;
link->init_timer = NULL;
link->init_timeout = 0;
link->init_retry_count = 0;
memcpy(&link->remote_addr, remote_addr, sizeof(struct sockaddr_storage));
link->last_activity = time(NULL);
link->ip_port_hash = sockaddr_hash(remote_addr);
insert_link(conn, link);
struct ETCP_LINK* l=etcp->links;
while (l && l->next) l=l->next;
if (l) l->next = link; else etcp->links = link;
if (is_server == 0) {
etcp_link_send_init(link);
}
return link;
}
void etcp_link_close(struct ETCP_LINK* link) {
if (!link || !link->etcp) return;
// Cancel init timer if active
if (link->init_timer) {
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer);
link->init_timer = NULL;
}
// универсальное удаление из односвязного списка
struct ETCP_LINK **pp = &link->etcp->links;
while (*pp) {
if (*pp == link) {
*pp = link->next;
break;
}
pp = &(*pp)->next;
}
remove_link(link->conn, link->ip_port_hash);
free(link);
}
int etcp_encrypt_send(struct ETCP_DGRAM* dgram) {
// printf("[ETCP DEBUG] etcp_encrypt_send: ENTERING FUNCTION\n");
int errcode=0;
sc_context_t* sc = &dgram->link->etcp->crypto_ctx;
int len=dgram->data_len-dgram->noencrypt_len;// не забываем добавить timestamp (2 bytes)
if (len<=0 || len>1480) { dgram->link->send_errors++; errcode=1; goto es_err; }
uint8_t enc_buf[1600];
size_t enc_buf_len=0;
dgram->timestamp=get_current_timestamp();
// DUMP: Show packet before encryption
packet_dump("ECTP_ENCRYPT_SEND", dgram->data, dgram->data_len, dgram->link);
sc_encrypt(sc, (uint8_t*)&dgram->timestamp/*не править это, тут верно!*/, sizeof(uint16_t) + len, enc_buf, &enc_buf_len);
if (enc_buf_len == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_encrypt_send: encryption failed for node %llu", (unsigned long long)dgram->link->etcp->instance->node_id);
dgram->link->send_errors++;
errcode=2;
goto es_err;
}
if (enc_buf_len + dgram->noencrypt_len > 1480) { dgram->link->send_errors++; errcode=2; goto es_err; }
memcpy(enc_buf+enc_buf_len, dgram->data+len, dgram->noencrypt_len);
// DUMP: Show complete packet before sending
// dump_packet_bytes("READY TO SEND", enc_buf, enc_buf_len + dgram->noencrypt_len);
struct sockaddr_storage* addr=&dgram->link->remote_addr;
socklen_t addr_len = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
// Debug: print where we're sending the packet
if (addr->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)addr;
char addr_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP] Sending packet to %s:%d, size=%zd", addr_str, ntohs(sin->sin_port), enc_buf_len + dgram->noencrypt_len);
}
ssize_t sent = utun_sendto_hook(dgram->link->conn->fd, enc_buf, enc_buf_len + dgram->noencrypt_len, 0, (struct sockaddr*)addr, addr_len);
if (sent < 0) { dgram->link->send_errors++; errcode=3; goto es_err;} else dgram->link->total_encrypted += sent;
return (int)sent;
es_err:
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] encrypt_send error %d", errcode);
return -1;
}
static void etcp_connections_read_callback(int fd, void* arg) {
// !!!!!! DANGER: в этой функции ПРЕДЕЛЬНАЯ АККУРАТНОСТЬ. Если кажется что не туда указатель то невнимательно аланизировал !!!!!
// НЕ РУИНИТЬ (uint8_t*)&pkt->timestamp - это правильно !!!!
//
// Ошибки функции (errorcode):
// 1 - пакет слишком маленький для init (< SC_PUBKEY_SIZE)
// 2 - не удалось установить peer public key при init
// 3 - не удалось расшифровать init пакет
// 4 - не init пакет (неверный код)
// 5 - коллизия peer ID и ключей
// 6 - не удалось расшифровать обычный пакет
// 13 - переполнение при парсинге пакета
// 46 - расшифрованный пакет слишком маленький (< 3 байта)
// 55 - не удалось создать подключение
// 66 - не удалось создать линк
struct ETCP_SOCKET* e_sock = (struct ETCP_SOCKET*)arg;
if (!e_sock) return;
// printf("[ETCP] Read callback triggered for fd=%d, socket=%p\n", fd, e_sock);
struct sockaddr_storage addr;
uint8_t data[PACKET_DATA_SIZE];
socklen_t addr_len=sizeof(addr);
memset(&addr, 0, sizeof(addr));
ssize_t recv_len = utun_recvfrom_hook(fd, data, PACKET_DATA_SIZE, 0, (struct sockaddr*)&addr, &addr_len);
if (recv_len <= 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[ETCP] recvfrom failed or no data, recv_len=%zd, errno=%d", recv_len, errno);
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: recvfrom failed, error=%zd, errno=%d", recv_len, errno);
return;
}
// printf("[ETCP] Received packet: %zd bytes from address\n", recv_len);
// DUMP: Show received packet content
packet_dump("RECV in:", data, recv_len, NULL); // link unknown at this point
struct ETCP_DGRAM* pkt = memory_pool_alloc(e_sock->instance->pkt_pool);
if (!pkt) return;
size_t pkt_len=0;
int errorcode=0;
struct ETCP_LINK* link=etcp_link_find_by_addr(e_sock, &addr);
// printf("[ETCP DEBUG] Received packet, link=%p, recv_len=%zd\n", link, recv_len);
if (link==NULL) {// пробуем расшифровать, возможно это init
// printf("[ETCP DEBUG] No existing link found, trying to decrypt as INIT packet\n");
struct secure_channel sc;
if (recv_len<=SC_PUBKEY_SIZE) { errorcode=1; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: packet too small for init, size=%zd", recv_len); goto ec_fr; }
sc_init_ctx(&sc, &e_sock->instance->my_keys);
// printf("[ETCP DEBUG] Extracting peer public key from position %ld, total packet size=%zd\n", recv_len-SC_PUBKEY_SIZE, recv_len);
// printf("[ETCP DEBUG] Last 64 bytes of packet (PUBKEY): ");
for (int i=0; i<SC_PUBKEY_SIZE; i++) DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "%02x ", data[recv_len-SC_PUBKEY_SIZE+i]);
if (sc_set_peer_public_key(&sc, &data[recv_len-SC_PUBKEY_SIZE], SC_PEER_PUBKEY_BIN)!=SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to set peer public key during init");
errorcode=2;
goto ec_fr;
}
if (sc_decrypt(&sc, data, recv_len-SC_PUBKEY_SIZE, (uint8_t*)&pkt->timestamp, &pkt_len)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt init packet");
errorcode=3;
goto ec_fr;
}
// printf("[ETCP DEBUG] Decrypt OK\n");
pkt->data_len=pkt_len-2;
pkt->noencrypt_len=0;
struct {
uint8_t code;
uint8_t id[8];
uint8_t mtu[2];
uint8_t keepalive[2];
uint8_t pubkey[SC_PUBKEY_SIZE];
} *ack_hdr=(void*)&pkt->data[0];
uint64_t peer_id;
memcpy(&peer_id, &ack_hdr->id[0], 8);
if (ack_hdr->code!=ETCP_INIT_REQUEST && ack_hdr->code!=ETCP_CHANNEL_INIT) { errorcode=4; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: not an init packet, code=%02x", ack_hdr->code); goto ec_fr; }// не init
struct ETCP_CONN* conn=e_sock->instance->connections;
while (conn) {// ищем есть ли подключение к этому пиру
if (conn->peer_node_id==peer_id) break;
conn=conn->next;
}
int new_conn=0;
if (!conn || conn->peer_node_id!=peer_id) {// создаём новое
new_conn=1;
conn=etcp_connection_create(e_sock->instance);
if (!conn) { errorcode=55; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connections_read_callback: failed to create connection"); goto ec_fr; }// облом
memcpy(&conn->crypto_ctx, &sc, sizeof(sc));// добавляем ключ
conn->peer_node_id=peer_id;
}
else {// check keys если существующее подключение
if (memcmp(conn->crypto_ctx.peer_public_key, sc.peer_public_key, SC_PUBKEY_SIZE)) { errorcode=5; DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: peer key mismatch for node %llu", (unsigned long long)peer_id); goto ec_fr; }// коллизия - peer id совпал а ключи разные.
}
struct ETCP_LINK* link = etcp_link_new(conn, e_sock, &addr, 1);
if (!link) { if (new_conn) etcp_connection_close(conn); errorcode=66; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connections_read_callback: failed to create link for connection"); goto ec_fr; }// облом
if (ack_hdr->code==0x02) etcp_conn_reset(conn);
struct {
uint8_t code;
uint8_t id[8];
uint8_t mtu[2];
} *ack_repl_hdr=(void*)&pkt->data[0];
ack_repl_hdr->code+=1;
memcpy(ack_repl_hdr->id, &e_sock->instance->node_id, 8);
int mtu=e_sock->instance->config->global.mtu;
ack_repl_hdr->mtu[0]=mtu>>8;
ack_repl_hdr->mtu[1]=mtu;
pkt->data_len=sizeof(*ack_repl_hdr);
pkt->noencrypt_len=0;
pkt->link=link;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[ETCP DEBUG] Send INIT RESPONSE");
etcp_encrypt_send(pkt);
// printf("[ETCP DEBUG] Send INIT RESPONSE ok\n");
memory_pool_free(e_sock->instance->pkt_pool, pkt);
return;
}
if (sc_decrypt(&link->etcp->crypto_ctx, data, recv_len, (uint8_t*)&pkt->timestamp, &pkt_len)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt packet from node %llu", (unsigned long long)link->etcp->instance->node_id);
errorcode=6;
goto ec_fr;
}
if (pkt_len<3) { errorcode=46; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: decrypted packet too small, size=%zu", pkt_len); goto ec_fr; }
pkt->data_len=pkt_len-2;
pkt->noencrypt_len=0;
pkt->link=link;
link->last_recv_local_time=get_current_time_units();
link->last_recv_timestamp=pkt->timestamp;
size_t offset = 0;
uint8_t code = pkt->data[offset++];
if (code == ETCP_INIT_RESPONSE || code == ETCP_CHANNEL_RESPONSE) {
// Parse response
if (code == ETCP_INIT_RESPONSE) etcp_conn_reset(link->etcp);
uint64_t server_node_id = 0;
for (int i = 0; i < 8; i++) {
server_node_id = (server_node_id << 8) | pkt->data[offset++];
}
link->mtu = (pkt->data[offset++] << 8) | pkt->data[offset++];
if (offset > pkt_len) { errorcode=13; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: packet parsing overflow, offset=%zu, pkt_len=%zu", offset, pkt_len); goto ec_fr; }
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Received INIT_RESPONSE from server_node_id=%llu, mtu=%d",
(unsigned long long)server_node_id, link->mtu);
link->etcp->peer_node_id = server_node_id; // If not set
// Mark link as initialized
link->initialized = 1;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[ETCP] Link initialized successfully! Server node_id=%llu, mtu=%d",
(unsigned long long)server_node_id, link->mtu);
// Cancel init timer if exists
if (link->init_timer) {
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer);
link->init_timer = NULL;
}
memory_pool_free(e_sock->instance->pkt_pool, pkt);
return; // INIT_RESPONSE is handled, no further processing needed
}
packet_dump("RECV decrypted:", pkt->data, pkt->data_len, link);
etcp_conn_input(pkt);
return;
ec_fr:
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: error %d", errorcode);
e_sock->pkt_format_errors++;
e_sock->errorcode=errorcode;
memory_pool_free(e_sock->instance->pkt_pool, pkt);
return;
}
int init_connections(struct UTUN_INSTANCE* instance) {
if (!instance || !instance->config) return -1;
struct utun_config* config = instance->config;
// Initialize servers first - create sockets for incoming connections
struct CFG_SERVER* server = config->servers;
while (server) {
// Create socket for this server
struct ETCP_SOCKET* e_sock = etcp_socket_add(instance, &server->ip, server->netif_index, server->so_mark, server->type);
if (!e_sock) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create socket for server %s", server->name);
server = server->next;
continue;
}
// Convert IP to string for logging
char addr_str[INET6_ADDRSTRLEN + 6];
if (server->ip.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&server->ip;
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN);
sprintf(addr_str + strlen(addr_str), ":%d", ntohs(sin->sin_port));
} else {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&server->ip;
inet_ntop(AF_INET6, &sin6->sin6_addr, addr_str, INET6_ADDRSTRLEN);
sprintf(addr_str + strlen(addr_str), ":%d", ntohs(sin6->sin6_port));
}
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized server %s on %s (links: %zu)",
server->name, addr_str, e_sock->num_channels);
server = server->next;
}
// Initialize clients - create outgoing connections
struct CFG_CLIENT* client = config->clients;
while (client) {
// Create ETCP connection for this client
struct ETCP_CONN* etcp_conn = etcp_connection_create(instance);
if (!etcp_conn) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create ETCP connection for client %s", client->name);
client = client->next;
continue;
}
// Initialize crypto context for this connection
if (sc_init_ctx(&etcp_conn->crypto_ctx, &instance->my_keys) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "init_connections: failed to initialize crypto context for client %s", client->name);
etcp_connection_close(etcp_conn);
client = client->next;
continue;
}
// If client has peer public key configured, set it
if (strlen(client->peer_public_key_hex) > 0) {
// For now, set peer node ID to indicate we have peer key
// The actual peer key will be exchanged during connection establishment
etcp_conn->peer_node_id = 1; // Simple indicator
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "init_connections: setting peer public key for client %s", client->name);
// Set peer public key (assuming hex format)
if (sc_set_peer_public_key(&etcp_conn->crypto_ctx, client->peer_public_key_hex, 1) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "init_connections: failed to set peer public key for client %s", client->name);
} else {
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "init_connections: successfully set peer public key for client %s", client->name);
}
} else {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "init_connections: no peer public key configured for client %s", client->name);
}
// Create links for this client
struct CFG_CLIENT_LINK* client_link = client->links;
while (client_link) {
// Find the local server for this link
struct CFG_SERVER* local_server = client_link->local_srv;
if (!local_server) {
client_link = client_link->next;
continue;
}
// Find the socket for this server
struct ETCP_SOCKET* e_sock = NULL;
struct ETCP_SOCKET* sock = instance->etcp_sockets;
while (sock) {
if (sock->local_addr.ss_family == local_server->ip.ss_family) {
if (sock->local_addr.ss_family == AF_INET) {
struct sockaddr_in* sock_addr = (struct sockaddr_in*)&sock->local_addr;
struct sockaddr_in* srv_addr = (struct sockaddr_in*)&local_server->ip;
if (sock_addr->sin_addr.s_addr == srv_addr->sin_addr.s_addr &&
sock_addr->sin_port == srv_addr->sin_port) {
e_sock = sock;
break;
}
}
}
sock = sock->next;
}
if (!e_sock) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "No socket found for client %s link", client->name);
client_link = client_link->next;
continue;
}
// Create link for this client connection
struct ETCP_LINK* link = etcp_link_new(etcp_conn, e_sock, &client_link->remote_addr, 0); // 0 = client initiates
if (!link) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create link for client %s", client->name);
client_link = client_link->next;
continue;
}
client_link = client_link->next;
}
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Added client %s with %d links", client->name, client->keepalive);
client = client->next;
}
// If there are clients configured but no connections created, that's an error
// If there are no clients (server-only mode), 0 connections is OK (server will accept incoming)
if (instance->connections_count == 0 && config->clients != NULL) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Clients configured but no connections initialized");
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized %d connections", instance->connections_count);
return 0;
}
// Wrapper for backward compatibility
struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct sockaddr_storage* ip, uint32_t netif_index, int so_mark, uint8_t type) {
return etcp_socket_add_ex(instance, ip, netif_index, so_mark, type, 0);
}

770
!/etcp_connections.c1

@ -1,770 +0,0 @@
#include "etcp_connections.h"
#include <arpa/inet.h>
#include <net/if.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "routing.h"
#include "utun_instance.h"
#include "config_parser.h"
#include "crc32.h"
#include "etcp.h"
#include "../lib/memory_pool.h"
#include "../lib/u_async.h"
#include <stdlib.h>
#include <time.h>
// Simple debug macros to replace missing debug_config.h
#define DEBUG_CATEGORY_CONNECTION 1
#define DEBUG_CATEGORY_ETCP 2
#define DEBUG_CATEGORY_CRYPTO 3
#define DEBUG_CATEGORY_CONFIG 4
#define DEBUG_CATEGORY_MEMORY 5
// Forward declaration
static void etcp_connections_read_callback(int fd, void* arg);
#define DEBUG_ERROR(category, fmt, ...) fprintf(stderr, "ERROR: " fmt "\n", ##__VA_ARGS__)
#define DEBUG_WARN(category, fmt, ...) fprintf(stderr, "WARN: " fmt "\n", ##__VA_ARGS__)
#define DEBUG_INFO(category, fmt, ...) fprintf(stdout, "INFO: " fmt "\n", ##__VA_ARGS__)
// Minimal packet dump - only shows first 16 bytes to avoid slowing down
static void dump_packet_bytes(const char* prefix, const uint8_t* data, size_t len) {
printf("[ETCP DUMP] %s: len=%zu; dump: ", prefix, len);
size_t show = len < 160 ? len : 160;
for (size_t i = 0; i < show; i++) {
printf("%02x ", data[i]);
}
if (len > 160) printf("...");
printf("\n");
}
// Forward declarations for missing functions
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance);
// CONNECTION MANAGEMENT (!!!это всё должно быть static!!!)
static void etcp_link_remove_from_connections(struct ETCP_SOCKET* conn, struct ETCP_LINK* link);
// Отправка кодограмм протокола (!!!это всё должно быть static!!!)
static void etcp_link_send_init(struct ETCP_LINK* link);
static int etcp_link_send_reset(struct ETCP_LINK* link);
static void etcp_link_init_timer_cbk(void* arg);
#define INIT_TIMEOUT_INITIAL 500
#define INIT_TIMEOUT_MAX 50000
static void etcp_link_send_init(struct ETCP_LINK* link) {
if (!link || !link->etcp || !link->etcp->instance) return;
struct ETCP_DGRAM* dgram = malloc(sizeof(struct ETCP_DGRAM) + 100);
if (!dgram) return;
dgram->link = link;
dgram->noencrypt_len = SC_PUBKEY_SIZE;
size_t offset = 0;
dgram->data[offset++] = ETCP_INIT_REQUEST;
uint64_t node_id = link->etcp->instance->node_id;
dgram->data[offset++] = (node_id >> 56) & 0xFF;
dgram->data[offset++] = (node_id >> 48) & 0xFF;
dgram->data[offset++] = (node_id >> 40) & 0xFF;
dgram->data[offset++] = (node_id >> 32) & 0xFF;
dgram->data[offset++] = (node_id >> 24) & 0xFF;
dgram->data[offset++] = (node_id >> 16) & 0xFF;
dgram->data[offset++] = (node_id >> 8) & 0xFF;
dgram->data[offset++] = node_id & 0xFF;
dgram->data[offset++] = (link->mtu >> 8) & 0xFF;
dgram->data[offset++] = link->mtu & 0xFF;
dgram->data[offset++] = (link->keepalive_interval >> 8) & 0xFF;
dgram->data[offset++] = link->keepalive_interval & 0xFF;
memcpy(dgram->data + offset, link->etcp->instance->my_keys.public_key, SC_PUBKEY_SIZE);
dgram->data_len = offset + SC_PUBKEY_SIZE;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Sending INIT request to link, node_id=%llu, retry=%d", (unsigned long long)node_id, link->init_retry_count);
// Debug: print remote address before sending
if (link->remote_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&link->remote_addr;
char addr_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN);
printf("[ETCP] INIT sending to %s:%d, link=%p, conn_fd=%d\n", addr_str, ntohs(sin->sin_port), link, link->conn->fd);
}
etcp_encrypt_send(dgram);
free(dgram);
link->init_retry_count++;
if (!link->init_timer && link->is_server == 0) {
link->init_timeout = INIT_TIMEOUT_INITIAL;
link->init_timer = uasync_set_timeout(link->etcp->instance->ua, link->init_timeout, link, etcp_link_init_timer_cbk);
} else if (link->init_timer) {
if ((link->init_retry_count % 10) == 0 && link->init_timeout < INIT_TIMEOUT_MAX) {
link->init_timeout *= 2;
if (link->init_timeout > INIT_TIMEOUT_MAX) link->init_timeout = INIT_TIMEOUT_MAX;
}
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer);
link->init_timer = uasync_set_timeout(link->etcp->instance->ua, link->init_timeout, link, etcp_link_init_timer_cbk);
}
}
static void etcp_link_init_timer_cbk(void* arg) {
struct ETCP_LINK* link = (struct ETCP_LINK*)arg;
if (!link || link->initialized || link->is_server != 0) return;
link->init_timer = NULL;
etcp_link_send_init(link);
}
static int etcp_link_send_reset(struct ETCP_LINK* link) {
if (!link) return -1;
struct ETCP_DGRAM* dgram = malloc(sizeof(struct ETCP_DGRAM) + 1);
if (!dgram) return -1;
dgram->link = link;
dgram->data_len = 1;
dgram->noencrypt_len = 0;
dgram->data[0] = 0x06;
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Sending RESET to link");
int ret = etcp_encrypt_send(dgram);
free(dgram);
return ret;
}
static uint32_t sockaddr_hash(struct sockaddr_storage* addr) {
socklen_t addr_len = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
return crc32_calc((void*)addr, addr_len);
}
// Бинарный поиск линка по ip_port_hash
static int find_link_index(struct ETCP_SOCKET* e_sock, uint32_t hash) {
if (!e_sock || e_sock->num_channels == 0) return -1;
int left = 0;
int right = e_sock->num_channels - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (e_sock->links[mid]->ip_port_hash == hash) {
return mid;
} else if (e_sock->links[mid]->ip_port_hash < hash) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -(left + 1);
}
// Реалокация массива линков с увеличением в 2 раза
static int realloc_links(struct ETCP_SOCKET* e_sock) {
size_t new_max = e_sock->max_channels == 0 ? 8 : e_sock->max_channels * 2;
struct ETCP_LINK** new_links = realloc(e_sock->links, new_max * sizeof(struct ETCP_LINK*));
if (!new_links) return -1;
e_sock->links = new_links;
e_sock->max_channels = new_max;
return 0;
}
// Вставка линка в отсортированный массив
static int insert_link(struct ETCP_SOCKET* e_sock, struct ETCP_LINK* link) {
if (!e_sock || !link) return -1;
if (e_sock->num_channels >= e_sock->max_channels) {
if (realloc_links(e_sock) < 0) return -1;
}
int idx = find_link_index(e_sock, link->ip_port_hash);
if (idx >= 0) return -1;
idx = -(idx + 1);
if (idx < (int)e_sock->num_channels) {
memmove(&e_sock->links[idx + 1], &e_sock->links[idx],
(e_sock->num_channels - idx) * sizeof(struct ETCP_LINK*));
}
e_sock->links[idx] = link;
e_sock->num_channels++;
return 0;
}
// Удаление линка из массива
static void remove_link(struct ETCP_SOCKET* e_sock, uint32_t hash) {
if (!e_sock || e_sock->num_channels == 0) return;
int idx = find_link_index(e_sock, hash);
if (idx < 0) return;
if (idx < (int)e_sock->num_channels - 1) {
memmove(&e_sock->links[idx], &e_sock->links[idx + 1],
(e_sock->num_channels - idx - 1) * sizeof(struct ETCP_LINK*));
}
e_sock->num_channels--;
}
// надо править, используй sockaddr_hash
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_SOCKET* e_sock, struct sockaddr_storage* addr) {
if (!e_sock || !addr) return NULL;
int idx = find_link_index(e_sock, sockaddr_hash(addr));
if (idx < 0) return NULL;
return e_sock->links[idx];
}
// ===============================
struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct sockaddr_storage* ip, uint32_t netif_index, int so_mark, uint8_t type) {
if (!instance) return NULL;
struct ETCP_SOCKET* e_sock = calloc(1, sizeof(struct ETCP_SOCKET));
if (!e_sock) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to allocate connection");
return NULL;
}
int family = AF_INET;
if (ip) {
family = ip->ss_family;
if (family != AF_INET && family != AF_INET6) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Unsupported address family: %d", family);
free(e_sock);
return NULL;
}
}
e_sock->fd = socket(family, SOCK_DGRAM, 0);
if (e_sock->fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to create socket: %s", strerror(errno));
free(e_sock);
return NULL;
}
int flags = fcntl(e_sock->fd, F_GETFL, 0);
fcntl(e_sock->fd, F_SETFL, flags | O_NONBLOCK);
// Set socket mark if specified
if (so_mark > 0) {
#ifdef SO_MARK
if (setsockopt(e_sock->fd, SOL_SOCKET, SO_MARK, &so_mark, sizeof(so_mark)) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to set SO_MARK: %s", strerror(errno));
}
#endif
}
// Bind to interface if specified
if (netif_index > 0) {
#ifdef SO_BINDTODEVICE
char ifname[IF_NAMESIZE];
if (if_indextoname(netif_index, ifname)) {
if (setsockopt(e_sock->fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to bind to interface %s: %s", ifname, strerror(errno));
}
}
#endif
}
// Store the local address and bind socket if provided
if (ip) {
memcpy(&e_sock->local_addr, ip, sizeof(struct sockaddr_storage));
// CRITICAL: Actually bind the socket to the address - this was missing!
socklen_t addr_len = (ip->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
if (bind(e_sock->fd, (struct sockaddr*)ip, addr_len) < 0) {
perror("bind");
printf("[ETCP] Failed to bind socket to address family %d\n", ip->ss_family);
if (ip->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)ip;
char addr_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN);
printf("[ETCP] Failed to bind to %s:%d\n", addr_str, ntohs(sin->sin_port));
}
close(e_sock->fd);
free(e_sock);
return NULL;
}
printf("[ETCP] Successfully bound socket to local address, family=%d\n", ip->ss_family);
}
e_sock->instance = instance;
e_sock->errorcode = 0;
e_sock->pkt_format_errors = 0;
// Add to instance's socket list
e_sock->next = instance->etcp_sockets;
instance->etcp_sockets = e_sock;
// Register socket with uasync for receiving packets
e_sock->socket_id = uasync_add_socket(instance->ua, e_sock->fd,
etcp_connections_read_callback,
NULL, NULL, e_sock);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Registered ETCP socket with uasync (fd=%d)", e_sock->fd);
printf("[ETCP] Socket %p (fd=%d) registered and active\n", e_sock, e_sock->fd);
return e_sock;
}
void etcp_socket_remove(struct ETCP_SOCKET* conn) {
if (!conn) return;
printf("[ETCP] Removing socket %p, fd=%d\n", conn, conn->fd);
if (conn->fd >= 0) {
close(conn->fd);
printf("[ETCP] Closed fd=%d\n", conn->fd);
}
for (size_t i = 0; i < conn->num_channels; i++) {
etcp_link_close(conn->links[i]);
}
free(conn->links);
free(conn);
}
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn, struct sockaddr_storage* remote_addr, uint8_t is_server) {
if (!remote_addr) return NULL;
struct ETCP_LINK* link = calloc(1, sizeof(struct ETCP_LINK));
if (!link) return NULL;
link->conn = conn;
link->etcp = etcp;
link->is_server = is_server;
link->mtu = 1500;
link->keepalive_interval = 30;
link->initialized = 0;
link->init_timer = NULL;
link->init_timeout = 0;
link->init_retry_count = 0;
memcpy(&link->remote_addr, remote_addr, sizeof(struct sockaddr_storage));
link->last_activity = time(NULL);
link->ip_port_hash = sockaddr_hash(remote_addr);
insert_link(conn, link);
struct ETCP_LINK* l=etcp->links;
while (l && l->next) l=l->next;
if (l) l->next = link; else etcp->links = link;
if (is_server == 0) {
etcp_link_send_init(link);
}
return link;
}
void etcp_link_close(struct ETCP_LINK* link) {
if (!link || !link->etcp) return;
// Cancel init timer if active
if (link->init_timer) {
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer);
link->init_timer = NULL;
}
// универсальное удаление из односвязного списка
struct ETCP_LINK **pp = &link->etcp->links;
while (*pp) {
if (*pp == link) {
*pp = link->next;
break;
}
pp = &(*pp)->next;
}
remove_link(link->conn, link->ip_port_hash);
free(link);
}
int etcp_encrypt_send(struct ETCP_DGRAM* dgram) {
// printf("[ETCP DEBUG] etcp_encrypt_send: ENTERING FUNCTION\n");
int errcode=0;
sc_context_t* sc = &dgram->link->etcp->crypto_ctx;
int len=dgram->data_len-dgram->noencrypt_len;// не забываем добавить timestamp (2 bytes)
if (len<=0 || len>1480) { dgram->link->send_errors++; errcode=1; goto es_err; }
uint8_t enc_buf[1600];
size_t enc_buf_len=0;
dgram->timestamp=get_current_timestamp();
// DUMP: Show packet before encryption
dump_packet_bytes("ECTP_ENCRYPT_SEND", dgram->data, dgram->data_len);
sc_encrypt(sc, (uint8_t*)&dgram->timestamp/*нейронка не правь блять это, тут верно!*/, sizeof(uint16_t) + len, enc_buf, &enc_buf_len);
if (enc_buf_len == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_encrypt_send: encryption failed for node %llu", (unsigned long long)dgram->link->etcp->instance->node_id);
dgram->link->send_errors++;
errcode=2;
goto es_err;
}
if (enc_buf_len + dgram->noencrypt_len > 1480) { dgram->link->send_errors++; errcode=2; goto es_err; }
memcpy(enc_buf+enc_buf_len, dgram->data+len, dgram->noencrypt_len);
// DUMP: Show complete packet before sending
// dump_packet_bytes("READY TO SEND", enc_buf, enc_buf_len + dgram->noencrypt_len);
struct sockaddr_storage* addr=&dgram->link->remote_addr;
socklen_t addr_len = (addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
// Debug: print where we're sending the packet
if (addr->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)addr;
char addr_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN);
printf("[ETCP] Sending packet to %s:%d, size=%zd\n", addr_str, ntohs(sin->sin_port), enc_buf_len + dgram->noencrypt_len);
}
ssize_t sent = sendto(dgram->link->conn->fd, enc_buf, enc_buf_len + dgram->noencrypt_len, 0, (struct sockaddr*)addr, addr_len);
if (sent < 0) { dgram->link->send_errors++; errcode=3; goto es_err;} else dgram->link->total_encrypted += sent;
return (int)sent;
es_err:
printf("[ETCP] encrypt_send error %d\n", errcode);
return -1;
}
static void etcp_connections_read_callback(int fd, void* arg) {
// !!!!!! DANGER: в этой функции ПРЕДЕЛЬНАЯ АККУРАТНОСТЬ. Если кажется что не туда указатель то невнимательно аланизировал !!!!!
// НЕ РУИНИТЬ (uint8_t*)&pkt->timestamp - это правильно !!!!
struct ETCP_SOCKET* e_sock = (struct ETCP_SOCKET*)arg;
if (!e_sock) return;
// printf("[ETCP] Read callback triggered for fd=%d, socket=%p\n", fd, e_sock);
struct sockaddr_storage addr;
uint8_t data[PACKET_DATA_SIZE];
socklen_t addr_len=sizeof(addr);
memset(&addr, 0, sizeof(addr));
ssize_t recv_len = recvfrom(fd, data, PACKET_DATA_SIZE, 0, (struct sockaddr*)&addr, &addr_len);
if (recv_len <= 0) {
printf("[ETCP] recvfrom failed or no data, recv_len=%zd, errno=%d\n", recv_len, errno);
return;
}
// printf("[ETCP] Received packet: %zd bytes from address\n", recv_len);
// DUMP: Show received packet content
dump_packet_bytes("RECV in:", data, recv_len);
struct ETCP_DGRAM* pkt = memory_pool_alloc(e_sock->instance->pkt_pool);
if (!pkt) return;
size_t pkt_len=0;
int errorcode=0;
struct ETCP_LINK* link=etcp_link_find_by_addr(e_sock, &addr);
// printf("[ETCP DEBUG] Received packet, link=%p, recv_len=%zd\n", link, recv_len);
if (link==NULL) {// пробуем расшифровать, возможно это init
// printf("[ETCP DEBUG] No existing link found, trying to decrypt as INIT packet\n");
struct secure_channel sc;
if (recv_len<=SC_PUBKEY_SIZE) { errorcode=1; goto ec_fr; }
sc_init_ctx(&sc, &e_sock->instance->my_keys);
// printf("[ETCP DEBUG] Extracting peer public key from position %ld, total packet size=%zd\n", recv_len-SC_PUBKEY_SIZE, recv_len);
// printf("[ETCP DEBUG] Last 64 bytes of packet (PUBKEY): ");
for (int i=0; i<SC_PUBKEY_SIZE; i++) printf("%02x ", data[recv_len-SC_PUBKEY_SIZE+i]);
if (sc_set_peer_public_key(&sc, &data[recv_len-SC_PUBKEY_SIZE], SC_PEER_PUBKEY_BIN)!=SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to set peer public key during init");
errorcode=2;
goto ec_fr;
}
if (sc_decrypt(&sc, data, recv_len-SC_PUBKEY_SIZE, (uint8_t*)&pkt->timestamp, &pkt_len)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt init packet");
errorcode=3;
goto ec_fr;
}
// printf("[ETCP DEBUG] Decrypt OK\n");
pkt->data_len=pkt_len-2;
pkt->noencrypt_len=0;
struct {
uint8_t code;
uint8_t id[8];
uint8_t mtu[2];
uint8_t keepalive[2];
uint8_t pubkey[SC_PUBKEY_SIZE];
} *ack_hdr=(void*)&pkt->data[0];
uint64_t peer_id;
memcpy(&peer_id, &ack_hdr->id[0], 8);
if (ack_hdr->code!=ETCP_INIT_REQUEST && ack_hdr->code!=ETCP_CHANNEL_INIT) { errorcode=4; goto ec_fr; }// не init
struct ETCP_CONN* conn=e_sock->instance->connections;
while (conn) {// ищем есть ли подключение к этому пиру
if (conn->peer_node_id==peer_id) break;
conn=conn->next;
}
int new_conn=0;
if (!conn || conn->peer_node_id!=peer_id) {// создаём новое
new_conn=1;
conn=etcp_connection_create(e_sock->instance);
if (!conn) { errorcode=55; goto ec_fr; }// облом
memcpy(&conn->crypto_ctx, &sc, sizeof(sc));// добавляем ключ
conn->peer_node_id=peer_id;
}
else {// check keys если существующее подключение
if (memcmp(conn->crypto_ctx.peer_public_key, sc.peer_public_key, SC_PUBKEY_SIZE)) { errorcode=5; goto ec_fr; }// коллизия - peer id совпал а ключи разные.
}
struct ETCP_LINK* link = etcp_link_new(conn, e_sock, &addr, 1);
if (!link) { if (new_conn) etcp_connection_close(conn); errorcode=66; goto ec_fr; }// облом
if (ack_hdr->code==0x02) etcp_conn_reset(conn);
struct {
uint8_t code;
uint8_t id[8];
uint8_t mtu[2];
} *ack_repl_hdr=(void*)&pkt->data[0];
ack_repl_hdr->code+=1;
memcpy(ack_repl_hdr->id, &e_sock->instance->node_id, 8);
int mtu=e_sock->instance->config->global.mtu;
ack_repl_hdr->mtu[0]=mtu>>8;
ack_repl_hdr->mtu[1]=mtu;
pkt->data_len=sizeof(*ack_repl_hdr);
pkt->noencrypt_len=0;
pkt->link=link;
printf("[ETCP DEBUG] Send INIT RESPONSE\n");
etcp_encrypt_send(pkt);
// printf("[ETCP DEBUG] Send INIT RESPONSE ok\n");
memory_pool_free(e_sock->instance->pkt_pool, pkt);
return;
}
if (sc_decrypt(&link->etcp->crypto_ctx, data, recv_len, (uint8_t*)&pkt->timestamp, &pkt_len)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: failed to decrypt packet from node %llu", (unsigned long long)link->etcp->instance->node_id);
errorcode=6;
goto ec_fr;
}
if (pkt_len<3) { errorcode=46; goto ec_fr; }
pkt->data_len=pkt_len-2;
pkt->noencrypt_len=0;
pkt->link=link;
link->last_recv_local_time=get_current_time_units();
link->last_recv_timestamp=pkt->timestamp;
size_t offset = 0;
uint8_t code = pkt->data[offset++];
if (code == ETCP_INIT_RESPONSE || code == ETCP_CHANNEL_RESPONSE) {
// Parse response
if (code == ETCP_INIT_RESPONSE) etcp_conn_reset(link->etcp);
uint64_t server_node_id = 0;
for (int i = 0; i < 8; i++) {
server_node_id = (server_node_id << 8) | pkt->data[offset++];
}
link->mtu = (pkt->data[offset++] << 8) | pkt->data[offset++];
if (offset > pkt_len) { errorcode=13; goto ec_fr; }
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Received INIT_RESPONSE from server_node_id=%llu, mtu=%d",
(unsigned long long)server_node_id, link->mtu);
link->etcp->peer_node_id = server_node_id; // If not set
// Mark link as initialized
link->initialized = 1;
printf("[ETCP] Link initialized successfully! Server node_id=%llu, mtu=%d\n",
(unsigned long long)server_node_id, link->mtu);
// Cancel init timer if exists
if (link->init_timer) {
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer);
link->init_timer = NULL;
}
memory_pool_free(e_sock->instance->pkt_pool, pkt);
return; // INIT_RESPONSE is handled, no further processing needed
}
dump_packet_bytes("RECV decrypted:", pkt->data, pkt->data_len);
etcp_conn_input(pkt);
return;
ec_fr:
printf("etcp_connections_read_callback: error %d\n", errorcode);
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "etcp_connections_read_callback: error %d", errorcode);
e_sock->pkt_format_errors++;
e_sock->errorcode=errorcode;
memory_pool_free(e_sock->instance->pkt_pool, pkt);
return;
}
int init_connections(struct UTUN_INSTANCE* instance) {
if (!instance || !instance->config) return -1;
struct utun_config* config = instance->config;
// Initialize servers first - create sockets for incoming connections
struct CFG_SERVER* server = config->servers;
while (server) {
// Create socket for this server
struct ETCP_SOCKET* e_sock = etcp_socket_add(instance, &server->ip, server->netif_index, server->so_mark, server->type);
if (!e_sock) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create socket for server %s", server->name);
server = server->next;
continue;
}
// Convert IP to string for logging
char addr_str[INET6_ADDRSTRLEN + 6];
if (server->ip.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&server->ip;
inet_ntop(AF_INET, &sin->sin_addr, addr_str, INET_ADDRSTRLEN);
sprintf(addr_str + strlen(addr_str), ":%d", ntohs(sin->sin_port));
} else {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&server->ip;
inet_ntop(AF_INET6, &sin6->sin6_addr, addr_str, INET6_ADDRSTRLEN);
sprintf(addr_str + strlen(addr_str), ":%d", ntohs(sin6->sin6_port));
}
printf("Initialized server %s on %s (links: %zu)\n",
server->name, addr_str, e_sock->num_channels);
server = server->next;
}
// Initialize clients - create outgoing connections
struct CFG_CLIENT* client = config->clients;
while (client) {
// Create ETCP connection for this client
struct ETCP_CONN* etcp_conn = etcp_connection_create(instance);
if (!etcp_conn) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create ETCP connection for client %s", client->name);
client = client->next;
continue;
}
// Initialize crypto context for this connection
if (sc_init_ctx(&etcp_conn->crypto_ctx, &instance->my_keys) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "init_connections: failed to initialize crypto context for client %s", client->name);
etcp_connection_close(etcp_conn);
client = client->next;
continue;
}
// If client has peer public key configured, set it
if (strlen(client->peer_public_key_hex) > 0) {
// For now, set peer node ID to indicate we have peer key
// The actual peer key will be exchanged during connection establishment
etcp_conn->peer_node_id = 1; // Simple indicator
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "init_connections: setting peer public key for client %s", client->name);
// Set peer public key (assuming hex format)
if (sc_set_peer_public_key(&etcp_conn->crypto_ctx, client->peer_public_key_hex, 1) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "init_connections: failed to set peer public key for client %s", client->name);
} else {
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "init_connections: successfully set peer public key for client %s", client->name);
}
} else {
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "init_connections: no peer public key configured for client %s", client->name);
}
// Create links for this client
struct CFG_CLIENT_LINK* client_link = client->links;
while (client_link) {
// Find the local server for this link
struct CFG_SERVER* local_server = client_link->local_srv;
if (!local_server) {
client_link = client_link->next;
continue;
}
// Find the socket for this server
struct ETCP_SOCKET* e_sock = NULL;
struct ETCP_SOCKET* sock = instance->etcp_sockets;
while (sock) {
if (sock->local_addr.ss_family == local_server->ip.ss_family) {
if (sock->local_addr.ss_family == AF_INET) {
struct sockaddr_in* sock_addr = (struct sockaddr_in*)&sock->local_addr;
struct sockaddr_in* srv_addr = (struct sockaddr_in*)&local_server->ip;
if (sock_addr->sin_addr.s_addr == srv_addr->sin_addr.s_addr &&
sock_addr->sin_port == srv_addr->sin_port) {
e_sock = sock;
break;
}
}
}
sock = sock->next;
}
if (!e_sock) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "No socket found for client %s link", client->name);
client_link = client_link->next;
continue;
}
// Create link for this client connection
struct ETCP_LINK* link = etcp_link_new(etcp_conn, e_sock, &client_link->remote_addr, 0); // 0 = client initiates
if (!link) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create link for client %s", client->name);
client_link = client_link->next;
continue;
}
client_link = client_link->next;
}
printf("Added client %s with %d links\n", client->name, client->keepalive);
client = client->next;
}
// If there are clients configured but no connections created, that's an error
// If there are no clients (server-only mode), 0 connections is OK (server will accept incoming)
if (instance->connections_count == 0 && config->clients != NULL) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Clients configured but no connections initialized");
return -1;
}
printf("Initialized %d connections\n", instance->connections_count);
return 0;
}
int etcp_connections_send(struct ETCP_SOCKET* e_sock, uint8_t* data, size_t len, struct sockaddr* addr, socklen_t addr_len) {
if (!e_sock || !data || !addr || len == 0) return -1;
struct sockaddr_storage remote_addr;
memcpy(&remote_addr, addr, addr_len);
struct ETCP_LINK* link = etcp_link_find_by_addr(e_sock, &remote_addr);
if (!link) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "No link found for address");
return -1;
}
if (!link->initialized && link->is_server == 0) {
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Link not initialized, triggering connection establishment");
if (!link->init_timer) {
etcp_link_send_init(link);
}
return -1;
}
struct ETCP_DGRAM* dgram = malloc(sizeof(struct ETCP_DGRAM) + len);
if (!dgram) return -1;
dgram->link = link;
dgram->data_len = len;
dgram->noencrypt_len = 0;
memcpy(dgram->data, data, len);
int ret = etcp_encrypt_send(dgram);
free(dgram);
return ret;
}

100
!/etcp_connections.h

@ -1,100 +0,0 @@
#ifndef ETCP_CONNECTIONS_H
#define ETCP_CONNECTIONS_H
// подмодуль ETCP который обслуживает сокеты ETCP для приёма-передачи пакетов и одно ETCP подключение через несколько каналов связи (failover)
#include "secure_channel.h"
#include "utun_instance.h"
#include <stdint.h>
#include <sys/socket.h>
#define PACKET_DATA_SIZE 1536
// Типы кодограмм протокола
#define ETCP_INIT_REQUEST 0x02
#define ETCP_INIT_RESPONSE 0x03
#define ETCP_CHANNEL_INIT 0x04
#define ETCP_CHANNEL_RESPONSE 0x05
struct ETCP_DGRAM {// пакет (незашифрованный)
struct ETCP_LINK* link;// откуда получена или куда отправялем
uint16_t data_len;// общий размер пакета не включая timestamp
uint16_t noencrypt_len;// число байт (с конца) которые не надо шифровать. для передачи pubkey
uint16_t timestamp;// timestamp принятого или для отправки
uint8_t data[0];// данные пакета (без timestamp)
};
// список активных подключений которые обслуживает сокет. каждый сокет может обслуживать много подключений
struct ETCP_SOCKET {
struct ETCP_SOCKET* next; // Linked list для всех соединений
struct UTUN_INSTANCE* instance;
int fd; // Файловый дескриптор UDP сокета
struct sockaddr_storage local_addr; // Локальный адрес
// для входящих подключений (links) - массив упорядоченный по ip_port_hash
size_t max_channels; // сколько выделено памяти
size_t num_channels; // сколько активно
struct ETCP_LINK** links;// массив указателей на линки, сортированный по ip_port_hash
int errorcode;
size_t pkt_format_errors;
void* socket_id; // Socket ID from uasync_add_socket
};
// ETCP Link - одно динамическое соединение (один путь)
struct ETCP_LINK {
uint32_t ip_port_hash; // crc32 для быстрого поиска
struct ETCP_LINK* next; // Linked list подключений для ETCP_CONN (каждое подключение это child для ETCP_CONN)
struct ETCP_CONN* etcp; // подключение (parent)
struct ETCP_SOCKET* conn; // сокет через который работаем
// Путь соединения
struct sockaddr_storage remote_addr; // Удалённый адрес
// Параметры соединения
uint16_t mtu; // MTU удаленного узла
uint16_t keepalive_interval; // Keepalive интервал
uint8_t is_server; // инициирует подключение клиент
uint8_t initialized; // Флаг инициализации (1=подтверждено или получен request)
// Состояние установки соединения (только для клиентов)
void* init_timer; // Таймер для повторов INIT (NULL=не подключается)
uint16_t init_timeout; // Текущий таймаут в мс
uint16_t init_retry_count; // Счетчик попыток
uint64_t last_activity; // Время последней активности
uint64_t last_recv_local_time;
uint16_t last_recv_timestamp;
size_t encrypt_errors;
size_t decrypt_errors;
size_t send_errors;
size_t recv_errors;
size_t total_encrypted;
size_t total_decrypted;
uint32_t bandwidth; // Link bandwidth in bits/sec
};
// INITIALIZATION (создаёт listen-сокеты и подключения из конфига)
int init_connections(struct UTUN_INSTANCE* instance);
// SOCKET FUNCTIONS
// добавляет новый версер (сокет для приёма и отправки кодограмм. обслуживает много подключений)
struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct sockaddr_storage* ip, uint32_t netif_index, int so_mark, uint8_t type);
struct ETCP_SOCKET* etcp_socket_add_ex(struct UTUN_INSTANCE* instance, struct sockaddr_storage* ip, uint32_t netif_index, int so_mark, uint8_t type, uint32_t flags);
// удаляет сокет и освобождает ресурсы (грохает все его подключения и сокет)
void etcp_socket_remove(struct ETCP_SOCKET* conn);
// connection functions
// создает новый канал связи для etcp подключения (ETCP_CONN)
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn, struct sockaddr_storage* remote_addr, uint8_t is_server);
void etcp_link_close(struct ETCP_LINK* link);
//int etcp_input_cbk(struct packet_buffer* pkt, struct ETCP_SOCKET* conn);// получает расшифрованный пакет
int etcp_encrypt_send(struct ETCP_DGRAM* dgram);// зашифровывает и отправляет пакет
// find link by address
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_SOCKET* e_sock, struct sockaddr_storage* addr);
#endif // ETCP_CONNECTIONS_H

164
!/etcp_loadbalancer.c

@ -1,164 +0,0 @@
// etcp_loadbalancer.c - Load Balancer Implementation (based on etcp_protocol.txt)
#include "etcp_loadbalancer.h"
#include "../lib/debug_config.h"
#include "../lib/u_async.h"
#include <stdlib.h>
// Enable comprehensive debug output for loadbalancer module
#define DEBUG_CATEGORY_LOADBALANCER 1
// Forward declaration
static void init_timeout_cb(void* arg);
// Constants
#define TIMEBASE_NS 100000 // 0.1ms = 100us = 100000ns
#define DELTA_TIME_NS 10000 // Example delta
// Internal
static uint64_t get_current_nanotime() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
uint64_t nanotime = (uint64_t)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "get_current_nanotime: tv_sec=%ld, tv_nsec=%ld, result=%llu",
ts.tv_sec, ts.tv_nsec, (unsigned long long)nanotime);
return nanotime;
}
// Select link for transmission (per spec)
struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: etcp=%p", etcp);
if (!etcp || !etcp->links) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: invalid parameters (etcp=%p, links=%p)",
etcp, etcp ? etcp->links : NULL);
return NULL;
}
struct ETCP_LINK* best = NULL;
uint64_t min_load_time = UINT64_MAX;
uint64_t now_ns = get_current_nanotime();
uint64_t now_tb = now_ns / (TIMEBASE_NS / 10); // To 0.1ms units
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: current time=%llu tb, scanning %d links",
(unsigned long long)now_tb, etcp->links ? 1 : 0);
struct ETCP_LINK* link = etcp->links;
int link_index = 0;
while (link) {
link_index++;
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: evaluating link %d (%p) - initialized=%u, is_server=%u, bandwidth=%u",
link_index, link, link->initialized, link->is_server, link->bandwidth);
if (!link->initialized) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d not initialized, checking if client connection needed",
link_index);
// Initiate connection if client
if (!link->is_server && !link->init_timer) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: initiating client connection for link %d", link_index);
// Send INIT (via etcp_encrypt_send with special dgram)
// Set timer
link->init_timer = uasync_set_timeout(etcp->instance->ua, link->init_timeout, link, init_timeout_cb); // Define cb
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: set init timer for link %d", link_index);
}
link = link->next;
continue;
}
// Update load time if inactive
if (link->last_activity < now_tb - (DELTA_TIME_NS / (TIMEBASE_NS / 10))) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d inactive, updating last_activity from %llu to %llu",
link_index, (unsigned long long)link->last_activity,
(unsigned long long)(now_tb - (DELTA_TIME_NS / (TIMEBASE_NS / 10))));
link->last_activity = now_tb - (DELTA_TIME_NS / (TIMEBASE_NS / 10));
}
// Check if can send (load time < now)
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d last_activity=%llu, now_tb=%llu",
link_index, (unsigned long long)link->last_activity, (unsigned long long)now_tb);
if (link->last_activity < now_tb) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d can send (load_time < now)", link_index);
if (link->last_activity < min_load_time) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d is better candidate (activity=%llu < %llu)",
link_index, (unsigned long long)link->last_activity, (unsigned long long)min_load_time);
min_load_time = link->last_activity;
best = link;
}
} else {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: link %d cannot send yet (load_time >= now)", link_index);
}
link = link->next;
}
if (best) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: selected link %p (activity=%llu)",
best, (unsigned long long)min_load_time);
} else {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_select_link: no suitable link found");
}
return best;
}
// Init timeout callback (placeholder)
static void init_timeout_cb(void* arg) {
struct ETCP_LINK* link = (struct ETCP_LINK*)arg;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "init_timeout_cb: link=%p, retry_count=%u, timeout=%u",
link, link->init_retry_count, link->init_timeout);
// Resend INIT, increment retry
link->init_retry_count++;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "init_timeout_cb: incremented retry_count to %u", link->init_retry_count);
if (link->init_retry_count > 5) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "init_timeout_cb: max retries exceeded (%u > 5), closing link", link->init_retry_count);
// Fail
etcp_link_close(link);
return;
}
// Resend...
link->init_timeout *= 2; // Backoff
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "init_timeout_cb: doubled timeout to %u ms", link->init_timeout);
link->init_timer = uasync_set_timeout(link->etcp->instance->ua, link->init_timeout, link, init_timeout_cb);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "init_timeout_cb: rescheduled timer");
}
// Bandwidth limit update (called after send)
void etcp_loadbalancer_update_after_send(struct ETCP_LINK* link, size_t pkt_size) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: link=%p, pkt_size=%zu", link, pkt_size);
if (!link) {
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: called with NULL link");
return;
}
if (link->bandwidth == 0) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: link bandwidth is 0, skipping update");
return;
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: link bandwidth=%u bits/sec, packet size=%zu bytes",
link->bandwidth, pkt_size);
// Time to transmit (ns)
double byte_time_ns = 1000000000.0 / (link->bandwidth * 8.0); // bits/sec to byte/ns
uint64_t tx_time_ns = (uint64_t)(pkt_size * byte_time_ns);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: byte_time_ns=%.2f, tx_time_ns=%llu",
byte_time_ns, (unsigned long long)tx_time_ns);
// To timebase (0.1ms units)
uint64_t tx_time_tb = tx_time_ns / (TIMEBASE_NS / 10);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: tx_time_tb=%llu", (unsigned long long)tx_time_tb);
// Update last_activity
uint64_t now_tb = get_current_nanotime() / (TIMEBASE_NS / 10);
uint64_t old_activity = link->last_activity;
link->last_activity += tx_time_tb;
if (link->last_activity < now_tb - (DELTA_TIME_NS / (TIMEBASE_NS / 10))) {
link->last_activity = now_tb - (DELTA_TIME_NS / (TIMEBASE_NS / 10));
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_loadbalancer_update_after_send: updated last_activity from %llu to %llu",
(unsigned long long)old_activity, (unsigned long long)link->last_activity);
}

20
!/etcp_loadbalancer.h

@ -1,20 +0,0 @@
// etcp_loadbalancer.h - Load Balancer for ETCP Channels
#ifndef ETCP_LOADBALANCER_H
#define ETCP_LOADBALANCER_H
#include "etcp.h"
#ifdef __cplusplus
extern "C" {
#endif
// Functions
struct ETCP_LINK* etcp_loadbalancer_select_link(struct ETCP_CONN* etcp);
// Add more as needed (e.g., update bandwidth)
#ifdef __cplusplus
}
#endif
#endif // ETCP_LOADBALANCER_H

10
!/fix.patch

@ -1,10 +0,0 @@
--- etcp_connections.c.backup
+++ etcp_connections.c
@@ -409,6 +409,7 @@
uint8_t enc_buf[1600];
size_t enc_buf_len;
dgram->timestamp=get_current_timestamp();
+ printf("[ETCP DEBUG] About to encrypt: data_len=%d, noencrypt_len=%d, len=%d\\n", dgram->data_len, dgram->noencrypt_len, len);
// DUMP: Show packet before encryption
dump_packet_bytes("BEFORE ENCRYPT", dgram->data, dgram->data_len);

42
!/packet_dump.h

@ -1,42 +0,0 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
// Packet dump function for debugging
static void dump_packet(const char* direction, const uint8_t* data, size_t len, struct sockaddr_storage* addr) {
printf("[DUMP] %s packet: %zd bytes ", direction, len);
// Print address if provided
if (addr) {
if (addr->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)addr;
printf("to/from %s:%d ",
inet_ntoa(sin->sin_addr),
ntohs(sin->sin_port));
}
}
// Print first 64 bytes
printf("data: ");
for (size_t i = 0; i < len && i < 64; i++) {
printf("%02x", data[i]);
if (i % 4 == 3) printf(" ");
}
if (len > 64) printf("...");
// If it's INIT packet, parse fields
if (len > 0 && data[0] == 0x02) { // ETCP_INIT_REQUEST
if (len >= 15) { // Minimum INIT size: code(1) + node_id(8) + mtu(2) + keepalive(2) + pubkey(2 at least)
uint64_t node_id = 0;
memcpy(&node_id, data + 1, 8);
uint16_t mtu = (data[9] << 8) | data[10];
uint16_t keepalive = (data[11] << 8) | data[12];
printf(" | INIT: node_id=%llu mtu=%d keepalive=%d",
(unsigned long long)node_id, mtu, keepalive);
}
}
printf("\n");
}

604
!/pkt_normalizer.c

@ -1,604 +0,0 @@
#include "pkt_normalizer.h"
#include "../lib/u_async.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
static void packer_handler(struct ll_queue* q, struct ll_entry* unused, void* arg);
static void unpacker_handler(struct ll_queue* q, struct ll_entry* unused, void* arg);
static void send_buf(struct pn_struct* pn);
static int get_header(uint8_t* header, size_t L);
/* Calculate fragment size from mtu */
struct pn_struct* pkt_normalizer_init(uasync_t* ua, int is_packer, int mtu) {
struct pn_struct* pn = malloc(sizeof(struct pn_struct));
if (!pn) return NULL;
pn->ua = ua;
pn->input = queue_new(ua, NULL); // No memory pool for now
if (!pn->input) {
free(pn);
return NULL;
}
pn->output = queue_new(ua, NULL); // No memory pool for now
if (!pn->output) {
queue_free(pn->input);
free(pn);
return NULL;
}
pn->is_packer = is_packer;
if (is_packer) {
// Calculate fragment size from mtu: fragment = mtu - 100
int fragment_size = mtu - ETCP_OVERHEAD;
if (fragment_size < 256) fragment_size = 256; // Minimum sane value
pn->u.packer.cap = fragment_size;
pn->u.packer.buf = malloc(pn->u.packer.cap);
if (!pn->u.packer.buf) {
queue_free(pn->input);
queue_free(pn->output);
free(pn);
return NULL;
}
pn->u.packer.len = 0;
pn->u.packer.error_count = 0;
queue_set_callback(pn->input, packer_handler, pn);
} else {
pn->u.unpacker.buf = NULL;
pn->u.unpacker.len = 0;
pn->u.unpacker.total_len = 0;
pn->u.unpacker.cap = 0;
pn->u.unpacker.error_count = 0;
queue_set_callback(pn->input, unpacker_handler, pn);
}
return pn;
}
void pkt_normalizer_deinit(struct pn_struct* pn) {
if (!pn) return;
queue_free(pn->input);
queue_free(pn->output);
if (pn->is_packer) {
free(pn->u.packer.buf);
} else {
free(pn->u.unpacker.buf);
free(pn->u.unpacker.service_buf);
}
free(pn);
}
struct pkt_normalizer_pair* pkt_normalizer_pair_init(uasync_t* ua, int mtu) {
struct pkt_normalizer_pair* pair = malloc(sizeof(struct pkt_normalizer_pair));
if (!pair) return NULL;
pair->packer = pkt_normalizer_init(ua, 1, mtu);
if (!pair->packer) {
free(pair);
return NULL;
}
pair->unpacker = pkt_normalizer_init(ua, 0, mtu);
if (!pair->unpacker) {
pkt_normalizer_deinit(pair->packer);
free(pair);
return NULL;
}
return pair;
}
void pkt_normalizer_pair_deinit(struct pkt_normalizer_pair* pair) {
if (!pair) return;
pkt_normalizer_deinit(pair->packer);
pkt_normalizer_deinit(pair->unpacker);
free(pair);
}
static int get_header(uint8_t* header, size_t L) {
if (L > 1535) return -1;
if (L <= 239) {
header[0] = (uint8_t)L;
return 1;
} else {
uint8_t high = (uint8_t)(L >> 8);
if (high > 5) return -1;
header[0] = 0xF0 + high;
header[1] = (uint8_t)(L & 0xFF);
return 2;
}
}
/* Сбросить состояние сборки фрагментов */
static void reset_fragment_state(struct pn_struct* pn) {
if (!pn->is_packer) {
pn->u.unpacker.len = 0;
pn->u.unpacker.total_len = 0;
pn->u.unpacker.in_fragment = 0;
}
}
/* Таймаут для сборки фрагментов */
static void send_buf(struct pn_struct* pn) {
if (pn->u.packer.len == 0) return;
size_t payload_len = pn->u.packer.len;
struct ll_entry* out = queue_entry_new(2 + payload_len);
if (!out) return;
uint8_t* d = ll_entry_data(out);
*(uint16_t*)d = (uint16_t)payload_len;
memcpy(d + 2, pn->u.packer.buf, payload_len);
queue_entry_put(pn->output, out);
pn->u.packer.len = 0;
}
static void packer_handler(struct ll_queue* q, struct ll_entry* unused, void* arg) {
(void)unused;
struct pn_struct* pn = arg;
size_t max = (size_t)1400;
struct ll_entry* entry = queue_entry_get(q);
if (!entry) {
queue_resume_callback(q);
return;
}
size_t L = ll_entry_size(entry);
uint8_t* data = ll_entry_data(entry);
uint8_t header[2];
int hsize = get_header(header, L);
size_t needed = (size_t)hsize + L;
if (hsize < 0 || needed > max) {
// Fragment
if (pn->u.packer.len > 0) {
send_buf(pn);
}
size_t remaining = L;
size_t pos = 0;
int fragment_count = 0;
while (remaining > 0) {
size_t chunk;
size_t payload_len;
struct ll_entry* fout;
uint8_t* fd;
uint8_t frag_header[2];
int frag_hsize;
if (fragment_count == 0) {
// Первый фрагмент: FF + общая длина (2 байта)
chunk = remaining > (max - 5) ? (max - 5) : remaining; // 2+1+2+chunk <= max
payload_len = 1 + 2 + chunk; // FF + total_len + data
fout = queue_entry_new(2 + payload_len);
if (!fout) {
break;
}
fd = ll_entry_data(fout);
*(uint16_t*)fd = (uint16_t)payload_len;
fd += 2;
*fd++ = 0xFF;
*fd++ = (uint8_t)(L >> 8); // старший байт общей длины
*fd++ = (uint8_t)(L & 0xFF); // младший байт общей длины
} else {
// Не первый фрагмент
if (remaining <= max - 3) {
// Это последний возможный фрагмент (помещается в один пакет с префиксом FE)
// Пытаемся отправить как обычный блок
frag_hsize = get_header(frag_header, remaining);
if (frag_hsize > 0 && (size_t)frag_hsize + remaining + 2 <= max) {
// Успешно: обычный блок
payload_len = frag_hsize + remaining;
chunk = remaining;
fout = queue_entry_new(2 + payload_len);
if (!fout) {
break;
}
fd = ll_entry_data(fout);
*(uint16_t*)fd = (uint16_t)payload_len;
fd += 2;
memcpy(fd, frag_header, frag_hsize);
fd += frag_hsize;
} else {
// Не удалось отправить как обычный блок - разбиваем на 2 фрагмента
// 1. FE фрагмент с частью данных
// 2. Обычный блок с оставшимися данными
// Находим максимальный размер для FE фрагмента
size_t max_fe_data = max - 3; // 2 байта длины + 0xFE
if (max_fe_data > remaining) {
max_fe_data = remaining;
}
// Пробуем различные размеры, начиная с максимального
size_t fe_data_size = 0;
for (size_t try_fe = max_fe_data; try_fe > 0; try_fe--) {
size_t try_regular = remaining - try_fe;
if (try_regular == 0) continue; // Нужно отправить что-то как обычный блок
uint8_t test_header[2];
int hsize = get_header(test_header, try_regular);
if (hsize <= 0) continue;
if ((size_t)hsize + try_regular + 2 <= max) {
fe_data_size = try_fe;
break;
}
}
if (fe_data_size == 0) {
// Не удалось найти разбиение - ошибка
pn->u.packer.error_count++;
// Отправляем как FE (нарушение спецификации, но это крайний случай)
chunk = remaining > (max - 3) ? (max - 3) : remaining;
payload_len = 1 + chunk; // FE + data
fout = queue_entry_new(2 + payload_len);
if (!fout) break;
fd = ll_entry_data(fout);
*(uint16_t*)fd = (uint16_t)payload_len;
fd += 2;
*fd++ = 0xFE;
} else {
// Отправляем FE фрагмент
chunk = fe_data_size;
payload_len = 1 + chunk; // FE + data
fout = queue_entry_new(2 + payload_len);
if (!fout) break;
fd = ll_entry_data(fout);
*(uint16_t*)fd = (uint16_t)payload_len;
fd += 2;
*fd++ = 0xFE;
memcpy(fd, data + pos, chunk);
queue_entry_put(pn->output, fout);
pos += chunk;
remaining -= chunk;
fragment_count++;
// Обновляем оставшиеся данные для обычного блока
// (цикл продолжит обработку на следующей итерации)
continue;
}
}
} else {
// Промежуточный фрагмент, отправляем как FE
chunk = remaining > (max - 3) ? (max - 3) : remaining;
payload_len = 1 + chunk; // FE + data
fout = queue_entry_new(2 + payload_len);
if (!fout) {
break;
}
fd = ll_entry_data(fout);
*(uint16_t*)fd = (uint16_t)payload_len;
fd += 2;
*fd++ = 0xFE;
}
}
memcpy(fd, data + pos, chunk);
queue_entry_put(pn->output, fout);
pos += chunk;
remaining -= chunk;
fragment_count++;
}
} else {
if (pn->u.packer.len + needed > max) {
send_buf(pn);
}
// Add to buffer
uint8_t* p = pn->u.packer.buf + pn->u.packer.len;
memcpy(p, header, (size_t)hsize);
memcpy(p + hsize, data, L);
pn->u.packer.len += needed;
}
queue_entry_free(entry);
if (pn->u.packer.len > 0) {
send_buf(pn);
}
queue_resume_callback(q);
}
void pkt_normalizer_set_service_callback(struct pn_struct* pn, pkt_normalizer_service_callback_t callback, void* user_data) {
if (!pn) return;
pn->service_callback = callback;
pn->service_callback_user_data = user_data;
}
void pkt_normalizer_reset_service_state(struct pn_struct* pn) {
if (!pn || pn->is_packer) return;
if (pn->u.unpacker.in_service) {
// Deliver pending service packet
if (pn->service_callback) {
pn->service_callback(pn->service_callback_user_data,
pn->u.unpacker.service_type,
pn->u.unpacker.service_buf,
pn->u.unpacker.service_len);
}
free(pn->u.unpacker.service_buf);
pn->u.unpacker.service_buf = NULL;
pn->u.unpacker.service_len = 0;
pn->u.unpacker.service_cap = 0;
pn->u.unpacker.in_service = 0;
}
}
void pkt_normalizer_reset_state(struct pn_struct* pn) {
if (!pn) return;
if (pn->is_packer) {
// Flush packer buffer
if (pn->u.packer.len > 0) {
send_buf(pn);
}
} else {
// Reset unpacker fragment state
reset_fragment_state(pn);
// Reset service state
pkt_normalizer_reset_service_state(pn);
}
}
int pkt_normalizer_send_service(struct pn_struct* pn, uint8_t type, const void* data, size_t len) {
if (!pn || !pn->is_packer) return -1;
// Service packet ограничен 256 байтами всего
if (len > 256 - 2) return -1; // 2 байта на заголовок (0xFC + тип)
size_t max = (size_t)1400;
if (max < 3) return -1;
// Размер сервисного пакета: 2 байта длины + 1 байт 0xFC + 1 байт тип + данные
// Если не помещается в один фрагмент - используем продолжение 0xFD
size_t total_service_len = 1 + 1 + len; // 0xFC + type + data
size_t pos = 0;
while (total_service_len > 0) {
// Определяем размер куска для этого пакета
size_t chunk;
uint8_t service_header;
if (pos == 0) {
// Первый пакет: 0xFC + тип + часть данных
// Максимум данных в первом пакете: max - 2 (длина) - 2 (0xFC+тип)
size_t max_first_data = max - 4;
if (max_first_data > len) max_first_data = len;
chunk = max_first_data;
service_header = 0xFC;
} else {
// Продолжение: 0xFD + данные
// Максимум данных: max - 2 (длина) - 1 (0xFD)
size_t max_cont_data = max - 3;
size_t remaining = len - pos;
if (max_cont_data > remaining) max_cont_data = remaining;
chunk = max_cont_data;
service_header = 0xFD;
}
if (chunk == 0) break;
size_t payload_len = 1 + chunk + (pos == 0 ? 1 : 0); // +1 байт типа для первого пакета
struct ll_entry* entry = queue_entry_new(2 + payload_len);
if (!entry) return -1;
uint8_t* d = ll_entry_data(entry);
*(uint16_t*)d = (uint16_t)payload_len;
d += 2;
*d++ = service_header;
if (pos == 0) {
*d++ = type;
}
memcpy(d, (const uint8_t*)data + pos, chunk);
queue_entry_put(pn->output, entry);
pos += chunk;
total_service_len -= chunk + (pos == chunk ? 2 : 1); // корректно вычитаем заголовки
}
return 0;
}
int pkt_normalizer_get_error_count(const struct pn_struct* pn) {
if (!pn) return 0;
if (pn->is_packer) {
return pn->u.packer.error_count;
}
return pn->u.unpacker.error_count;
}
void pkt_normalizer_reset_error_count(struct pn_struct* pn) {
if (!pn) return;
if (pn->is_packer) {
pn->u.packer.error_count = 0;
} else {
pn->u.unpacker.error_count = 0;
}
}
void pkt_normalizer_flush(struct pn_struct* pn) {
if (!pn || !pn->is_packer) return;
if (pn->u.packer.len > 0) {
send_buf(pn);
}
}
static void unpacker_handler(struct ll_queue* q, struct ll_entry* unused, void* arg) {
(void)unused;
struct pn_struct* pn = arg;
while (queue_entry_count(q) > 0) {
struct ll_entry* entry = queue_entry_get(q);
uint8_t* data = ll_entry_data(entry);
size_t total = ll_entry_size(entry);
uint16_t payload_len = *(uint16_t*)data;
if (total != 2 + (size_t)payload_len) {
queue_entry_free(entry);
continue;
}
uint8_t* cg = data + 2;
size_t cg_pos = 0;
size_t cg_len = (size_t)payload_len;
while (cg_pos < cg_len) {
uint8_t byte = cg[cg_pos++];
if (byte == 0xFF) {
/* Начало нового фрагментированного пакета */
if (pn->u.unpacker.in_fragment) {
/* Не завершен предыдущий фрагмент - ошибка */
pn->u.unpacker.error_count++;
reset_fragment_state(pn);
}
/* Проверить, что есть 2 байта для общей длины */
if (cg_pos + 2 > cg_len) {
pn->u.unpacker.error_count++;
goto err;
}
/* Прочитать общую длину */
uint16_t total_len = ((uint16_t)cg[cg_pos] << 8) | cg[cg_pos + 1];
cg_pos += 2;
size_t chunk_len = cg_len - cg_pos;
/* Выделить буфер при необходимости */
size_t new_len = chunk_len;
if (new_len > pn->u.unpacker.cap) {
size_t new_cap = pn->u.unpacker.cap ? pn->u.unpacker.cap * 2 : 4096;
if (new_cap < new_len) new_cap = new_len;
pn->u.unpacker.buf = realloc(pn->u.unpacker.buf, new_cap);
pn->u.unpacker.cap = new_cap;
}
memcpy(pn->u.unpacker.buf, cg + cg_pos, chunk_len);
pn->u.unpacker.len = chunk_len;
pn->u.unpacker.total_len = total_len;
pn->u.unpacker.in_fragment = 1;
cg_pos += chunk_len;
/* Проверить, не собрали ли уже весь пакет */
if (pn->u.unpacker.len >= pn->u.unpacker.total_len) {
if (pn->u.unpacker.len == pn->u.unpacker.total_len) {
struct ll_entry* out = queue_entry_new(pn->u.unpacker.total_len);
if (out) {
memcpy(ll_entry_data(out), pn->u.unpacker.buf, pn->u.unpacker.total_len);
queue_entry_put(pn->output, out);
}
} else {
/* Слишком много данных - ошибка */
pn->u.unpacker.error_count++;
}
reset_fragment_state(pn);
}
continue;
}
if (byte == 0xFE) {
/* Продолжение фрагментированного пакета */
if (!pn->u.unpacker.in_fragment) {
/* Не было начала фрагмента - ошибка */
pn->u.unpacker.error_count++;
goto err;
}
size_t chunk_len = cg_len - cg_pos;
size_t new_len = pn->u.unpacker.len + chunk_len;
if (new_len > pn->u.unpacker.cap) {
size_t new_cap = pn->u.unpacker.cap ? pn->u.unpacker.cap * 2 : 4096;
if (new_cap < new_len) new_cap = new_len;
pn->u.unpacker.buf = realloc(pn->u.unpacker.buf, new_cap);
pn->u.unpacker.cap = new_cap;
}
memcpy(pn->u.unpacker.buf + pn->u.unpacker.len, cg + cg_pos, chunk_len);
pn->u.unpacker.len = new_len;
cg_pos += chunk_len;
/* Проверить, не собрали ли уже весь пакет */
if (pn->u.unpacker.len >= pn->u.unpacker.total_len) {
if (pn->u.unpacker.len == pn->u.unpacker.total_len) {
struct ll_entry* out = queue_entry_new(pn->u.unpacker.total_len);
if (out) {
memcpy(ll_entry_data(out), pn->u.unpacker.buf, pn->u.unpacker.total_len);
queue_entry_put(pn->output, out);
}
} else {
/* Слишком много данных - ошибка */
pn->u.unpacker.error_count++;
}
reset_fragment_state(pn);
}
continue;
}
if (byte == 0xFC || byte == 0xFD) {
/* Service packet */
if (byte == 0xFC) {
/* Start of service packet */
if (pn->u.unpacker.in_service) {
/* Previous service packet finished - deliver it */
if (pn->service_callback) {
pn->service_callback(pn->service_callback_user_data,
pn->u.unpacker.service_type,
pn->u.unpacker.service_buf,
pn->u.unpacker.service_len);
}
free(pn->u.unpacker.service_buf);
pn->u.unpacker.service_buf = NULL;
pn->u.unpacker.service_len = 0;
pn->u.unpacker.service_cap = 0;
pn->u.unpacker.in_service = 0;
}
/* Read service type */
if (cg_pos >= cg_len) goto err;
uint8_t service_type = cg[cg_pos++];
pn->u.unpacker.service_type = service_type;
pn->u.unpacker.in_service = 1;
pn->u.unpacker.service_len = 0;
} else {
/* 0xFD - continuation */
if (!pn->u.unpacker.in_service) {
/* No service packet started - error */
pn->u.unpacker.error_count++;
goto err;
}
}
/* Read data */
size_t data_len = cg_len - cg_pos;
if (data_len > 0) {
size_t new_len = pn->u.unpacker.service_len + data_len;
if (new_len > 256) {
/* Service packet too long - error */
pn->u.unpacker.error_count++;
free(pn->u.unpacker.service_buf);
pn->u.unpacker.service_buf = NULL;
pn->u.unpacker.service_len = 0;
pn->u.unpacker.service_cap = 0;
pn->u.unpacker.in_service = 0;
goto err;
}
if (new_len > pn->u.unpacker.service_cap) {
size_t new_cap = pn->u.unpacker.service_cap ? pn->u.unpacker.service_cap * 2 : 256;
if (new_cap < new_len) new_cap = new_len;
if (new_cap > 256) new_cap = 256;
uint8_t* new_buf = realloc(pn->u.unpacker.service_buf, new_cap);
if (!new_buf) {
pn->u.unpacker.error_count++;
goto err;
}
pn->u.unpacker.service_buf = new_buf;
pn->u.unpacker.service_cap = new_cap;
}
memcpy(pn->u.unpacker.service_buf + pn->u.unpacker.service_len, cg + cg_pos, data_len);
pn->u.unpacker.service_len = new_len;
cg_pos += data_len;
}
/* Check if this is the end of service packet (end of payload) */
if (cg_pos >= cg_len) {
/* End of current payload, but service packet may continue in next transport packet */
continue;
} else {
/* There is more data in this payload after service packet - error */
pn->u.unpacker.error_count++;
free(pn->u.unpacker.service_buf);
pn->u.unpacker.service_buf = NULL;
pn->u.unpacker.service_len = 0;
pn->u.unpacker.service_cap = 0;
pn->u.unpacker.in_service = 0;
goto err;
}
}
/* Обычная запись (не фрагмент) */
size_t L;
if (byte <= 0xEF) {
L = byte;
} else if (byte >= 0xF0 && byte <= 0xF5) {
if (cg_pos >= cg_len) goto err;
uint8_t ext = cg[cg_pos++];
L = ((size_t)(byte - 0xF0) << 8) | ext;
} else {
/* Недопустимый байт */
goto err;
}
if (cg_pos + L > cg_len) goto err;
if (pn->u.unpacker.in_fragment) {
/* Это последний фрагмент в виде обычной записи */
size_t new_len = pn->u.unpacker.len + L;
if (new_len > pn->u.unpacker.cap) {
size_t new_cap = pn->u.unpacker.cap ? pn->u.unpacker.cap * 2 : 4096;
if (new_cap < new_len) new_cap = new_len;
pn->u.unpacker.buf = realloc(pn->u.unpacker.buf, new_cap);
pn->u.unpacker.cap = new_cap;
}
memcpy(pn->u.unpacker.buf + pn->u.unpacker.len, cg + cg_pos, L);
pn->u.unpacker.len = new_len;
cg_pos += L;
/* Проверить, собрали ли весь пакет */
if (pn->u.unpacker.len >= pn->u.unpacker.total_len) {
if (pn->u.unpacker.len == pn->u.unpacker.total_len) {
struct ll_entry* out = queue_entry_new(pn->u.unpacker.total_len);
if (out) {
memcpy(ll_entry_data(out), pn->u.unpacker.buf, pn->u.unpacker.total_len);
queue_entry_put(pn->output, out);
}
} else {
/* Слишком много данных - ошибка */
pn->u.unpacker.error_count++;
}
reset_fragment_state(pn);
}
} else {
/* Обычная запись (не часть фрагмента) */
struct ll_entry* out = queue_entry_new(L);
if (out) {
memcpy(ll_entry_data(out), cg + cg_pos, L);
queue_entry_put(pn->output, out);
}
cg_pos += L;
}
}
err:
queue_entry_free(entry);
}
queue_resume_callback(q);
}

75
!/pkt_normalizer.h

@ -1,75 +0,0 @@
// pkt_normalizer.h
#ifndef PKT_NORMALIZER_H
#define PKT_NORMALIZER_H
#include "ll_queue.h"
#include "../lib/u_async.h"
#include <stdint.h>
/* Default fragment reassembly timeout in uasync timebase units (0.1 ms) */
#ifndef PKT_NORMALIZER_FRAGMENT_TIMEOUT
#define PKT_NORMALIZER_FRAGMENT_TIMEOUT 5000 /* 500 ms */
#endif
/* ETCP overhead for calculating fragment size from MTU */
#define ETCP_OVERHEAD 100 // Reserve 100 bytes for headers, crypto, etc.*/
/* Service packet callback type */
typedef void (*pkt_normalizer_service_callback_t)(void* user_data, uint8_t type, const uint8_t* data, size_t len);
struct pn_struct {
struct ll_queue* input;
struct ll_queue* output;
uasync_t* ua;
int is_packer;
union {
struct {
uint8_t* buf;
size_t len;
size_t cap;
int error_count;
} packer;
struct {
uint8_t* buf; /* буфер для сборки фрагментов */
size_t len; /* текущая накопленная длина */
size_t total_len; /* ожидаемая общая длина из первого фрагмента */
size_t cap; /* ёмкость буфера */
int error_count; /* счетчик ошибок сборки */
int in_fragment; /* флаг: идет сборка фрагментов (1) или нет (0) */
/* Service packet reassembly */
uint8_t* service_buf; /* буфер для сборки сервисных пакетов */
size_t service_len; /* текущая накопленная длина сервисного пакета */
size_t service_cap; /* ёмкость буфера сервисного пакета */
uint8_t service_type; /* тип сервисного пакета */
int in_service; /* флаг: идет сборка сервисного пакета (1) или нет (0) */
} unpacker;
} u;
/* Service packet callback */
pkt_normalizer_service_callback_t service_callback;
void* service_callback_user_data;
};
struct pkt_normalizer_pair {
struct pn_struct* packer;
struct pn_struct* unpacker;
};
struct pn_struct* pkt_normalizer_init(uasync_t* ua, int is_packer, int mtu); // 1 for packer, 0 for unpacker, mtu for fragment size calculation
void pkt_normalizer_deinit(struct pn_struct* pn);
struct pkt_normalizer_pair* pkt_normalizer_pair_init(uasync_t* ua, int mtu);
void pkt_normalizer_pair_deinit(struct pkt_normalizer_pair* pair);
/* Error handling */
int pkt_normalizer_get_error_count(const struct pn_struct* pn);
void pkt_normalizer_reset_error_count(struct pn_struct* pn);
/* Flush internal buffer (packer only) */
void pkt_normalizer_flush(struct pn_struct* pn);
int pkt_normalizer_send_service(struct pn_struct* pn, uint8_t type, const void* data, size_t len);
void pkt_normalizer_set_service_callback(struct pn_struct* pn, pkt_normalizer_service_callback_t callback, void* user_data);
void pkt_normalizer_reset_service_state(struct pn_struct* pn);
void pkt_normalizer_reset_state(struct pn_struct* pn);
#endif // PKT_NORMALIZER_H

87
!/routing.h

@ -1,87 +0,0 @@
#ifndef ROUTING_H
#define ROUTING_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// Forward declarations
struct ETCP_CONNECTIONS;
// Типы маршрутов
typedef enum {
ROUTE_TYPE_STATIC = 0,
ROUTE_TYPE_DYNAMIC = 1,
ROUTE_TYPE_LOCAL = 2,
ROUTE_TYPE_LEARNED = 3
} route_type_t;
// Флаги маршрута
typedef enum {
ROUTE_FLAG_ACTIVE = (1 << 0),
ROUTE_FLAG_VALIDATED = (1 << 1),
ROUTE_FLAG_ADVERTISED = (1 << 2),
ROUTE_FLAG_LEARNED = (1 << 3)
} route_flags_t;
// Расширенные метрики маршрута
struct route_metrics {
uint32_t bandwidth_kbps;
uint16_t packet_loss_rate;
uint16_t latency_ms;
uint8_t hop_count;
uint64_t last_updated;
};
// Расширенная запись маршрута
struct route_entry {
uint32_t network;
uint8_t prefix_length;
uint32_t next_hop_ip;
struct ETCP_SOCKET* next_hop;
route_type_t type;
uint8_t flags;
struct route_metrics metrics;
uint64_t created_time;
uint64_t last_update;
uint64_t last_used;
};
// Таблица маршрутизации
struct routing_table {
struct route_entry *entries;
size_t count;
size_t capacity;
uint32_t *dynamic_subnets;
size_t dynamic_subnet_count;
uint32_t *local_subnets;
size_t local_subnet_count;
struct {
uint64_t total_routes;
uint64_t static_routes;
uint64_t dynamic_routes;
uint64_t local_routes;
uint64_t learned_routes;
uint64_t routes_added;
uint64_t routes_deleted;
uint64_t lookup_count;
uint64_t hit_count;
uint64_t routes_lookup_hits;
uint64_t routes_lookup_misses;
uint64_t validation_failures;
} stats;
};
struct routing_table *routing_table_create(void);
void routing_table_destroy(struct routing_table *table);
bool routing_table_insert(struct routing_table *table, const struct route_entry *entry);
bool routing_table_delete(struct routing_table *table, uint32_t network, uint8_t prefix_length, uint32_t source_node_id);
bool routing_table_lookup(struct routing_table *table, uint32_t dest_ip, struct route_entry *best_route);
bool routing_validate_route(struct routing_table *table, uint32_t network, uint8_t prefix_length, route_type_t route_type);
bool routing_add_dynamic_subnet(struct routing_table *table, uint32_t network, uint8_t prefix_length);
bool routing_add_local_subnet(struct routing_table *table, uint32_t network, uint8_t prefix_length);
bool routing_get_all_routes(const struct routing_table *table, uint32_t network, uint8_t prefix_length, struct route_entry **routes, size_t *count);
void routing_table_print(const struct routing_table *table);
const char* route_type_to_string(route_type_t type);
char* ip_to_string(uint32_t ip, char *buffer);
#endif // ROUTING_H

388
!/secure_channel.c

@ -1,388 +0,0 @@
/* sc_lib.c - Secure Channel library implementation using TinyCrypt */
#include "secure_channel.h"
#include "../tinycrypt/lib/include/tinycrypt/ecc.h"
#include "../tinycrypt/lib/include/tinycrypt/ecc_dh.h"
#include "../tinycrypt/lib/include/tinycrypt/aes.h"
#include "../tinycrypt/lib/include/tinycrypt/ccm_mode.h"
#include "../tinycrypt/lib/include/tinycrypt/constants.h"
#include "../tinycrypt/lib/include/tinycrypt/ecc_platform_specific.h"
#include "../tinycrypt/lib/include/tinycrypt/sha256.h"
#include <string.h>
#include <stddef.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include <stdio.h>
// Simple debug macros
#define DEBUG_CATEGORY_CRYPTO 1
#define DEBUG_ERROR(category, fmt, ...) fprintf(stderr, "ERROR: " fmt "\n", ##__VA_ARGS__)
#define DEBUG_INFO(category, fmt, ...) fprintf(stdout, "INFO: " fmt "\n", ##__VA_ARGS__)
#include <stdio.h>
#include <fcntl.h>
#include "crc32.h"
static const struct uECC_Curve_t *curve = NULL;
static uint8_t sc_urandom_seed[8] = {0};
static int sc_urandom_initialized = 0;
static void sc_init_random_seed(void)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd >= 0) {
ssize_t ret = read(fd, sc_urandom_seed, 8);
close(fd);
if (ret == 8) {
sc_urandom_initialized = 1;
}
}
}
static int sc_rng(uint8_t *dest, unsigned size)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
return 0;
}
ssize_t ret = read(fd, dest, size);
close(fd);
if (ret != size) {
return 0;
}
/* Mix in PID and microtime for additional entropy */
pid_t pid = getpid();
struct timeval tv;
gettimeofday(&tv, NULL);
for (unsigned i = 0; i < size; i++) {
dest[i] ^= ((pid >> (i % (sizeof(pid) * 8))) & 0xFF);
dest[i] ^= ((tv.tv_sec >> (i % (sizeof(tv.tv_sec) * 8))) & 0xFF);
dest[i] ^= ((tv.tv_usec >> (i % (sizeof(tv.tv_usec) * 8))) & 0xFF);
}
return 1;
}
static int sc_validate_key(const uint8_t *public_key)
{
if (!curve) {
curve = uECC_secp256r1();
}
int result = uECC_valid_public_key(public_key, curve);
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_validate_key: uECC_valid_public_key returned %d", result);
return result;
}
sc_status_t sc_generate_keypair(struct SC_MYKEYS *pk)
{
if (!pk) {
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
/* Set custom RNG function */
uECC_set_rng(sc_rng);
if (!uECC_make_key(pk->public_key, pk->private_key, curve)) {
return SC_ERR_CRYPTO;
}
return SC_OK;
}
// Конвертация hex строки в бинарный формат
static int hex_to_binary(const char *hex_str, uint8_t *binary, size_t binary_len) {
if (!hex_str || !binary || strlen(hex_str) != binary_len * 2) return -1;
for (size_t i = 0; i < binary_len; i++) {
unsigned int byte;
if (sscanf(hex_str + i * 2, "%2x", &byte) != 1) return -1;
binary[i] = (uint8_t)byte;
}
return 0;
}
sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_key, const char *private_key) {
if (!mykeys || !public_key || !private_key) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: invalid arguments");
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: public_key len=%zu, private_key len=%zu",
strlen(public_key), strlen(private_key));
/* Convert hex to binary first */
if (hex_to_binary(public_key, mykeys->public_key, SC_PUBKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: failed to convert public key from hex");
return SC_ERR_INVALID_ARG;
}
if (hex_to_binary(private_key, mykeys->private_key, SC_PRIVKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: failed to convert private key from hex");
return SC_ERR_INVALID_ARG;
}
/* Validate the converted binary public key */
if (sc_validate_key(mykeys->public_key) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: public key validation failed");
return SC_ERR_INVALID_ARG;
}
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: keys initialized successfully");
return SC_OK;
}
sc_status_t sc_init_ctx(sc_context_t *ctx, struct SC_MYKEYS *mykeys) {
ctx->pk=mykeys;
ctx->initialized = 1;
ctx->peer_key_set = 0;
ctx->session_ready = 0;
ctx->tx_counter = 0;
ctx->rx_counter = 0;
return SC_OK;
}
sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key_h, int mode) {
uint8_t shared_secret[SC_SHARED_SECRET_SIZE];
uint8_t peer_public_key[SC_PUBKEY_SIZE];
if (mode) {
if (hex_to_binary(peer_public_key_h, peer_public_key, SC_PUBKEY_SIZE)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid hex key format");
return SC_ERR_INVALID_ARG;
}
}
else memcpy(peer_public_key, peer_public_key_h, SC_PUBKEY_SIZE);
if (!ctx) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid ctx");
return SC_ERR_INVALID_ARG;
}
if (!ctx->initialized) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: ctx not initialized");
return SC_ERR_NOT_INITIALIZED;
}
if (!curve) {
curve = uECC_secp256r1();
}
/* Validate peer public key */
if (sc_validate_key(peer_public_key) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid key");
return SC_ERR_INVALID_ARG;
}
/* Compute shared secret using ECDH */
if (!ctx->pk) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: no private key");
return SC_ERR_NOT_INITIALIZED;
}
if (!uECC_shared_secret(peer_public_key, ctx->pk->private_key,
shared_secret, curve)) {
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: shared secret error");
return SC_ERR_CRYPTO;
}
/* Derive session key from shared secret (simple copy for demo) */
memcpy(ctx->session_key, shared_secret, SC_SESSION_KEY_SIZE);
/* Store peer public key */
memcpy(ctx->peer_public_key, peer_public_key, SC_PUBKEY_SIZE);
ctx->peer_key_set = 1;
ctx->session_ready = 1;
return SC_OK;
}
static void sc_build_nonce(uint64_t counter, uint8_t *nonce_out)
{
struct tc_sha256_state_struct sha_ctx;
uint8_t hash[32];
struct timeval tv;
uint8_t data[8 + 8 + 4];
if (!sc_urandom_initialized) {
sc_init_random_seed();
}
gettimeofday(&tv, NULL);
memcpy(data, sc_urandom_seed, 8);
data[8] = (counter >> 0) & 0xFF;
data[9] = (counter >> 8) & 0xFF;
data[10] = (counter >> 16) & 0xFF;
data[11] = (counter >> 24) & 0xFF;
data[12] = (counter >> 32) & 0xFF;
data[13] = (counter >> 40) & 0xFF;
data[14] = (counter >> 48) & 0xFF;
data[15] = (counter >> 56) & 0xFF;
data[16] = (tv.tv_sec >> 0) & 0xFF;
data[17] = (tv.tv_sec >> 8) & 0xFF;
data[18] = (tv.tv_sec >> 16) & 0xFF;
data[19] = (tv.tv_sec >> 24) & 0xFF;
tc_sha256_init(&sha_ctx);
tc_sha256_update(&sha_ctx, data, 20);
tc_sha256_final(hash, &sha_ctx);
memcpy(nonce_out, hash, SC_NONCE_SIZE);
}
sc_status_t sc_encrypt(sc_context_t *ctx,
const uint8_t *plaintext,
size_t plaintext_len,
uint8_t *ciphertext,
size_t *ciphertext_len)
{
uint8_t nonce[SC_NONCE_SIZE];
struct tc_aes_key_sched_struct sched;
struct tc_ccm_mode_struct ccm_state;
size_t total_plaintext_len = plaintext_len + SC_CRC32_SIZE;
uint8_t plaintext_with_crc[total_plaintext_len];
uint8_t combined_output[total_plaintext_len + SC_TAG_SIZE];
if (!ctx || !plaintext || !ciphertext || !ciphertext_len) {
return SC_ERR_INVALID_ARG;
}
if (!ctx->session_ready) {
return SC_ERR_NOT_INITIALIZED;
}
if (plaintext_len == 0) {
return SC_ERR_INVALID_ARG;
}
/* Добавляем CRC32 к данным */
memcpy(plaintext_with_crc, plaintext, plaintext_len);
uint32_t crc = crc32_calc(plaintext, plaintext_len);
plaintext_with_crc[plaintext_len] = (crc >> 0) & 0xFF;
plaintext_with_crc[plaintext_len + 1] = (crc >> 8) & 0xFF;
plaintext_with_crc[plaintext_len + 2] = (crc >> 16) & 0xFF;
plaintext_with_crc[plaintext_len + 3] = (crc >> 24) & 0xFF;
/* Initialize AES key schedule */
if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Build nonce from counter */
sc_build_nonce(ctx->tx_counter, nonce);
/* Configure CCM mode */
if (tc_ccm_config(&ccm_state, &sched, nonce, SC_NONCE_SIZE, SC_TAG_SIZE) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Encrypt and generate tag */
if (tc_ccm_generation_encryption(combined_output, sizeof(combined_output),
NULL, 0, /* no associated data */
plaintext_with_crc, total_plaintext_len,
&ccm_state) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Copy ciphertext + tag to output buffer */
memcpy(ciphertext, combined_output, total_plaintext_len + SC_TAG_SIZE);
*ciphertext_len = total_plaintext_len + SC_TAG_SIZE;
ctx->tx_counter++;
return SC_OK;
}
sc_status_t sc_decrypt(sc_context_t *ctx,
const uint8_t *ciphertext,
size_t ciphertext_len,
uint8_t *plaintext,
size_t *plaintext_len)
{
uint8_t nonce[SC_NONCE_SIZE];
struct tc_aes_key_sched_struct sched;
struct tc_ccm_mode_struct ccm_state;
TCCcmMode_t c = &ccm_state;
size_t total_plaintext_len = ciphertext_len - SC_TAG_SIZE;
uint8_t plaintext_with_crc[total_plaintext_len];
if (!ctx || !ciphertext || !plaintext || !plaintext_len) {
return SC_ERR_INVALID_ARG;
}
if (!ctx->session_ready) {
return SC_ERR_NOT_INITIALIZED;
}
if (ciphertext_len < SC_TAG_SIZE + SC_CRC32_SIZE) {
return SC_ERR_INVALID_ARG;
}
/* Initialize AES key schedule */
if (tc_aes128_set_encrypt_key(&sched, ctx->session_key) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Build nonce from counter */
sc_build_nonce(ctx->rx_counter, nonce);
/* Configure CCM mode */
if (tc_ccm_config(c, &sched, nonce, SC_NONCE_SIZE, SC_TAG_SIZE) != TC_CRYPTO_SUCCESS) {
return SC_ERR_CRYPTO;
}
/* Decrypt and verify tag */
if (tc_ccm_decryption_verification(plaintext_with_crc, total_plaintext_len,
NULL, 0, /* no associated data */
ciphertext, ciphertext_len,
c) != TC_CRYPTO_SUCCESS) {
return SC_ERR_AUTH_FAILED;
}
/* Проверяем CRC32 */
size_t data_len = total_plaintext_len - SC_CRC32_SIZE;
uint32_t expected_crc = crc32_calc(plaintext_with_crc, data_len);
uint32_t received_crc = (plaintext_with_crc[data_len] << 0) |
(plaintext_with_crc[data_len + 1] << 8) |
(plaintext_with_crc[data_len + 2] << 16) |
(plaintext_with_crc[data_len + 3] << 24);
if (expected_crc != received_crc) {
return SC_ERR_CRC_FAILED;
}
/* Копируем данные без CRC32 */
memcpy(plaintext, plaintext_with_crc, data_len);
*plaintext_len = data_len;
ctx->rx_counter++;
return SC_OK;
}
sc_status_t sc_compute_public_key_from_private(const uint8_t *private_key, uint8_t *public_key) {
if (!private_key || !public_key) {
return SC_ERR_INVALID_ARG;
}
if (!curve) {
curve = uECC_secp256r1();
}
if (!uECC_compute_public_key(private_key, public_key, curve)) {
return SC_ERR_CRYPTO;
}
return SC_OK;
}

68
!/secure_channel.h

@ -1,68 +0,0 @@
// secure_channel.h
#ifndef SECURE_CHANNEL_H
#define SECURE_CHANNEL_H
#include <stdint.h>
#include <stddef.h>
// Размеры ключей
#define SC_PRIVKEY_SIZE 32
#define SC_PUBKEY_SIZE 64
#define SC_HASH_SIZE 32
#define SC_NONCE_SIZE 13 // CCM requires exactly 13 bytes
#define SC_SHARED_SECRET_SIZE SC_HASH_SIZE
#define SC_SESSION_KEY_SIZE 16
#define SC_TAG_SIZE 8
#define SC_CRC32_SIZE 4
// Коды возврата
#define SC_OK 0
#define SC_ERR_INVALID_ARG -1
#define SC_ERR_CRYPTO -2
#define SC_ERR_NOT_INITIALIZED -3
#define SC_ERR_AUTH_FAILED -4
#define SC_ERR_CRC_FAILED -5
#define SC_PEER_PUBKEY_BIN 0
#define SC_PEER_PUBKEY_HEX 1
// Типы
typedef int sc_status_t;
typedef struct secure_channel sc_context_t;
struct SC_MYKEYS {
/* Локальные ключи */
uint8_t private_key[SC_PRIVKEY_SIZE];
uint8_t public_key[SC_PUBKEY_SIZE];
};
// Контекст защищенного канала
struct secure_channel {
struct SC_MYKEYS* pk;
/* Ключи пира (после key exchange) */
uint8_t peer_public_key[SC_PUBKEY_SIZE];
uint8_t session_key[SC_SESSION_KEY_SIZE]; /* Derived session key */
/* Nonces для отправки и приема */
uint8_t send_nonce[SC_NONCE_SIZE];
uint8_t recv_nonce[SC_NONCE_SIZE];
uint8_t initialized;
uint8_t peer_key_set;
uint8_t session_ready;
uint64_t tx_counter;
uint64_t rx_counter;
};
// Функции инициализации
sc_status_t sc_init_ctx(sc_context_t *ctx, struct SC_MYKEYS *mykeys);
sc_status_t sc_generate_keypair(struct SC_MYKEYS *keys);
sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_key, const char *private_key);
sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key, int mode);// mode: 0-bin 1-hex key format
sc_status_t sc_compute_public_key_from_private(const uint8_t *private_key, uint8_t *public_key);
// Криптографические операции
sc_status_t sc_encrypt(sc_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t *ciphertext_len);
sc_status_t sc_decrypt(sc_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t *plaintext_len);
#endif // SECURE_CHANNEL_H

342
!/test_udp_socket.c

@ -1,342 +0,0 @@
#include "test_udp_socket.h"
#include "../lib/debug_config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#ifndef DEBUG_CATEGORY_TEST
#define DEBUG_CATEGORY_TEST (1 << 30)
#endif
// Global socket registry for fd-based operations
static struct test_udp_socket* g_socket_registry[1024] = {NULL};
pthread_mutex_t g_registry_mutex = PTHREAD_MUTEX_INITIALIZER; // Make it non-static for external access
static int g_next_fd = 1000; // Start with high fd numbers to avoid conflicts
// Helper function to allocate packet
static struct test_udp_packet* allocate_packet(const uint8_t* data, size_t len,
const struct sockaddr* addr, socklen_t addr_len) {
struct test_udp_packet* packet = calloc(1, sizeof(struct test_udp_packet));
if (!packet) return NULL;
packet->data = malloc(len);
if (!packet->data) {
free(packet);
return NULL;
}
memcpy(packet->data, data, len);
packet->len = len;
if (addr && addr_len > 0 && addr_len <= sizeof(struct sockaddr_storage)) {
memcpy(&packet->addr, addr, addr_len);
packet->addr_len = addr_len;
} else {
packet->addr_len = 0;
}
return packet;
}
// Helper function to free packet
static void free_packet(struct test_udp_packet* packet) {
if (!packet) return;
if (packet->data) free(packet->data);
free(packet);
}
// Create virtual UDP socket
struct test_udp_socket* test_udp_socket_create(int family) {
if (family != AF_INET && family != AF_INET6) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Unsupported address family: %d", family);
return NULL;
}
struct test_udp_socket* sock = calloc(1, sizeof(struct test_udp_socket));
if (!sock) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to allocate UDP socket");
return NULL;
}
// Assign virtual file descriptor
pthread_mutex_lock(&g_registry_mutex);
sock->fd = g_next_fd++;
if (g_next_fd >= 2000) g_next_fd = 1000; // Wrap around
pthread_mutex_unlock(&g_registry_mutex);
sock->family = family;
sock->bound = false;
sock->nonblocking = false;
sock->recv_queue_head = NULL;
sock->recv_queue_tail = NULL;
sock->recv_queue_size = 0;
// Register socket
test_udp_socket_register(sock);
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Created virtual UDP socket: fd=%d, family=%d", sock->fd, family);
return sock;
}
// Destroy virtual UDP socket
void test_udp_socket_destroy(struct test_udp_socket* sock) {
if (!sock) return;
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Destroying virtual UDP socket: fd=%d (stats: sent=%zu/%zu, recv=%zu/%zu, errors=%zu/%zu)",
sock->fd, sock->stats.packets_sent, sock->stats.bytes_sent,
sock->stats.packets_received, sock->stats.bytes_received,
sock->stats.send_errors, sock->stats.recv_errors);
// Unregister socket
test_udp_socket_unregister(sock);
// Free receive queue
struct test_udp_packet* packet = sock->recv_queue_head;
while (packet) {
struct test_udp_packet* next = packet->next;
free_packet(packet);
packet = next;
}
free(sock);
}
// Bind virtual socket
int test_udp_socket_bind(struct test_udp_socket* sock, const struct sockaddr* addr, socklen_t len) {
if (!sock || !addr || len == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Invalid parameters for socket bind");
return -1;
}
if (sock->bound) {
DEBUG_WARN(DEBUG_CATEGORY_TEST, "Socket already bound");
return 0;
}
if (len > sizeof(struct sockaddr_storage)) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Address too large: %d", len);
return -1;
}
memcpy(&sock->local_addr, addr, len);
sock->bound = true;
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Bound virtual UDP socket fd=%d", sock->fd);
return 0;
}
// Set socket options (limited implementation for testing)
int test_udp_socket_setsockopt(struct test_udp_socket* sock, int level, int optname,
const void *optval, socklen_t optlen) {
if (!sock) return -1;
// For testing purposes, just log the option setting
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "setsockopt on fd=%d: level=%d, optname=%d, optlen=%d",
sock->fd, level, optname, optlen);
// Handle some common options
if (level == SOL_SOCKET) {
switch (optname) {
case SO_REUSEADDR:
case SO_REUSEPORT:
// Always allow for testing
return 0;
case SO_BROADCAST:
return 0;
default:
break;
}
}
return 0; // Pretend success for most options
}
// Get socket options
int test_udp_socket_getsockopt(struct test_udp_socket* sock, int level, int optname,
void *optval, socklen_t *optlen) {
if (!sock || !optval || !optlen) return -1;
// For testing purposes, return reasonable defaults
if (level == SOL_SOCKET) {
switch (optname) {
case SO_ERROR:
*(int*)optval = 0;
*optlen = sizeof(int);
return 0;
default:
break;
}
}
return -1; // Not implemented
}
// Set non-blocking mode
int test_udp_socket_set_nonblocking(struct test_udp_socket* sock, bool nonblocking) {
if (!sock) return -1;
sock->nonblocking = nonblocking;
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Set nonblocking on fd=%d: %s", sock->fd, nonblocking ? "true" : "false");
return 0;
}
// Send packet using virtual socket
ssize_t test_udp_socket_sendto(struct test_udp_socket* sock, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addr_len) {
if (!sock || !buf || len == 0) {
return -1;
}
const uint8_t* data = (const uint8_t*)buf;
// Call packet sent callback if available
if (sock->packet_sent) {
sock->packet_sent(data, len, dest_addr, addr_len, sock->context);
}
sock->stats.packets_sent++;
sock->stats.bytes_sent += len;
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Sent packet on fd=%d: %zu bytes to family=%d",
sock->fd, len, dest_addr ? dest_addr->sa_family : -1);
return len;
}
// Receive packet from virtual socket
ssize_t test_udp_socket_recvfrom(struct test_udp_socket* sock, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addr_len) {
if (!sock || !buf || len == 0) {
return -1;
}
uint8_t* buffer = (uint8_t*)buf;
// Check if we have packets in queue
pthread_mutex_lock(&g_registry_mutex);
struct test_udp_packet* packet = sock->recv_queue_head;
if (!packet) {
pthread_mutex_unlock(&g_registry_mutex);
errno = EAGAIN;
return -1;
}
// Remove packet from queue
sock->recv_queue_head = packet->next;
if (!sock->recv_queue_head) {
sock->recv_queue_tail = NULL;
}
sock->recv_queue_size--;
pthread_mutex_unlock(&g_registry_mutex);
// Copy packet data
size_t copy_len = packet->len < len ? packet->len : len;
memcpy(buffer, packet->data, copy_len);
// Copy source address if requested
if (src_addr && addr_len && packet->addr_len > 0) {
socklen_t copy_addr_len = packet->addr_len < *addr_len ? packet->addr_len : *addr_len;
memcpy(src_addr, &packet->addr, copy_addr_len);
*addr_len = copy_addr_len;
}
sock->stats.packets_received++;
sock->stats.bytes_received += copy_len;
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Received packet on fd=%d: %zu bytes", sock->fd, copy_len);
free_packet(packet);
return copy_len;
}
// Inject packet into receive queue (simulates incoming packet)
int test_udp_socket_inject(struct test_udp_socket* sock,
const uint8_t* data, size_t len,
const struct sockaddr* src_addr, socklen_t addr_len) {
if (!sock || !data || len == 0) {
return -1;
}
if (sock->recv_queue_size >= TEST_UDP_MAX_QUEUE_SIZE) {
DEBUG_WARN(DEBUG_CATEGORY_TEST, "Receive queue full on fd=%d", sock->fd);
return -1;
}
struct test_udp_packet* packet = allocate_packet(data, len, src_addr, addr_len);
if (!packet) {
return -1;
}
pthread_mutex_lock(&g_registry_mutex);
if (sock->recv_queue_tail) {
sock->recv_queue_tail->next = packet;
} else {
sock->recv_queue_head = packet;
}
sock->recv_queue_tail = packet;
sock->recv_queue_size++;
pthread_mutex_unlock(&g_registry_mutex);
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Injected packet into fd=%d: %zu bytes", sock->fd, len);
return 0;
}
// Get virtual socket file descriptor
int test_udp_socket_get_fd(struct test_udp_socket* sock) {
return sock ? sock->fd : -1;
}
// Get socket statistics
void test_udp_socket_get_stats(struct test_udp_socket* sock, size_t* packets_sent,
size_t* packets_received, size_t* bytes_sent,
size_t* bytes_received, size_t* send_errors, size_t* recv_errors) {
if (!sock) return;
if (packets_sent) *packets_sent = sock->stats.packets_sent;
if (packets_received) *packets_received = sock->stats.packets_received;
if (bytes_sent) *bytes_sent = sock->stats.bytes_sent;
if (bytes_received) *bytes_received = sock->stats.bytes_received;
if (send_errors) *send_errors = sock->stats.send_errors;
if (recv_errors) *recv_errors = sock->stats.recv_errors;
}
// Reset socket statistics
void test_udp_socket_reset_stats(struct test_udp_socket* sock) {
if (!sock) return;
memset(&sock->stats, 0, sizeof(sock->stats));
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Reset statistics for virtual UDP socket fd=%d", sock->fd);
}
// Global virtual socket registry for fd-based operations
struct test_udp_socket* test_udp_socket_find_by_fd(int fd) {
pthread_mutex_lock(&g_registry_mutex);
struct test_udp_socket* sock = NULL;
if (fd >= 0 && fd < 1024) {
sock = g_socket_registry[fd];
}
pthread_mutex_unlock(&g_registry_mutex);
return sock;
}
void test_udp_socket_register(struct test_udp_socket* sock) {
if (!sock) return;
pthread_mutex_lock(&g_registry_mutex);
if (sock->fd >= 0 && sock->fd < 1024) {
g_socket_registry[sock->fd] = sock;
}
pthread_mutex_unlock(&g_registry_mutex);
}
void test_udp_socket_unregister(struct test_udp_socket* sock) {
if (!sock) return;
pthread_mutex_lock(&g_registry_mutex);
if (sock->fd >= 0 && sock->fd < 1024) {
g_socket_registry[sock->fd] = NULL;
}
pthread_mutex_unlock(&g_registry_mutex);
}

101
!/test_udp_socket.h

@ -1,101 +0,0 @@
#ifndef TEST_UDP_SOCKET_H
#define TEST_UDP_SOCKET_H
#include <sys/socket.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define TEST_UDP_MAX_PACKET_SIZE 65536
#define TEST_UDP_MAX_QUEUE_SIZE 1000
// Test UDP packet structure
struct test_udp_packet {
uint8_t* data;
size_t len;
struct sockaddr_storage addr;
socklen_t addr_len;
struct test_udp_packet* next;
};
// Virtual UDP socket for testing
struct test_udp_socket {
int fd; // Virtual file descriptor
int family; // Address family (AF_INET or AF_INET6)
struct sockaddr_storage local_addr;
bool bound;
bool nonblocking;
// Receive queue (packets waiting to be read)
struct test_udp_packet* recv_queue_head;
struct test_udp_packet* recv_queue_tail;
size_t recv_queue_size;
// Statistics
struct {
size_t packets_sent;
size_t packets_received;
size_t bytes_sent;
size_t bytes_received;
size_t send_errors;
size_t recv_errors;
} stats;
// Callback for outgoing packets
void (*packet_sent)(const uint8_t* data, size_t len,
const struct sockaddr* dest_addr, socklen_t addr_len,
void* context);
void* context;
};
// Create virtual UDP socket
struct test_udp_socket* test_udp_socket_create(int family);
// Destroy virtual UDP socket
void test_udp_socket_destroy(struct test_udp_socket* sock);
// Bind virtual socket
int test_udp_socket_bind(struct test_udp_socket* sock, const struct sockaddr* addr, socklen_t len);
// Set socket options (limited implementation for testing)
int test_udp_socket_setsockopt(struct test_udp_socket* sock, int level, int optname,
const void *optval, socklen_t optlen);
// Get socket options
int test_udp_socket_getsockopt(struct test_udp_socket* sock, int level, int optname,
void *optval, socklen_t *optlen);
// Set non-blocking mode
int test_udp_socket_set_nonblocking(struct test_udp_socket* sock, bool nonblocking);
// Send packet using virtual socket
ssize_t test_udp_socket_sendto(struct test_udp_socket* sock, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addr_len);
// Receive packet from virtual socket
ssize_t test_udp_socket_recvfrom(struct test_udp_socket* sock, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addr_len);
// Inject packet into receive queue (simulates incoming packet)
int test_udp_socket_inject(struct test_udp_socket* sock,
const uint8_t* data, size_t len,
const struct sockaddr* src_addr, socklen_t addr_len);
// Get virtual socket file descriptor
int test_udp_socket_get_fd(struct test_udp_socket* sock);
// Get socket statistics
void test_udp_socket_get_stats(struct test_udp_socket* sock, size_t* packets_sent,
size_t* packets_received, size_t* bytes_sent,
size_t* bytes_received, size_t* send_errors, size_t* recv_errors);
// Reset socket statistics
void test_udp_socket_reset_stats(struct test_udp_socket* sock);
// Global virtual socket registry for fd-based operations
struct test_udp_socket* test_udp_socket_find_by_fd(int fd);
void test_udp_socket_register(struct test_udp_socket* sock);
void test_udp_socket_unregister(struct test_udp_socket* sock);
#endif // TEST_UDP_SOCKET_H

190
!/test_virtual_tun.c

@ -1,190 +0,0 @@
#include "test_virtual_tun.h"
#include "../lib/debug_config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#ifndef DEBUG_CATEGORY_TEST
#define DEBUG_CATEGORY_TEST (1 << 30)
#endif
// Create virtual TUN with bidirectional pipes
struct virtual_tun* virtual_tun_create(const char* ifname) {
if (!ifname) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "NULL interface name provided");
return NULL;
}
struct virtual_tun* vtun = calloc(1, sizeof(struct virtual_tun));
if (!vtun) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to allocate virtual TUN");
return NULL;
}
// Copy interface name
strncpy(vtun->ifname, ifname, sizeof(vtun->ifname) - 1);
vtun->ifname[sizeof(vtun->ifname) - 1] = '\0';
vtun->enabled = true;
// Create pipes for bidirectional communication
// read_pipe: [0] = read end, [1] = write end (for packets from network)
// write_pipe: [0] = read end, [1] = write end (for packets to network)
if (pipe(vtun->read_pipe) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to create read pipe: %s", strerror(errno));
free(vtun);
return NULL;
}
if (pipe(vtun->write_pipe) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to create write pipe: %s", strerror(errno));
close(vtun->read_pipe[0]);
close(vtun->read_pipe[1]);
free(vtun);
return NULL;
}
// Set non-blocking mode on pipe ends
for (int i = 0; i < 2; i++) {
int flags = fcntl(vtun->read_pipe[i], F_GETFL, 0);
if (flags == -1 || fcntl(vtun->read_pipe[i], F_SETFL, flags | O_NONBLOCK) < 0) {
DEBUG_WARN(DEBUG_CATEGORY_TEST, "Failed to set non-blocking on read pipe[%d]: %s", i, strerror(errno));
}
flags = fcntl(vtun->write_pipe[i], F_GETFL, 0);
if (flags == -1 || fcntl(vtun->write_pipe[i], F_SETFL, flags | O_NONBLOCK) < 0) {
DEBUG_WARN(DEBUG_CATEGORY_TEST, "Failed to set non-blocking on write pipe[%d]: %s", i, strerror(errno));
}
}
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Virtual TUN created: %s (read_fd=%d, write_fd=%d)",
vtun->ifname, vtun->read_pipe[0], vtun->write_pipe[1]);
return vtun;
}
// Cleanup virtual TUN
void virtual_tun_destroy(struct virtual_tun* vtun) {
if (!vtun) return;
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Destroying virtual TUN: %s (stats: sent=%zu packets/%zu bytes, recv=%zu packets/%zu bytes)",
vtun->ifname, vtun->stats.packets_sent, vtun->stats.bytes_sent,
vtun->stats.packets_received, vtun->stats.bytes_received);
// Close all pipe file descriptors
if (vtun->read_pipe[0] >= 0) close(vtun->read_pipe[0]);
if (vtun->read_pipe[1] >= 0) close(vtun->read_pipe[1]);
if (vtun->write_pipe[0] >= 0) close(vtun->write_pipe[0]);
if (vtun->write_pipe[1] >= 0) close(vtun->write_pipe[1]);
free(vtun);
}
// Get file descriptor for reading (simulates TUN device read)
int virtual_tun_get_read_fd(struct virtual_tun* vtun) {
if (!vtun || !vtun->enabled) return -1;
return vtun->read_pipe[0]; // Read end of read pipe
}
// Get file descriptor for writing (simulates TUN device write)
int virtual_tun_get_write_fd(struct virtual_tun* vtun) {
if (!vtun || !vtun->enabled) return -1;
return vtun->write_pipe[1]; // Write end of write pipe
}
// Inject packet into virtual TUN (simulates packet from network)
int virtual_tun_inject_packet(struct virtual_tun* vtun, const uint8_t* packet, size_t len) {
if (!vtun || !vtun->enabled || !packet || len == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Invalid parameters for packet injection");
return -1;
}
if (len > VIRTUAL_TUN_MAX_PACKET_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Packet too large: %zu > %d", len, VIRTUAL_TUN_MAX_PACKET_SIZE);
return -1;
}
// Write packet to read pipe (simulates packet arriving from network)
ssize_t written = write(vtun->read_pipe[1], packet, len);
if (written < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to inject packet: %s", strerror(errno));
}
return -1;
}
vtun->stats.packets_received++;
vtun->stats.bytes_received += written;
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Injected packet into %s: %zu bytes", vtun->ifname, written);
return 0;
}
// Read packet from virtual TUN (captures packets going to network)
ssize_t virtual_tun_read_packet(struct virtual_tun* vtun, uint8_t* buffer, size_t max_len) {
if (!vtun || !vtun->enabled || !buffer || max_len == 0) {
return -1;
}
// Read from write pipe (captures packets going to network)
ssize_t bytes_read = read(vtun->write_pipe[0], buffer, max_len);
if (bytes_read < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to read packet: %s", strerror(errno));
}
return -1;
}
if (bytes_read > 0) {
vtun->stats.packets_sent++;
vtun->stats.bytes_sent += bytes_read;
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Read packet from %s: %zu bytes", vtun->ifname, bytes_read);
}
return bytes_read;
}
// Write packet to virtual TUN (sends packet to network)
ssize_t virtual_tun_write_packet(struct virtual_tun* vtun, const uint8_t* packet, size_t len) {
if (!vtun || !vtun->enabled || !packet || len == 0) {
return -1;
}
if (len > VIRTUAL_TUN_MAX_PACKET_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Packet too large: %zu > %d", len, VIRTUAL_TUN_MAX_PACKET_SIZE);
return -1;
}
// Write packet to write pipe (sends to network)
ssize_t written = write(vtun->write_pipe[1], packet, len);
if (written < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to write packet: %s", strerror(errno));
}
return -1;
}
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Wrote packet to %s: %zu bytes", vtun->ifname, written);
return written;
}
// Get virtual TUN statistics
void virtual_tun_get_stats(struct virtual_tun* vtun, size_t* packets_sent, size_t* packets_received,
size_t* bytes_sent, size_t* bytes_received) {
if (!vtun) return;
if (packets_sent) *packets_sent = vtun->stats.packets_sent;
if (packets_received) *packets_received = vtun->stats.packets_received;
if (bytes_sent) *bytes_sent = vtun->stats.bytes_sent;
if (bytes_received) *bytes_received = vtun->stats.bytes_received;
}
// Reset virtual TUN statistics
void virtual_tun_reset_stats(struct virtual_tun* vtun) {
if (!vtun) return;
memset(&vtun->stats, 0, sizeof(vtun->stats));
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Reset statistics for virtual TUN: %s", vtun->ifname);
}

55
!/test_virtual_tun.h

@ -1,55 +0,0 @@
#ifndef TEST_VIRTUAL_TUN_H
#define TEST_VIRTUAL_TUN_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <sys/types.h>
#define VIRTUAL_TUN_MAX_PACKET_SIZE 65536
struct virtual_tun {
int read_pipe[2]; // [0] = read end, [1] = write end
int write_pipe[2]; // [0] = read end, [1] = write end
char ifname[32]; // Virtual interface name
bool enabled;
void* test_context; // For callbacks
// Statistics
struct {
size_t packets_sent;
size_t packets_received;
size_t bytes_sent;
size_t bytes_received;
} stats;
};
// Create virtual TUN with bidirectional pipes
struct virtual_tun* virtual_tun_create(const char* ifname);
// Cleanup virtual TUN
void virtual_tun_destroy(struct virtual_tun* vtun);
// Get file descriptor for reading (simulates TUN device)
int virtual_tun_get_read_fd(struct virtual_tun* vtun);
// Get file descriptor for writing (simulates TUN device)
int virtual_tun_get_write_fd(struct virtual_tun* vtun);
// Inject packet into virtual TUN (simulates packet from network)
int virtual_tun_inject_packet(struct virtual_tun* vtun, const uint8_t* packet, size_t len);
// Read packet from virtual TUN (captures packets going to network)
ssize_t virtual_tun_read_packet(struct virtual_tun* vtun, uint8_t* buffer, size_t max_len);
// Write packet to virtual TUN (sends packet to network)
ssize_t virtual_tun_write_packet(struct virtual_tun* vtun, const uint8_t* packet, size_t len);
// Get virtual TUN statistics
void virtual_tun_get_stats(struct virtual_tun* vtun, size_t* packets_sent, size_t* packets_received,
size_t* bytes_sent, size_t* bytes_received);
// Reset virtual TUN statistics
void virtual_tun_reset_stats(struct virtual_tun* vtun);
#endif // TEST_VIRTUAL_TUN_H

339
!/tun_if.c

@ -1,339 +0,0 @@
// tun_if.c - TUN interface management implementation
#define _POSIX_C_SOURCE 200809L
#include "tun_if.h"
#include "../lib/debug_config.h"
#include "routing.h"
#include "../lib/u_async.h"
#include "etcp_connections.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <errno.h>
// Create TUN device
static int create_tun_device(char *ifname, size_t ifname_len) {
struct ifreq ifr;
int fd;
fd = open("/dev/net/tun", O_RDWR);
if (fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to open /dev/net/tun: %s", strerror(errno));
return -1;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
if (ifname && ifname_len > 0 && ifname[0] != '\0') {
strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
}
if (ioctl(fd, TUNSETIFF, &ifr) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to configure TUN device: %s", strerror(errno));
close(fd);
return -1;
}
if (ifname && ifname_len > 0) {
strncpy(ifname, ifr.ifr_name, ifname_len - 1);
ifname[ifname_len - 1] = '\0';
}
return fd;
}
// Run system command
static int run_command(const char *cmd) {
int ret = system(cmd);
if (ret == -1) {
perror("system");
return -1;
}
if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Command failed: %s", cmd);
return -1;
}
return 0;
}
// Parse IP address and mask
static int parse_ip_mask(const char *ip_addr, char *ip, size_t ip_len, int *mask) {
if (!ip_addr || !ip || !mask) return -1;
char *slash = strchr(ip_addr, '/');
if (!slash) {
// Default mask /32
*mask = 32;
strncpy(ip, ip_addr, ip_len - 1);
ip[ip_len - 1] = '\0';
} else {
size_t ip_size = slash - ip_addr;
if (ip_size >= ip_len) return -1;
strncpy(ip, ip_addr, ip_size);
ip[ip_size] = '\0';
char *endptr;
long mask_val = strtol(slash + 1, &endptr, 10);
if (*endptr != '\0' || mask_val < 0 || mask_val > 32) return -1;
*mask = (int)mask_val;
}
return 0;
}
int tun_create(struct tun_config *config) {
if (!config) {
errno = EINVAL;
return -1;
}
// Create TUN device
int fd = create_tun_device(config->ifname, sizeof(config->ifname));
if (fd < 0) {
return -1;
}
config->fd = fd;
// Configure IP if specified
if (config->ip_addr[0] != '\0') {
if (tun_set_ip(config->ifname, config->ip_addr) < 0) {
close(fd);
return -1;
}
}
// Set MTU if specified
if (config->mtu > 0) {
if (tun_set_mtu(config->ifname, config->mtu) < 0) {
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Failed to set MTU %d on %s: %s", config->mtu, config->ifname, strerror(errno));
}
}
// Bring interface up
if (tun_set_up(config->ifname) < 0) {
close(fd);
return -1;
}
config->is_up = 1;
return 0;
}
int tun_set_ip(const char *ifname, const char *ip_addr) {
if (!ifname || !ip_addr) {
errno = EINVAL;
return -1;
}
char ip[64];
int mask;
if (parse_ip_mask(ip_addr, ip, sizeof(ip), &mask) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid IP address format: %s", ip_addr);
errno = EINVAL;
return -1;
}
char cmd[256];
snprintf(cmd, sizeof(cmd), "ip addr add %s dev %s", ip_addr, ifname);
if (run_command(cmd) < 0) {
return -1;
}
return 0;
}
int tun_set_up(const char *ifname) {
if (!ifname) {
errno = EINVAL;
return -1;
}
char cmd[256];
snprintf(cmd, sizeof(cmd), "ip link set %s up", ifname);
if (run_command(cmd) < 0) {
return -1;
}
return 0;
}
int tun_set_mtu(const char *ifname, int mtu) {
if (!ifname || mtu <= 0) {
errno = EINVAL;
return -1;
}
char cmd[256];
snprintf(cmd, sizeof(cmd), "ip link set %s mtu %d", ifname, mtu);
if (run_command(cmd) < 0) {
return -1;
}
return 0;
}
ssize_t tun_read(int fd, uint8_t *buffer, size_t size) {
if (fd < 0 || !buffer || size == 0) {
errno = EINVAL;
return -1;
}
ssize_t nread = read(fd, buffer, size);
if (nread < 0) {
perror("tun_read");
}
return nread;
}
ssize_t tun_write(int fd, const uint8_t *buffer, size_t size) {
if (fd < 0 || !buffer || size == 0) {
errno = EINVAL;
return -1;
}
ssize_t nwritten = write(fd, buffer, size);
if (nwritten < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to write to TUN device fd=%d: %s", fd, strerror(errno));
}
return nwritten;
}
void tun_close(struct tun_config *config) {
if (!config) return;
if (config->fd >= 0) {
close(config->fd);
config->fd = -1;
}
config->is_up = 0;
}
int tun_get_config(const char *ifname, struct tun_config *config) {
if (!ifname || !config) {
errno = EINVAL;
return -1;
}
// TODO: Implement reading current interface configuration
// This would require parsing ip addr show output
memset(config, 0, sizeof(*config));
strncpy(config->ifname, ifname, sizeof(config->ifname) - 1);
config->fd = -1;
return 0;
}
// Extract destination IPv4 address from packet
static uint32_t tun_get_dest_ip(const uint8_t *packet, size_t len) {
if (len < 20) return 0; // Minimum IPv4 header size
// Check IP version (first nibble)
uint8_t version = (packet[0] >> 4) & 0x0F;
if (version != 4) return 0;
// Destination IP is at offset 16
uint32_t dest_ip;
memcpy(&dest_ip, packet + 16, 4);
return dest_ip;
}
// Extract destination IPv4 address from packet
static uint32_t get_dest_ip(const uint8_t *packet, size_t len) {
if (len < 20) return 0; // Minimum IPv4 header size
// Check IP version (first nibble)
uint8_t version = (packet[0] >> 4) & 0x0F;
if (version != 4) return 0;
// Destination IP is at offset 16
uint32_t dest_ip;
memcpy(&dest_ip, packet + 16, 4);
return dest_ip;
}
// Callback for TUN device read events
void tun_read_callback(int fd, void* user_arg) {
struct UTUN_INSTANCE *instance = (struct UTUN_INSTANCE*)user_arg;
uint8_t buffer[MAX_PACKET_SIZE];
// Read from TUN device
ssize_t nread = tun_read(fd, buffer, sizeof(buffer));
if (nread < 0) {
if (errno == EINTR) return;
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to read from TUN device %s: %s", instance->tun.ifname, strerror(errno));
return;
}
if (nread > 0) {
// Route packet based on destination IP
uint32_t dest_ip = get_dest_ip(buffer, nread);
struct route_entry route;
if (routing_table_lookup(instance->routing_table, dest_ip, &route)) {
// Found route, send to next hop connection
if (route.next_hop) {
struct sockaddr_in dest_addr;
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = route.next_hop_ip;
// todo: отправлять в модуль роутинга все входящие пакеты. когда этот модуль будет. модуль роутинга это не роутинг таблица (routing.h)
// if (etcp_connections_send(route.next_hop, buffer, nread, (struct sockaddr*)&dest_addr, sizeof(dest_addr)) < 0) {
// DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to send packet via route");
// }
} else {
// Local route - no forwarding needed
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "Local packet, no forwarding");
}
} else {
// No route found, drop packet
char ip_str[16];
ip_to_string(dest_ip, ip_str);
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "No route for destination IP %s", ip_str);
}
}
}
// Register sockets with uasync
int utun_instance_register_sockets(struct UTUN_INSTANCE *instance) {
if (!instance || !instance->ua || instance->tun.fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Invalid instance or TUN fd");
return -1;
}
// Register TUN file descriptor
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");
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Registered TUN socket (fd=%d)", instance->tun.fd);
return 0;
}
// Unregister sockets
void utun_instance_unregister_sockets(struct UTUN_INSTANCE *instance) {
if (!instance || !instance->ua) return;
if (instance->tun_socket_id) {
uasync_remove_socket(instance->ua, instance->tun_socket_id);
instance->tun_socket_id = NULL;
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Unregistered TUN socket: %s", instance->tun.ifname);
}
}

107
!/tun_if.h

@ -1,107 +0,0 @@
// tun_if.h - TUN interface management for utun
#ifndef TUN_IF_H
#define TUN_IF_H
#include <stdint.h>
#include <stddef.h>
#include <sys/types.h>
// Forward declarations
struct UTUN_INSTANCE;
#ifdef __cplusplus
extern "C" {
#endif
#define MAX_PACKET_SIZE 1500
// TUN interface configuration
struct tun_config {
char ifname[16]; // Interface name (e.g., "tun12")
char ip_addr[64]; // IP address with mask (e.g., "10.0.0.1/24")
int mtu; // MTU size
int fd; // File descriptor
uint8_t is_up; // 1 if interface is up
// Statistics
uint64_t bytes_read; // Bytes read from TUN
uint64_t bytes_written; // Bytes written to TUN
uint32_t packets_read; // Packets read from TUN
uint32_t packets_written; // Packets written to TUN
uint32_t read_errors; // Read errors
uint32_t write_errors; // Write errors
};
/**
* @brief Create and configure TUN interface
* @param config TUN configuration (ifname can be empty for auto)
* @return 0 on success, -1 on error
*/
int tun_create(struct tun_config *config);
/**
* @brief Configure IP address on TUN interface
* @param ifname Interface name
* @param ip_addr IP address with mask (e.g., "10.0.0.1/24")
* @return 0 on success, -1 on error
*/
int tun_set_ip(const char *ifname, const char *ip_addr);
/**
* @brief Bring TUN interface up
* @param ifname Interface name
* @return 0 on success, -1 on error
*/
int tun_set_up(const char *ifname);
/**
* @brief Set MTU on TUN interface
* @param ifname Interface name
* @param mtu MTU value
* @return 0 on success, -1 on error
*/
int tun_set_mtu(const char *ifname, int mtu);
/**
* @brief Read packet from TUN interface
* @param fd TUN file descriptor
* @param buffer Buffer to store packet
* @param size Buffer size
* @return Number of bytes read, -1 on error
*/
ssize_t tun_read(int fd, uint8_t *buffer, size_t size);
/**
* @brief Write packet to TUN interface
* @param fd TUN file descriptor
* @param buffer Packet data
* @param size Packet size
* @return Number of bytes written, -1 on error
*/
ssize_t tun_write(int fd, const uint8_t *buffer, size_t size);
/**
* @brief Close TUN interface
* @param config TUN configuration
*/
void tun_close(struct tun_config *config);
/**
* @brief Get current TUN configuration
* @param ifname Interface name
* @param config Output configuration
* @return 0 on success, -1 on error
*/
int tun_get_config(const char *ifname, struct tun_config *config);
int utun_instance_register_sockets(struct UTUN_INSTANCE *instance);
void utun_instance_unregister_sockets(struct UTUN_INSTANCE *instance);
// TUN callback function
void tun_read_callback(int fd, void* user_arg);
#ifdef __cplusplus
}
#endif
#endif /* TUN_IF_H */

321
!/utun.c

@ -1,321 +0,0 @@
// utun.c - Main application for utun VPN tunnel
#define _DEFAULT_SOURCE
#define _POSIX_C_SOURCE 200809L
#include "config_parser.h"
#include "etcp_connections.h"
#include "tun_if.h"
#include "secure_channel.h"
#include "routing.h"
#include "utun_instance.h"
#include "u_async.h"
#include "debug_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <getopt.h>
#include <arpa/inet.h>
/*
Архитектура:
main -> init utun instnaces -> mainloop()
utun instances: можно stop() - он ывзывает закрытие всех etcp (etcp_close) которые вызовут закрытие подключений), вызывает закрытие всех etcp сокетов
*/
// Global wakeup pipe write fd for signal handler
static int g_wakeup_pipe_write_fd = -1;
#define DEFAULT_CONFIG "utun.conf"
#define DEFAULT_PIDFILE "/var/run/utun.pid"
// Command line arguments
typedef struct {
char *config_file;
char *pid_file;
char *log_file;
char *debug_config;
int foreground;
int help;
} cmd_args_t;
// Global state
// Parse subnet string
static int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_length) {
if (!subnet_str || !network || !prefix_length) return -1;
char ip[64];
int prefix;
if (sscanf(subnet_str, "%[^/]/%d", ip, &prefix) != 2) return -1;
if (prefix < 0 || prefix > 32) return -1;
struct in_addr addr;
if (inet_pton(AF_INET, ip, &addr) != 1) return -1;
*network = addr.s_addr;
*prefix_length = (uint8_t)prefix;
return 0;
}
// Extract destination IPv4 address from packet
static uint32_t get_dest_ip(const uint8_t *packet, size_t len) {
if (len < 20) return 0; // Minimum IPv4 header size
// Check IP version (first nibble)
uint8_t version = (packet[0] >> 4) & 0x0F;
if (version != 4) return 0;
// Destination IP is at offset 16
uint32_t dest_ip;
memcpy(&dest_ip, packet + 16, 4);
return dest_ip;
}
// Parse command line arguments
static void parse_args(int argc, char *argv[], cmd_args_t *args) {
memset(args, 0, sizeof(*args));
args->config_file = DEFAULT_CONFIG;
args->pid_file = DEFAULT_PIDFILE;
args->log_file = NULL;
args->debug_config = NULL;
args->foreground = 0;
args->help = 0;
static struct option long_options[] = {
{"config", required_argument, 0, 'c'},
{"pidfile", required_argument, 0, 'p'},
{"log", required_argument, 0, 'l'},
{"debug", required_argument, 0, 'd'},
{"foreground", no_argument, 0, 'f'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, "c:p:l:d:fh",
long_options, &option_index)) != -1) {
switch (opt) {
case 'c':
args->config_file = optarg;
break;
case 'p':
args->pid_file = optarg;
break;
case 'l':
args->log_file = optarg;
break;
case 'f':
args->foreground = 1;
break;
case 'd':
args->debug_config = optarg;
break;
case 'h':
args->help = 1;
break;
default:
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Unknown option: %c", opt);
exit(1);
}
}
}
// Print usage
static void print_usage(const char *progname) {
printf("Usage: %s [OPTIONS]\n", progname);
printf("Secure VPN tunnel over UDP with TUN interface\n\n");
printf("Options:\n");
printf(" -c, --config FILE Configuration file (default: %s)\n", DEFAULT_CONFIG);
printf(" -p, --pidfile FILE PID file (default: %s)\n", DEFAULT_PIDFILE);
printf(" -l, --log FILE Log file (default: stderr)\n");
printf(" -d, --debug CONFIG Debug configuration (e.g., \"etcp:debug,routing:info\")\n");
printf(" -f, --foreground Run in foreground (don't daemonize)\n");
printf(" -h, --help Show this help\n");
printf("\nExamples:\n");
printf(" %s -c myconfig.conf\n", progname);
printf(" %s --config server.conf --pidfile /var/run/utun.pid\n", progname);
}
// Write PID file
static int write_pidfile(const char *pidfile) {
if (!pidfile) return -1;
FILE *fp = fopen(pidfile, "w");
if (!fp) {
perror("fopen pidfile");
return -1;
}
fprintf(fp, "%d\n", getpid());
fclose(fp);
return 0;
}
// Remove PID file
static void remove_pidfile(const char *pidfile) {
if (pidfile) {
unlink(pidfile);
}
}
// Daemonize process
static int daemonize(void) {
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return -1;
}
if (pid > 0) {
// Parent exits
exit(0);
}
// Child becomes session leader
if (setsid() < 0) {
perror("setsid");
return -1;
}
// Close standard file descriptors
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// Redirect to /dev/null
int fd = open("/dev/null", O_RDWR);
if (fd >= 0) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > 2) close(fd);
}
return 0;
}
// Open log file
static FILE* open_logfile(const char *logfile) {
if (!logfile) return stderr;
FILE *fp = fopen(logfile, "a");
if (!fp) {
perror("fopen logfile");
return stderr;
}
// Set line buffering
setlinebuf(fp);
return fp;
}
// Main function
static volatile sig_atomic_t g_running = 1;
struct UASYNC* main_ua = NULL;
static void signal_handler(int sig) {
(void)sig;
g_running = 0;
if (main_ua) {
uasync_wakeup(main_ua);
}
}
int main(int argc, char *argv[]) {
cmd_args_t args;
parse_args(argc, argv, &args);
if (args.help) {
print_usage(argv[0]);
return 0;
}
// Initialize global debug system early if configured from command line
if (args.debug_config) {
debug_config_init();
if (debug_parse_config(args.debug_config) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid debug configuration: %s", args.debug_config);
return 1;
}
}
// Create uasync instance
struct UASYNC* ua = uasync_create();
main_ua=ua;
if (!ua) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to create uasync instance");
return 1;
}
// Create and initialize instance
struct UTUN_INSTANCE *instance = utun_instance_create(ua, args.config_file, args.log_file);
if (!instance) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to create UTUN instance");
return 1;
}
// Print config for debugging
if (args.foreground) {
print_config(instance->config);
}
// Initialize all components (TUN, routing, connections)
if (utun_instance_init(instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to initialize instance");
utun_instance_destroy(instance);
return 1;
}
// Register sockets with uasync
if (utun_instance_register_sockets(instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to register sockets");
utun_instance_destroy(instance);
return 1;
}
// Setup signal handlers
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGHUP, signal_handler);
// Daemonize if not in foreground
if (!args.foreground) {
if (daemonize() < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to daemonize");
utun_instance_destroy(instance);
return 1;
}
}
// Write PID file
if (write_pidfile(args.pid_file) < 0) {
utun_instance_destroy(instance);
return 1;
}
while (instance->running) uasync_poll(ua, 100);
// Cleanup
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Shutdown");
utun_instance_unregister_sockets(instance);
utun_instance_destroy(instance);
remove_pidfile(args.pid_file);
return 0;
}

42
!/utun.conf

@ -1,42 +0,0 @@
[global]
tun_ip=10.0.0.1
mtu=1500 # MTU for all connections (0 = use default 1500)
control_ip=127.0.0.1
control_port=12345
net_debug=0
my_node_id=61be9d4cd3c60c2d
my_private_key=1313912e5d34768983b0e06530a48c77816d228a5b5605e1ab3dc443d107a3dc
my_public_key=
[routing]
allowed_subnet=10.0.0.0/24
allowed_subnet=10.22.0.0/16
allowed_subnet=10.23.0.0/16
my_subnet=10.23.5.0/24
my_subnet=10.23.6.0/24
# мои адреса и каналы
[server: lo0_test]
addr=127.0.0.1:1330
#so_mark=100
#netif=eth0
type=nat # public / nat / private
[server: lan1]
addr=192.168.29.117:1333
so_mark=100
netif=eth0
type=public # public / nat / private
[client: client_test1]
# линки
link=lo0_test:192.168.0.20:1234
#link=wired1_fast:1.2.3.4:1234
link=lan1:192.168.0.20:1234
#link=wireless_bkp:1.2.3.4:1234
keepalive=1
peer_public_key=deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbee

388
!/utun_instance.c

@ -1,388 +0,0 @@
// 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.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;
// 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");
}

314
!/utun_instance.c.backup

@ -1,314 +0,0 @@
// 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 "../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 <arpa/inet.h>
// Forward declarations
static void tun_read_callback(int fd, void* user_arg);
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
struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char *config_file, const char *log_file) {
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;
// 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
if (log_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);
/*
// Initialize TUN device
if (tun_create(&instance->tun) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create TUN device");
return -1;
}
// Configure TUN device
if (tun_set_ip(instance->tun.ifname, instance->config->global.tun_ip) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to set TUN IP");
tun_close(&instance->tun);
return -1;
}
if (instance->config->global.mtu > 0) {
if (tun_set_mtu(instance->tun.ifname, instance->config->global.mtu) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to set TUN MTU");
tun_close(&instance->tun);
return -1;
}
}
if (tun_set_up(instance->tun.ifname) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to bring up TUN interface");
tun_close(&instance->tun);
return -1;
}
// Create routing table
instance->routing_table = routing_table_create();
if (!instance->routing_table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to create routing table");
return -1;
}
*/
// 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;
printf("[INSTANCE_DESTROY] Starting cleanup for instance %p\n", instance);
// Диагностика ресурсов ДО cleanup
utun_instance_diagnose_leaks(instance, "BEFORE_CLEANUP");
// Stop running if not already
instance->running = 0;
// 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_MEMORY, "[INSTANCE_DESTROY] Closing TUN interface");
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;
// 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) {
printf("[DIAGNOSE] NULL instance for phase: %s\n", 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");
}

230
!/utun_instance.c1

@ -1,230 +0,0 @@
// 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 "../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 <arpa/inet.h>
// Forward declarations
static void tun_read_callback(int fd, void* user_arg);
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
struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char *config_file, const char *log_file) {
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;
// 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
if (log_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);
/*
// Initialize TUN device
if (tun_create(&instance->tun) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create TUN device");
return -1;
}
// Configure TUN device
if (tun_set_ip(instance->tun.ifname, instance->config->global.tun_ip) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to set TUN IP");
tun_close(&instance->tun);
return -1;
}
if (instance->config->global.mtu > 0) {
if (tun_set_mtu(instance->tun.ifname, instance->config->global.mtu) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to set TUN MTU");
tun_close(&instance->tun);
return -1;
}
}
if (tun_set_up(instance->tun.ifname) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to bring up TUN interface");
tun_close(&instance->tun);
return -1;
}
// Create routing table
instance->routing_table = routing_table_create();
if (!instance->routing_table) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to create routing table");
return -1;
}
*/
// 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;
printf("[INSTANCE_DESTROY] Starting cleanup for instance %p\n", instance);
// Stop running
instance->running = 0;
// Cleanup ETCP sockets and connections FIRST (before destroying uasync)
printf("[INSTANCE_DESTROY] Cleaning up ETCP sockets and connections\n");
if (instance->etcp_sockets) {
printf("[INSTANCE_DESTROY] Found ETCP sockets to cleanup\n");
struct ETCP_SOCKET* sock = instance->etcp_sockets;
while (sock) {
struct ETCP_SOCKET* next = sock->next;
printf("[INSTANCE_DESTROY] Removing socket %p, fd=%d\n", sock, sock->fd);
etcp_socket_remove(sock); // Полный cleanup сокета
sock = next;
}
instance->etcp_sockets = NULL;
printf("[INSTANCE_DESTROY] ETCP sockets cleanup complete\n");
}
if (instance->connections) {
printf("[INSTANCE_DESTROY] Found ETCP connections to cleanup\n");
struct ETCP_CONN* conn = instance->connections;
while (conn) {
struct ETCP_CONN* next = conn->next;
printf("[INSTANCE_DESTROY] Closing connection %p\n", conn);
etcp_connection_close(conn); // Закрыть соединение
conn = next;
}
instance->connections = NULL;
printf("[INSTANCE_DESTROY] ETCP connections cleanup complete\n");
}
// Cleanup other components
if (instance->routing_table) {
routing_table_destroy(instance->routing_table);
}
// Cleanup TUN
if (instance->tun.fd >= 0) {
tun_close(&instance->tun);
}
// Cleanup config
if (instance->config) {
free_config(instance->config);
}
// Close log file
if (instance->log_fp) {
fclose(instance->log_fp);
}
// FINALLY destroy uasync (after all resources are cleaned up)
printf("[INSTANCE_DESTROY] Destroying uasync instance\n");
if (instance->ua) {
uasync_destroy(instance->ua);
}
// Free the instance memory
printf("[INSTANCE_DESTROY] Freeing instance memory\n");
free(instance);
printf("[INSTANCE_DESTROY] Instance destroyed completely\n");
// Cleanup TUN
if (instance->tun.fd >= 0) {
tun_close(&instance->tun);
}
// Cleanup config
if (instance->config) {
free_config(instance->config);
}
// Close log file
if (instance->log_fp) {
fclose(instance->log_fp);
}
// uasync cleanup will handle its built-in wakeup pipe
// Clear global instance
if (g_instance == instance) {
g_instance = NULL;
}
free(instance);
}
// 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;
// Initialize connections
if (init_connections(instance) < 0) {
return -1;
}
return 0;
}

72
!/utun_instance.h

@ -1,72 +0,0 @@
#ifndef UTUN_INSTANCE_H
#define UTUN_INSTANCE_H
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "../lib/memory_pool.h"
#include "secure_channel.h"
#include "tun_if.h"
// Forward declarations
struct utun_config;
struct uasync_s;
struct routing_table;
struct ETCP_CONN;
struct ETCP_SOCKET;
// uTun instance configuration
struct UTUN_INSTANCE {
// Configuration (moved from utun_state)
struct utun_config *config;
// TUN interface
struct tun_config tun;
void *tun_socket_id; // Socket ID from uasync_add_socket
// Identification
uint64_t node_id;
struct SC_MYKEYS my_keys;
// Main async context
struct UASYNC* ua;
// State
int running;
FILE *log_fp;
struct memory_pool* pkt_pool;
// Routing
struct routing_table *routing_table;
// Connections
struct ETCP_CONN* connections;// linked-list
int connections_count; // Number of connections
// Active sockets
struct ETCP_SOCKET* etcp_sockets;// linked-list
};
// Instance creation flags
#define UTUN_CREATE_ALLOW_TUN_FAILURE 0x0001 // Continue if TUN creation fails
#define UTUN_CREATE_NO_TUN 0x0002 // Skip TUN initialization entirely
#define UTUN_CREATE_TEST_MODE 0x0004 // Enable test hooks and virtual interfaces
#define UTUN_CREATE_NO_SOCKET_BIND 0x0008 // Skip socket binding (for test injection)
// Functions
struct UTUN_INSTANCE* utun_instance_create_ex(struct UASYNC* ua, const char* config_file, const char* log_file, uint32_t flags);
// Backward compatibility wrapper
static inline struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char* config_file, const char* log_file) {
return utun_instance_create_ex(ua, config_file, log_file, 0);
}
void utun_instance_destroy(struct UTUN_INSTANCE* instance);
int utun_instance_init(struct UTUN_INSTANCE *instance);
void utun_instance_run(struct UTUN_INSTANCE *instance);
void utun_instance_stop(struct UTUN_INSTANCE *instance);
// Diagnostic function for memory leak analysis
void utun_instance_diagnose_leaks(struct UTUN_INSTANCE* instance, const char* phase);
#endif // UTUN_INSTANCE_H

586
!/utun_test_framework.c

@ -1,586 +0,0 @@
#include "utun_test_framework.h"
#include "utun_instance.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#include "../lib/ll_queue.h"
#include "test_udp_socket.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
// External mutex from test_udp_socket.c
extern pthread_mutex_t g_registry_mutex;
#ifndef DEBUG_CATEGORY_TEST
#define DEBUG_CATEGORY_TEST (1 << 30)
#endif
// Default test configurations
const struct test_instance_config TEST_SERVER_CONFIG = {
.config_file = "test_server.conf",
.node_id = 0x1111222233334444,
.port_base = 40000,
.enable_tun = false,
.enable_socket_hooks = true,
.enable_packet_capture = true,
.log_file = NULL
};
const struct test_instance_config TEST_CLIENT_CONFIG = {
.config_file = "test_client.conf",
.node_id = 0x8888777766665555,
.port_base = 40100,
.enable_tun = false,
.enable_socket_hooks = true,
.enable_packet_capture = true,
.log_file = NULL
};
// Forward declarations for internal functions
static int test_tun_create_hook(void* priv_data);
static ssize_t test_tun_read_hook(int fd, void *buf, size_t count, void* priv_data);
static ssize_t test_tun_write_hook(int fd, const void *buf, size_t count, void* priv_data);
static int test_socket_create_hook(int domain, int type, int protocol, void* context);
static void test_packet_capture_hook(struct UTUN_INSTANCE* instance, const uint8_t* packet, size_t len,
const char* direction, void* context);
static void test_cleanup_captured_packets(struct test_instance* test);
// Create test instance with virtual components
struct test_instance* test_create_instance(const struct test_instance_config* config) {
if (!config) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "NULL configuration provided");
return NULL;
}
struct test_instance* test = calloc(1, sizeof(struct test_instance));
if (!test) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to allocate test instance");
return NULL;
}
// Copy configuration
test->config = *config;
// Create uasync instance
test->ua = uasync_create();
if (!test->ua) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to create uasync instance");
free(test);
return NULL;
}
// Setup test hooks based on configuration
if (config->enable_tun || config->enable_socket_hooks || config->enable_packet_capture) {
test->hooks.test_context = test;
if (config->enable_tun) {
// Create virtual TUN
char vtun_name[32];
snprintf(vtun_name, sizeof(vtun_name), "vtun%ld", config->node_id & 0xFFFF);
test->vtun = virtual_tun_create(vtun_name);
if (!test->vtun) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to create virtual TUN");
uasync_destroy(test->ua);
free(test);
return NULL;
}
test->hooks.tun_create_override = test_tun_create_hook;
test->hooks.tun_read_override = test_tun_read_hook;
test->hooks.tun_write_override = test_tun_write_hook;
}
if (config->enable_socket_hooks) {
test->hooks.socket_create_override = test_socket_create_hook;
test->hooks.sendto_override = NULL; // Will be handled by virtual UDP sockets
test->hooks.recvfrom_override = NULL; // Will be handled by virtual UDP sockets
}
if (config->enable_packet_capture) {
test->hooks.packet_captured = test_packet_capture_hook;
}
// Install hooks
#ifdef TEST_BUILD
utun_test_hooks_set(&test->hooks);
test->hooks_installed = true;
#endif
}
// Allocate packet capture buffer
if (config->enable_packet_capture) {
test->captured_packets.capacity = 1000;
test->captured_packets.packets = calloc(test->captured_packets.capacity, sizeof(uint8_t*));
test->captured_packets.lengths = calloc(test->captured_packets.capacity, sizeof(size_t));
test->captured_packets.directions = calloc(test->captured_packets.capacity, sizeof(char*));
if (!test->captured_packets.packets || !test->captured_packets.lengths || !test->captured_packets.directions) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to allocate packet capture buffers");
if (test->vtun) virtual_tun_destroy(test->vtun);
uasync_destroy(test->ua);
free(test);
return NULL;
}
}
// Allocate UDP socket tracking
test->udp_socket_capacity = 16;
test->udp_sockets = calloc(test->udp_socket_capacity, sizeof(struct test_udp_socket*));
if (!test->udp_sockets) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to allocate UDP socket tracking");
test_cleanup_captured_packets(test);
if (test->vtun) virtual_tun_destroy(test->vtun);
uasync_destroy(test->ua);
free(test);
return NULL;
}
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Created test instance: node_id=0x%016lX, config=%s",
config->node_id, config->config_file);
return test;
}
// Destroy test instance
void test_destroy_instance(struct test_instance* test) {
if (!test) return;
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Destroying test instance: node_id=0x%016lX",
test->config.node_id);
// Stop instance if running
test_stop_instance(test);
// Clear hooks if installed
if (test->hooks_installed) {
#ifdef TEST_BUILD
utun_test_hooks_clear();
#endif
test->hooks_installed = false;
}
// Destroy virtual components
if (test->vtun) {
virtual_tun_destroy(test->vtun);
}
// Destroy UDP sockets
for (int i = 0; i < test->udp_socket_count; i++) {
if (test->udp_sockets[i]) {
test_udp_socket_destroy(test->udp_sockets[i]);
}
}
free(test->udp_sockets);
// Cleanup captured packets
test_cleanup_captured_packets(test);
// Destroy uasync
if (test->ua) {
uasync_destroy(test->ua);
}
// Destroy UTUN instance
if (test->utun) {
utun_instance_destroy(test->utun);
}
free(test);
}
// Start test instance
int test_start_instance(struct test_instance* test) {
if (!test) return -1;
if (test->utun) return 0; // Already started
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Starting test instance: node_id=0x%016lX", test->config.node_id);
// Determine flags for instance creation
uint32_t flags = 0;
if (!test->config.enable_tun) {
flags |= UTUN_CREATE_NO_TUN;
}
if (test->config.enable_socket_hooks || test->config.enable_packet_capture) {
flags |= UTUN_CREATE_TEST_MODE;
}
if (test->config.enable_socket_hooks) {
flags |= UTUN_CREATE_NO_SOCKET_BIND;
}
if (test->config.enable_tun) {
flags |= UTUN_CREATE_ALLOW_TUN_FAILURE; // Allow virtual TUN to fail gracefully
}
// Create UTUN instance
test->utun = utun_instance_create_ex(test->ua, test->config.config_file, test->config.log_file, flags);
if (!test->utun) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to create UTUN instance");
return -1;
}
// Initialize instance
if (utun_instance_init(test->utun) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to initialize UTUN instance");
utun_instance_destroy(test->utun);
test->utun = NULL;
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Test instance started successfully");
return 0;
}
// Stop test instance
void test_stop_instance(struct test_instance* test) {
if (!test || !test->utun) return;
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Stopping test instance: node_id=0x%016lX", test->config.node_id);
utun_instance_stop(test->utun);
}
// Inject packet into TUN interface
int test_inject_tun_packet(struct test_instance* test, const uint8_t* packet, size_t len) {
if (!test || !test->vtun || !packet || len == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Invalid parameters for TUN packet injection");
return -1;
}
return virtual_tun_inject_packet(test->vtun, packet, len);
}
// Inject UDP packet
int test_inject_udp_packet(struct test_instance* test, int socket_fd,
const uint8_t* packet, size_t len,
const struct sockaddr* src_addr, socklen_t addr_len) {
if (!test || !packet || len == 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Invalid parameters for UDP packet injection");
return -1;
}
struct test_udp_socket* sock = test_udp_socket_find_by_fd(socket_fd);
if (!sock) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Socket fd=%d not found", socket_fd);
return -1;
}
return test_udp_socket_inject(sock, packet, len, src_addr, addr_len);
}
// Get captured packets
size_t test_get_captured_packets(struct test_instance* test,
const char* direction,
uint8_t*** packets, size_t** lengths) {
if (!test || !direction || !packets || !lengths) return 0;
// Count packets matching direction
size_t count = 0;
for (size_t i = 0; i < test->captured_packets.count; i++) {
if (strcmp(test->captured_packets.directions[i], direction) == 0) {
count++;
}
}
if (count == 0) return 0;
// Allocate result arrays
*packets = calloc(count, sizeof(uint8_t*));
*lengths = calloc(count, sizeof(size_t));
if (!*packets || !*lengths) {
free(*packets);
free(*lengths);
return 0;
}
// Fill result arrays
size_t result_idx = 0;
for (size_t i = 0; i < test->captured_packets.count; i++) {
if (strcmp(test->captured_packets.directions[i], direction) == 0) {
(*packets)[result_idx] = test->captured_packets.packets[i];
(*lengths)[result_idx] = test->captured_packets.lengths[i];
result_idx++;
}
}
return count;
}
// Get captured packet count
size_t test_get_captured_packet_count(struct test_instance* test, const char* direction) {
if (!test || !direction) return 0;
size_t count = 0;
for (size_t i = 0; i < test->captured_packets.count; i++) {
if (strcmp(test->captured_packets.directions[i], direction) == 0) {
count++;
}
}
return count;
}
// Clear captured packets
void test_clear_captured_packets(struct test_instance* test) {
if (!test) return;
for (size_t i = 0; i < test->captured_packets.count; i++) {
if (test->captured_packets.packets[i]) {
free(test->captured_packets.packets[i]);
}
if (test->captured_packets.directions[i]) {
free(test->captured_packets.directions[i]);
}
}
test->captured_packets.count = 0;
}
// Wait for packet with timeout
int test_wait_for_packet(struct test_instance* test, const char* direction, int timeout_ms) {
if (!test || !direction || timeout_ms <= 0) return -1;
size_t initial_count = test_get_captured_packet_count(test, direction);
size_t current_count = initial_count;
struct timeval start, current;
gettimeofday(&start, NULL);
while (current_count == initial_count) {
gettimeofday(&current, NULL);
int elapsed_ms = (current.tv_sec - start.tv_sec) * 1000 + (current.tv_usec - start.tv_usec) / 1000;
if (elapsed_ms >= timeout_ms) {
DEBUG_WARN(DEBUG_CATEGORY_TEST, "Timeout waiting for %s packet after %dms", direction, timeout_ms);
return -1;
}
usleep(10000); // Sleep 10ms
current_count = test_get_captured_packet_count(test, direction);
}
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Detected %s packet after %dms", direction,
(current.tv_sec - start.tv_sec) * 1000 + (current.tv_usec - start.tv_usec) / 1000);
return 0;
}
// Two-instance test runner
int test_run_two_instances(const struct test_instance_config* server_config,
const struct test_instance_config* client_config,
void (*test_scenario)(struct test_instance* server,
struct test_instance* client),
int timeout_ms) {
if (!server_config || !client_config || !test_scenario) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Invalid parameters for two-instance test");
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Starting two-instance test");
// Create instances
struct test_instance* server = test_create_instance(server_config);
struct test_instance* client = test_create_instance(client_config);
if (!server || !client) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to create test instances");
test_destroy_instance(server);
test_destroy_instance(client);
return -1;
}
// Start instances
if (test_start_instance(server) < 0 || test_start_instance(client) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to start test instances");
test_destroy_instance(server);
test_destroy_instance(client);
return -1;
}
// Give instances time to initialize
sleep(1);
// Run test scenario
int result = 0;
if (timeout_ms > 0) {
// TODO: Implement timeout mechanism for test scenario
test_scenario(server, client);
} else {
test_scenario(server, client);
}
// Cleanup
test_destroy_instance(server);
test_destroy_instance(client);
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Two-instance test completed: %s", result == 0 ? "SUCCESS" : "FAILED");
return result;
}
// Get test instance statistics
void test_get_instance_stats(struct test_instance* test, size_t* tun_packets_sent,
size_t* tun_packets_received, size_t* udp_packets_sent,
size_t* udp_packets_received) {
if (!test) return;
if (test->vtun) {
virtual_tun_get_stats(test->vtun, tun_packets_sent, tun_packets_received, NULL, NULL);
}
// Aggregate UDP socket statistics
size_t udp_sent = 0, udp_received = 0;
for (int i = 0; i < test->udp_socket_count; i++) {
if (test->udp_sockets[i]) {
size_t sent, recv;
test_udp_socket_get_stats(test->udp_sockets[i], &sent, &recv, NULL, NULL, NULL, NULL);
udp_sent += sent;
udp_received += recv;
}
}
if (udp_packets_sent) *udp_packets_sent = udp_sent;
if (udp_packets_received) *udp_packets_received = udp_received;
}
// Utility functions
const char* test_get_direction_name(const char* direction) {
if (!direction) return "unknown";
if (strcmp(direction, "tun_in") == 0) return "TUN incoming";
if (strcmp(direction, "tun_out") == 0) return "TUN outgoing";
if (strcmp(direction, "udp_in") == 0) return "UDP incoming";
if (strcmp(direction, "udp_out") == 0) return "UDP outgoing";
return direction;
}
int test_compare_packets(const uint8_t* pkt1, size_t len1, const uint8_t* pkt2, size_t len2) {
if (!pkt1 || !pkt2) return -1;
if (len1 != len2) return -1;
return memcmp(pkt1, pkt2, len1);
}
void test_dump_packet(const char* prefix, const uint8_t* packet, size_t len) {
if (!prefix || !packet || len == 0) return;
printf("%s: %zu bytes\n", prefix, len);
for (size_t i = 0; i < len; i++) {
printf("%02X ", packet[i]);
if ((i + 1) % 16 == 0) printf("\n");
}
if (len % 16 != 0) printf("\n");
}
// Internal hook implementations
static int test_tun_create_hook(void* priv_data) {
struct test_instance* test = (struct test_instance*)priv_data;
if (!test || !test->vtun) return -1;
// Return the read fd as the TUN device fd
return virtual_tun_get_read_fd(test->vtun);
}
static ssize_t test_tun_read_hook(int fd, void *buf, size_t count, void* priv_data) {
struct test_instance* test = (struct test_instance*)priv_data;
if (!test || !test->vtun) return -1;
return virtual_tun_read_packet(test->vtun, buf, count);
}
static ssize_t test_tun_write_hook(int fd, const void *buf, size_t count, void* priv_data) {
struct test_instance* test = (struct test_instance*)priv_data;
if (!test || !test->vtun) return -1;
ssize_t result = virtual_tun_write_packet(test->vtun, buf, count);
// Capture packet if enabled
if (test->config.enable_packet_capture && result > 0) {
test_packet_capture_hook(test->utun, buf, result, "tun_out", test);
}
return result;
}
static int test_socket_create_hook(int domain, int type, int protocol, void* context) {
struct test_instance* test = (struct test_instance*)context;
if (!test || !test->config.enable_socket_hooks) return -1;
// Create virtual UDP socket
struct test_udp_socket* sock = test_udp_socket_create(domain);
if (!sock) return -1;
// Add to tracking
pthread_mutex_lock(&g_registry_mutex);
if (test->udp_socket_count >= test->udp_socket_capacity) {
// Expand array
int new_capacity = test->udp_socket_capacity * 2;
struct test_udp_socket** new_sockets = realloc(test->udp_sockets,
new_capacity * sizeof(struct test_udp_socket*));
if (!new_sockets) {
pthread_mutex_unlock(&g_registry_mutex);
test_udp_socket_destroy(sock);
return -1;
}
test->udp_sockets = new_sockets;
test->udp_socket_capacity = new_capacity;
}
test->udp_sockets[test->udp_socket_count++] = sock;
pthread_mutex_unlock(&g_registry_mutex);
return test_udp_socket_get_fd(sock);
}
static void test_packet_capture_hook(struct UTUN_INSTANCE* instance, const uint8_t* packet, size_t len,
const char* direction, void* context) {
struct test_instance* test = (struct test_instance*)context;
if (!test || !test->config.enable_packet_capture || !packet || len == 0) return;
// Expand capture buffer if needed
if (test->captured_packets.count >= test->captured_packets.capacity) {
size_t new_capacity = test->captured_packets.capacity * 2;
uint8_t** new_packets = realloc(test->captured_packets.packets,
new_capacity * sizeof(uint8_t*));
size_t* new_lengths = realloc(test->captured_packets.lengths,
new_capacity * sizeof(size_t));
char** new_directions = realloc(test->captured_packets.directions,
new_capacity * sizeof(char*));
if (!new_packets || !new_lengths || !new_directions) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Failed to expand packet capture buffer");
return;
}
test->captured_packets.packets = new_packets;
test->captured_packets.lengths = new_lengths;
test->captured_packets.directions = new_directions;
test->captured_packets.capacity = new_capacity;
}
// Store packet
uint8_t* packet_copy = malloc(len);
if (!packet_copy) return;
memcpy(packet_copy, packet, len);
test->captured_packets.packets[test->captured_packets.count] = packet_copy;
test->captured_packets.lengths[test->captured_packets.count] = len;
test->captured_packets.directions[test->captured_packets.count] = strdup(direction);
test->captured_packets.count++;
DEBUG_DEBUG(DEBUG_CATEGORY_TEST, "Captured packet: %s, %zu bytes", direction, len);
}
static void test_cleanup_captured_packets(struct test_instance* test) {
if (!test) return;
for (size_t i = 0; i < test->captured_packets.count; i++) {
if (test->captured_packets.packets[i]) {
free(test->captured_packets.packets[i]);
}
if (test->captured_packets.directions[i]) {
free(test->captured_packets.directions[i]);
}
}
free(test->captured_packets.packets);
free(test->captured_packets.lengths);
free(test->captured_packets.directions);
memset(&test->captured_packets, 0, sizeof(test->captured_packets));
}

114
!/utun_test_framework.h

@ -1,114 +0,0 @@
#ifndef UTUN_TEST_FRAMEWORK_H
#define UTUN_TEST_FRAMEWORK_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "utun_test_hooks.h"
#include "test_virtual_tun.h"
#include "test_udp_socket.h"
// Forward declarations
struct UTUN_INSTANCE;
struct UASYNC;
// Test instance configuration
struct test_instance_config {
const char* config_file;
uint64_t node_id;
int port_base;
bool enable_tun;
bool enable_socket_hooks;
bool enable_packet_capture;
const char* log_file;
};
// Test instance with virtual components
struct test_instance {
struct UTUN_INSTANCE* utun;
struct UASYNC* ua;
// Virtual components
struct virtual_tun* vtun;
struct test_udp_socket** udp_sockets;
int udp_socket_count;
int udp_socket_capacity;
// Packet capture
struct {
uint8_t** packets;
size_t* lengths;
char** directions; // "tun_in", "tun_out", "udp_in", "udp_out"
size_t count;
size_t capacity;
} captured_packets;
// Test configuration
struct test_instance_config config;
// Test context
void* user_context;
// Test hooks (for internal use)
struct utun_test_hooks hooks;
bool hooks_installed;
};
// Create test instance with virtual components
struct test_instance* test_create_instance(const struct test_instance_config* config);
// Destroy test instance
void test_destroy_instance(struct test_instance* test);
// Start test instance (initialize and run)
int test_start_instance(struct test_instance* test);
// Stop test instance
void test_stop_instance(struct test_instance* test);
// Inject packet into TUN interface
int test_inject_tun_packet(struct test_instance* test, const uint8_t* packet, size_t len);
// Inject UDP packet
int test_inject_udp_packet(struct test_instance* test, int socket_fd,
const uint8_t* packet, size_t len,
const struct sockaddr* src_addr, socklen_t addr_len);
// Get captured packets
size_t test_get_captured_packets(struct test_instance* test,
const char* direction, // "tun_in", "tun_out", "udp_in", "udp_out"
uint8_t*** packets, size_t** lengths);
// Get captured packet count
size_t test_get_captured_packet_count(struct test_instance* test, const char* direction);
// Clear captured packets
void test_clear_captured_packets(struct test_instance* test);
// Wait for packet with timeout (milliseconds)
int test_wait_for_packet(struct test_instance* test,
const char* direction,
int timeout_ms);
// Two-instance test runner
int test_run_two_instances(const struct test_instance_config* server_config,
const struct test_instance_config* client_config,
void (*test_scenario)(struct test_instance* server,
struct test_instance* client),
int timeout_ms);
// Get test instance statistics
void test_get_instance_stats(struct test_instance* test, size_t* tun_packets_sent,
size_t* tun_packets_received, size_t* udp_packets_sent,
size_t* udp_packets_received);
// Utility functions
const char* test_get_direction_name(const char* direction);
int test_compare_packets(const uint8_t* pkt1, size_t len1, const uint8_t* pkt2, size_t len2);
void test_dump_packet(const char* prefix, const uint8_t* packet, size_t len);
// Default test configurations
extern const struct test_instance_config TEST_SERVER_CONFIG;
extern const struct test_instance_config TEST_CLIENT_CONFIG;
#endif // UTUN_TEST_FRAMEWORK_H

30
!/utun_test_hooks.c

@ -1,30 +0,0 @@
#include "utun_test_hooks.h"
#include "utun_instance.h"
#include "../lib/debug_config.h"
// Add test category if not already defined
#ifndef DEBUG_CATEGORY_TEST
#define DEBUG_CATEGORY_TEST (1 << 30) // High bit for test category
#endif
// Global test hooks - NULL in production
struct utun_test_hooks* g_utun_test_hooks = NULL;
#ifdef TEST_BUILD
void utun_test_hooks_set(struct utun_test_hooks* hooks) {
if (!hooks) {
DEBUG_ERROR(DEBUG_CATEGORY_TEST, "Attempt to set NULL test hooks");
return;
}
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Setting test hooks: sendto=%p, recvfrom=%p, tun_create=%p",
hooks->sendto_override, hooks->recvfrom_override, hooks->tun_create_override);
g_utun_test_hooks = hooks;
}
void utun_test_hooks_clear(void) {
DEBUG_INFO(DEBUG_CATEGORY_TEST, "Clearing test hooks (was: %p)", g_utun_test_hooks);
g_utun_test_hooks = NULL;
}
#endif

126
!/utun_test_hooks.h

@ -1,126 +0,0 @@
#ifndef UTUN_TEST_HOOKS_H
#define UTUN_TEST_HOOKS_H
#include <sys/types.h>
#include <sys/socket.h>
#include <stdint.h>
#include <stddef.h>
#include <unistd.h>
// Forward declarations
struct UTUN_INSTANCE;
struct ETCP_SOCKET;
// UDP packet interception callbacks
typedef ssize_t (*utun_sendto_hook_t)(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen,
void* context);
typedef ssize_t (*utun_recvfrom_hook_t)(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen,
void* context);
// TUN virtual interface callbacks
typedef int (*tun_create_hook_t)(void* priv_data);
typedef ssize_t (*tun_read_hook_t)(int fd, void *buf, size_t count, void* priv_data);
typedef ssize_t (*tun_write_hook_t)(int fd, const void *buf, size_t count, void* priv_data);
// Socket creation callback
typedef int (*socket_create_hook_t)(int domain, int type, int protocol, void* context);
// Test packet capture callback
typedef void (*packet_capture_hook_t)(struct UTUN_INSTANCE* instance,
const uint8_t* packet, size_t len,
const char* direction, // "tun_in", "tun_out", "udp_in", "udp_out"
void* context);
struct utun_test_hooks {
// UDP socket hooks
utun_sendto_hook_t sendto_override;
utun_recvfrom_hook_t recvfrom_override;
// TUN hooks
tun_create_hook_t tun_create_override;
tun_read_hook_t tun_read_override;
tun_write_hook_t tun_write_override;
// Socket creation hook
socket_create_hook_t socket_create_override;
// Packet capture
packet_capture_hook_t packet_captured;
// Context for callbacks
void* test_context;
};
// Global test hooks - NULL in production, set only by test code
extern struct utun_test_hooks* g_utun_test_hooks;
// Test-aware wrapper functions - inline for zero overhead when NULL
static inline ssize_t utun_sendto_hook(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen) {
if (g_utun_test_hooks && g_utun_test_hooks->sendto_override) {
return g_utun_test_hooks->sendto_override(sockfd, buf, len, flags, dest_addr, addrlen,
g_utun_test_hooks->test_context);
}
return sendto(sockfd, buf, len, flags, dest_addr, addrlen);
}
static inline ssize_t utun_recvfrom_hook(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen) {
if (g_utun_test_hooks && g_utun_test_hooks->recvfrom_override) {
return g_utun_test_hooks->recvfrom_override(sockfd, buf, len, flags, src_addr, addrlen,
g_utun_test_hooks->test_context);
}
return recvfrom(sockfd, buf, len, flags, src_addr, addrlen);
}
static inline int tun_create_hook(void* priv_data) {
if (g_utun_test_hooks && g_utun_test_hooks->tun_create_override) {
return g_utun_test_hooks->tun_create_override(priv_data);
}
return -1; // Default: no TUN available in test mode
}
static inline ssize_t tun_read_hook(int fd, void *buf, size_t count, void* priv_data) {
if (g_utun_test_hooks && g_utun_test_hooks->tun_read_override) {
return g_utun_test_hooks->tun_read_override(fd, buf, count, priv_data);
}
return read(fd, buf, count);
}
static inline ssize_t tun_write_hook(int fd, const void *buf, size_t count, void* priv_data) {
if (g_utun_test_hooks && g_utun_test_hooks->tun_write_override) {
return g_utun_test_hooks->tun_write_override(fd, buf, count, priv_data);
}
return write(fd, buf, count);
}
static inline int socket_create_hook(int domain, int type, int protocol) {
if (g_utun_test_hooks && g_utun_test_hooks->socket_create_override) {
return g_utun_test_hooks->socket_create_override(domain, type, protocol,
g_utun_test_hooks->test_context);
}
return socket(domain, type, protocol);
}
static inline void packet_capture_hook(struct UTUN_INSTANCE* instance,
const uint8_t* packet, size_t len,
const char* direction) {
if (g_utun_test_hooks && g_utun_test_hooks->packet_captured) {
g_utun_test_hooks->packet_captured(instance, packet, len, direction,
g_utun_test_hooks->test_context);
}
}
// Functions to set/clear test hooks (only available in test builds)
#ifdef TEST_BUILD
void utun_test_hooks_set(struct utun_test_hooks* hooks);
void utun_test_hooks_clear(void);
#else
// In production builds, these are no-ops for safety
static inline void utun_test_hooks_set(struct utun_test_hooks* hooks) { (void)hooks; }
static inline void utun_test_hooks_clear(void) {}
#endif
#endif // UTUN_TEST_HOOKS_H

117
!/utun_test_socket_api.c

@ -1,117 +0,0 @@
#include "utun_test_socket_api.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
void utun_test_packet_queue_init(struct utun_test_packet_queue* queue) {
if (!queue) return;
queue->head = NULL;
queue->tail = NULL;
queue->count = 0;
}
void utun_test_packet_queue_cleanup(struct utun_test_packet_queue* queue) {
if (!queue) return;
struct utun_test_packet* pkt = queue->head;
while (pkt) {
struct utun_test_packet* next = pkt->next;
free(pkt->data);
free(pkt);
pkt = next;
}
queue->head = NULL;
queue->tail = NULL;
queue->count = 0;
}
void utun_test_packet_queue_push(struct utun_test_packet_queue* queue,
const uint8_t* data, size_t len,
const struct sockaddr* addr, socklen_t addrlen) {
if (!queue || !data) return;
struct utun_test_packet* pkt = calloc(1, sizeof(*pkt));
if (!pkt) return;
pkt->data = malloc(len);
if (!pkt->data) {
free(pkt);
return;
}
memcpy(pkt->data, data, len);
pkt->len = len;
if (addr && addrlen > 0 && addrlen <= sizeof(pkt->addr)) {
memcpy(&pkt->addr, addr, addrlen);
pkt->addrlen = addrlen;
}
pkt->next = NULL;
if (queue->tail) {
queue->tail->next = pkt;
} else {
queue->head = pkt;
}
queue->tail = pkt;
queue->count++;
}
struct utun_test_packet* utun_test_packet_queue_pop(struct utun_test_packet_queue* queue) {
if (!queue || !queue->head) return NULL;
struct utun_test_packet* pkt = queue->head;
queue->head = pkt->next;
if (!queue->head) {
queue->tail = NULL;
}
queue->count--;
pkt->next = NULL;
return pkt;
}
size_t utun_test_packet_queue_count(struct utun_test_packet_queue* queue) {
return queue ? queue->count : 0;
}
ssize_t utun_test_default_on_send(const uint8_t* data, size_t len,
const struct sockaddr* dest_addr, socklen_t dest_addrlen,
void* user_data) {
struct utun_test_socket_api* api = (struct utun_test_socket_api*)user_data;
if (!api || !data || len == 0) return -1;
utun_test_packet_queue_push(&api->send_queue, data, len, dest_addr, dest_addrlen);
return len;
}
ssize_t utun_test_default_on_recv(uint8_t* buffer, size_t buffer_size,
struct sockaddr* src_addr, socklen_t* src_addrlen,
void* user_data) {
struct utun_test_socket_api* api = (struct utun_test_socket_api*)user_data;
if (!api || !buffer || buffer_size == 0) return -1;
struct utun_test_packet* pkt = utun_test_packet_queue_pop(&api->recv_queue);
if (!pkt) {
errno = EAGAIN;
return -1;
}
size_t to_copy = (pkt->len < buffer_size) ? pkt->len : buffer_size;
memcpy(buffer, pkt->data, to_copy);
if (src_addr && src_addrlen && *src_addrlen > 0) {
socklen_t copy_len = (pkt->addrlen < *src_addrlen) ? pkt->addrlen : *src_addrlen;
memcpy(src_addr, &pkt->addr, copy_len);
*src_addrlen = copy_len;
}
free(pkt->data);
free(pkt);
return to_copy;
}

52
!/utun_test_socket_api.h

@ -1,52 +0,0 @@
#ifndef UTUN_TEST_SOCKET_API_H
#define UTUN_TEST_SOCKET_API_H
#include <sys/socket.h>
#include <stdint.h>
struct utun_test_packet {
uint8_t* data;
size_t len;
struct sockaddr_storage addr;
socklen_t addrlen;
struct utun_test_packet* next;
};
struct utun_test_packet_queue {
struct utun_test_packet* head;
struct utun_test_packet* tail;
size_t count;
};
struct utun_test_socket_api {
struct utun_test_packet_queue send_queue;
struct utun_test_packet_queue recv_queue;
ssize_t (*on_send)(const uint8_t* data, size_t len,
const struct sockaddr* dest_addr, socklen_t dest_addrlen,
void* user_data);
ssize_t (*on_recv)(uint8_t* buffer, size_t buffer_size,
struct sockaddr* src_addr, socklen_t* src_addrlen,
void* user_data);
void* user_data;
};
void utun_test_packet_queue_init(struct utun_test_packet_queue* queue);
void utun_test_packet_queue_cleanup(struct utun_test_packet_queue* queue);
void utun_test_packet_queue_push(struct utun_test_packet_queue* queue,
const uint8_t* data, size_t len,
const struct sockaddr* addr, socklen_t addrlen);
struct utun_test_packet* utun_test_packet_queue_pop(struct utun_test_packet_queue* queue);
size_t utun_test_packet_queue_count(struct utun_test_packet_queue* queue);
ssize_t utun_test_default_on_send(const uint8_t* data, size_t len,
const struct sockaddr* dest_addr, socklen_t dest_addrlen,
void* user_data);
ssize_t utun_test_default_on_recv(uint8_t* buffer, size_t buffer_size,
struct sockaddr* src_addr, socklen_t* src_addrlen,
void* user_data);
#endif

1
src/Makefile.am

@ -6,6 +6,7 @@ utun_CORE_SOURCES = \
utun_instance.c \
config_parser.c \
config_updater.c \
route_lib.c \
routing.c \
tun_if.c \
etcp.c \

32
src/etcp.c

@ -1,8 +1,9 @@
// etcp.c - ETCP Protocol Implementation (refactored and expanded based on etcp_protocol.txt)
#include "etcp.h"
#include "etcp_loadbalancer.h"
#include "../lib/u_async.h"
#include "etcp.h"
#include "etcp_loadbalancer.h"
#include "routing.h"
#include "../lib/u_async.h"
#include "../lib/ll_queue.h"
#include "../lib/debug_config.h"
#include "crc32.h" // For potential hashing, though not used yet.
@ -93,13 +94,16 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance) {
etcp_connection_close(etcp);
return NULL;
}
// etcp->normalizer = pn_pair_init(instance->ua, etcp->mtu);
// if (!etcp->normalizer) {
// etcp_connection_close(etcp);
// return NULL;
// }
etcp->normalizer = pn_init(etcp);
if (!etcp->normalizer) {
etcp_connection_close(etcp);
return NULL;
}
// Register with routing module (must be done after normalizer is assigned)
routing_add_conn(etcp);
etcp->mtu = 1500; // Default MTU
// etcp->window_size = MAX_INFLIGHT_BYTES; // Not used
etcp->next_tx_id = 1;
@ -129,7 +133,13 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance) {
void etcp_connection_close(struct ETCP_CONN* etcp) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "");
if (!etcp) return;
// Deinitialize packet normalizer (this will call routing_del_conn)
if (etcp->normalizer) {
pn_deinit((struct PKTNORM*)etcp->normalizer);
etcp->normalizer = NULL;
}
// Drain and free input_queue (contains ETCP_FRAGMENT with pkt_data from data_pool)
if (etcp->input_queue) {
struct ETCP_FRAGMENT* pkt;

2
src/etcp_connections.c

@ -5,7 +5,7 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "routing.h"
#include "route_lib.h"
#include "utun_instance.h"
#include "config_parser.h"
#include "crc32.h"

13
src/pkt_normalizer.c

@ -1,6 +1,8 @@
// pkt_normalizer.c - Implementation of packet normalizer for ETCP
#include "pkt_normalizer.h"
#include "etcp.h" // For ETCP_CONN and related structures
#include "routing.h" // For routing_add_conn/routing_del_conn
#include "utun_instance.h" // For UTUN_INSTANCE
#include "ll_queue.h" // For queue operations
#include "u_async.h" // For UASYNC
#include <stdlib.h>
@ -33,14 +35,14 @@ struct PKTNORM* pn_init(struct ETCP_CONN* etcp) {
pn->input = queue_new(pn->ua, 0); // No hash needed
if (!pn->input) {
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "pn_init: queue_new(input) failed");
pn_pair_deinit(pn);
pn_deinit(pn);
return NULL;
}
pn->output = queue_new(pn->ua, 0); // No hash needed
if (!pn->output) {
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "pn_init: queue_new(output) failed");
pn_pair_deinit(pn);
pn_deinit(pn);
return NULL;
}
@ -55,9 +57,14 @@ struct PKTNORM* pn_init(struct ETCP_CONN* etcp) {
}
// Deinitialization
void pn_pair_deinit(struct PKTNORM* pn) {
void pn_deinit(struct PKTNORM* pn) {
if (!pn) return;
// Unregister from routing module
if (pn->etcp) {
routing_del_conn(pn->etcp);
}
// Drain and free queues
if (pn->input) {
struct ll_entry* entry;

2
src/pkt_normalizer.h

@ -40,7 +40,7 @@ struct PKTNORM {
struct PKTNORM* pn_init(struct ETCP_CONN* etcp);// все что нужно (в т.ч. mtu и ua) берет из etcp
// Деинициализация пары
void pn_pair_deinit(struct PKTNORM* pn);
void pn_deinit(struct PKTNORM* pn);
// Сброс состояния (для unpacker)
void pn_unpacker_reset_state(struct PKTNORM* pn);

73
!/routing.c → src/route_lib.c

@ -1,9 +1,9 @@
/**
* @file enhanced_routing.c
* @file route_lib.c
* @brief Enhanced routing system with bandwidth support and route types
*/
#include "routing.h"
#include "route_lib.h"
#include "etcp_connections.h"
#include "../lib/debug_config.h"
#include <stdio.h>
@ -62,17 +62,17 @@ static int compare_routes(const void *a, const void *b) {
return route_a->metrics.hop_count - route_b->metrics.hop_count;
}
struct routing_table *routing_table_create(void) {
struct routing_table *table = calloc(1, sizeof(struct routing_table));
struct route_table *route_table_create(void) {
struct route_table *table = calloc(1, sizeof(struct route_table));
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_create: failed to allocate memory for routing table");
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate memory for routing table");
return NULL;
}
table->capacity = INITIAL_ROUTE_CAPACITY;
table->entries = calloc(table->capacity, sizeof(struct route_entry));
if (!table->entries) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_create: failed to allocate memory for %d route entries", table->capacity);
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate memory for %d route entries", table->capacity);
free(table);
return NULL;
}
@ -82,7 +82,7 @@ struct routing_table *routing_table_create(void) {
table->local_subnets = calloc(MAX_SUBNET_VALIDATION_RANGES, sizeof(uint32_t) * 2);
if (!table->dynamic_subnets || !table->local_subnets) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_create: failed to allocate memory for subnet validation arrays");
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_create: failed to allocate memory for subnet validation arrays");
free(table->entries);
free(table->dynamic_subnets);
free(table->local_subnets);
@ -93,7 +93,7 @@ struct routing_table *routing_table_create(void) {
return table;
}
void routing_table_destroy(struct routing_table *table) {
void route_table_destroy(struct route_table *table) {
if (!table) return;
// Free route entries
@ -106,11 +106,11 @@ void routing_table_destroy(struct routing_table *table) {
free(table);
}
bool routing_table_insert(struct routing_table *table, const struct route_entry *entry) {
bool route_table_insert(struct route_table *table, const struct route_entry *entry) {
if (!table || !entry) return false;
// Validate the route
if (!routing_validate_route(table, entry->network, entry->prefix_length, entry->type)) {
if (!route_validate_route(table, entry->network, entry->prefix_length, entry->type)) {
char ip_str[16];
ip_to_string(entry->network, ip_str);
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Ignoring invalid route: %s/%d (type: %s)",
@ -125,8 +125,8 @@ bool routing_table_insert(struct routing_table *table, const struct route_entry
size_t new_capacity = table->capacity * ROUTE_EXPANSION_FACTOR;
struct route_entry *new_entries = realloc(table->entries, new_capacity * sizeof(struct route_entry));
if (!new_entries) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_insert: failed to expand routing table to %zu entries", new_capacity);
return false;
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "route_table_insert: failed to expand routing table to %zu entries", new_capacity);
return false;
}
table->entries = new_entries;
@ -158,9 +158,9 @@ bool routing_table_insert(struct routing_table *table, const struct route_entry
return false;
}
bool routing_table_lookup(struct routing_table *table, uint32_t dest_ip, struct route_entry *best_route) {
bool route_table_lookup(struct route_table *table, uint32_t dest_ip, struct route_entry *best_route) {
if (!table || !best_route) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "routing_table_lookup: invalid parameters (table=%p, best_route=%p)", table, best_route);
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_table_lookup: invalid parameters (table=%p, best_route=%p)", table, best_route);
return false;
}
@ -179,9 +179,8 @@ bool routing_table_lookup(struct routing_table *table, uint32_t dest_ip, struct
char ip_str[16];
ip_to_string(dest_ip, ip_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Found route for %s: %s/%d -> %s",
ip_str, ip_to_string(entry->network, ip_str), entry->prefix_length,
ip_to_string(entry->next_hop_ip, ip_str));
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Found route for %s: %s/%d",
ip_str, ip_to_string(entry->network, ip_str), entry->prefix_length);
table->stats.hit_count++;
table->stats.routes_lookup_hits++;
@ -193,12 +192,12 @@ bool routing_table_lookup(struct routing_table *table, uint32_t dest_ip, struct
// No route found
char ip_str[16];
ip_to_string(dest_ip, ip_str);
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "routing_table_lookup: no route found for destination IP %s", ip_str);
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_table_lookup: no route found for destination IP %s", ip_str);
return false;
}
bool routing_validate_route(struct routing_table *table, uint32_t network, uint8_t prefix_length, route_type_t route_type) {
bool route_validate_route(struct route_table *table, uint32_t network, uint8_t prefix_length, route_type_t route_type) {
if (!table) return false;
uint32_t *validation_ranges = NULL;
@ -240,7 +239,7 @@ bool routing_validate_route(struct routing_table *table, uint32_t network, uint8
return false;
}
bool enhanced_routing_add_subnet_range(struct routing_table *table, uint32_t network, uint8_t prefix_length, uint32_t **ranges, size_t *count) {
static bool enhanced_route_add_subnet_range(struct route_table *table, uint32_t network, uint8_t prefix_length, uint32_t **ranges, size_t *count) {
if (!table || !ranges || !count) return false;
if (*count >= MAX_SUBNET_VALIDATION_RANGES - 2) return false;
@ -266,15 +265,15 @@ bool enhanced_routing_add_subnet_range(struct routing_table *table, uint32_t net
bool routing_add_dynamic_subnet(struct routing_table *table, uint32_t network, uint8_t prefix_length) {
return enhanced_routing_add_subnet_range(table, network, prefix_length, &table->dynamic_subnets, &table->dynamic_subnet_count);
bool route_add_dynamic_subnet(struct route_table *table, uint32_t network, uint8_t prefix_length) {
return enhanced_route_add_subnet_range(table, network, prefix_length, &table->dynamic_subnets, &table->dynamic_subnet_count);
}
bool routing_add_local_subnet(struct routing_table *table, uint32_t network, uint8_t prefix_length) {
return enhanced_routing_add_subnet_range(table, network, prefix_length, &table->local_subnets, &table->local_subnet_count);
bool route_add_local_subnet(struct route_table *table, uint32_t network, uint8_t prefix_length) {
return enhanced_route_add_subnet_range(table, network, prefix_length, &table->local_subnets, &table->local_subnet_count);
}
bool routing_get_all_routes(const struct routing_table *table, uint32_t network, uint8_t prefix_length,
bool route_get_all_routes(const struct route_table *table, uint32_t network, uint8_t prefix_length,
struct route_entry **routes, size_t *count) {
if (!table || !routes || !count) return false;
@ -310,44 +309,40 @@ bool routing_get_all_routes(const struct routing_table *table, uint32_t network,
return true;
}
void routing_table_print(const struct routing_table *table) {
void route_table_print(const struct route_table *table) {
if (!table) return;
printf("\n=== Routing Table ===\n");
printf("Total routes: %zu\n", table->count);
printf("Dynamic subnets: %zu, Local subnets: %zu\n",
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=== Routing Table ===");
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Total routes: %zu", table->count);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Dynamic subnets: %zu, Local subnets: %zu",
table->dynamic_subnet_count / 2, table->local_subnet_count / 2);
if (table->count > 0) {
printf("\nRoutes:\n");
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routes:");
for (size_t i = 0; i < table->count; i++) {
const struct route_entry *entry = &table->entries[i];
char network_str[16], next_hop_str[16];
ip_to_string(entry->network, network_str);
ip_to_string(entry->next_hop_ip, next_hop_str);
printf(" %zu: %s/%d -> %s [%s]\n",
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d -> %s [%s]",
i + 1, network_str, entry->prefix_length, next_hop_str,
route_type_to_string(entry->type));
printf(" Bandwidth: %uK, Loss: %.2f%%, Latency: %ums, Hops: %d\n",
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Bandwidth: %uK, Loss: %.2f%%, Latency: %ums, Hops: %d",
entry->metrics.bandwidth_kbps,
entry->metrics.packet_loss_rate / 100.0,
entry->metrics.latency_ms,
entry->metrics.hop_count);
printf(" Flags: %s%s%s%s\n",
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Flags: %s%s%s%s",
(entry->flags & ROUTE_FLAG_ACTIVE) ? "ACTIVE " : "",
(entry->flags & ROUTE_FLAG_VALIDATED) ? "VALIDATED " : "",
(entry->flags & ROUTE_FLAG_ADVERTISED) ? "ADVERTISED " : "",
(entry->flags & ROUTE_FLAG_LEARNED) ? "LEARNED" : "");
printf("\n");
}
} else {
printf("\nNo routes configured.\n");
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "No routes configured.");
}
}
@ -369,4 +364,4 @@ char *ip_to_string(uint32_t ip, char *buffer) {
strncpy(buffer, inet_ntoa(addr), 15);
buffer[15] = '\0';
return buffer;
}
}

200
src/route_lib.h

@ -0,0 +1,200 @@
#ifndef ROUTE_LIB_H
#define ROUTE_LIB_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// Forward declarations
struct ETCP_CONNECTIONS;
/**
* @brief Типы маршрутов
*/
typedef enum {
ROUTE_TYPE_STATIC = 0, /**< Статический маршрут */
ROUTE_TYPE_DYNAMIC = 1, /**< Динамический маршрут */
ROUTE_TYPE_LOCAL = 2, /**< Локальный маршрут */
ROUTE_TYPE_LEARNED = 3 /**< Изученный маршрут */
} route_type_t;
/**
* @brief Флаги маршрута
*/
typedef enum {
ROUTE_FLAG_ACTIVE = (1 << 0), /**< Маршрут активен */
ROUTE_FLAG_VALIDATED = (1 << 1), /**< Маршрут валидирован */
ROUTE_FLAG_ADVERTISED = (1 << 2), /**< Маршрут анонсирован */
ROUTE_FLAG_LEARNED = (1 << 3) /**< Маршрут изучен */
} route_flags_t;
/**
* @brief Расширенные метрики маршрута
*
* Структура содержит дополнительные метрики для оценки качества маршрута.
*/
struct route_metrics {
uint32_t bandwidth_kbps; /**< Пропускная способность в Кбит/с */
uint16_t packet_loss_rate; /**< Уровень потери пакетов (в промилле) */
uint16_t latency_ms; /**< Задержка в миллисекундах */
uint8_t hop_count; /**< Количество прыжков */
uint64_t last_updated; /**< Время последнего обновления (в микросекундах) */
};
/**
* @brief Расширенная запись маршрута
*
* Структура представляет собой отдельную запись в таблице маршрутизации с детальной информацией о маршруте.
*/
struct route_entry {
uint32_t network; /**< Сетевой адрес */
uint8_t prefix_length; /**< Длина префикса подсети */
struct ETCP_SOCKET* next_hop; /**< Указатель на сокет следующего хопа */
route_type_t type; /**< Тип маршрута */
uint8_t flags; /**< Флаги маршрута */
struct route_metrics metrics; /**< Метрики маршрута */
uint64_t created_time; /**< Время создания (в микросекундах) */
uint64_t last_update; /**< Время последнего обновления (в микросекундах) */
uint64_t last_used; /**< Время последнего использования (в микросекундах) */
};
/**
* @brief Таблица маршрутизации
*
* Структура представляет собой таблицу маршрутизации, содержащую записи маршрутов, подсети и статистику.
*/
struct route_table {
struct route_entry *entries; /**< Массив записей маршрутов */
size_t count; /**< Текущее количество записей */
size_t capacity; /**< Максимальная емкость таблицы */
uint32_t *dynamic_subnets; /**< Динамические подсети (массив пар: сеть, префикс) */
size_t dynamic_subnet_count; /**< Количество динамических подсетей */
uint32_t *local_subnets; /**< Локальные подсети (массив пар: сеть, префикс) */
size_t local_subnet_count; /**< Количество локальных подсетей */
struct {
uint64_t total_routes; /**< Общее количество маршрутов */
uint64_t static_routes; /**< Количество статических маршрутов */
uint64_t dynamic_routes; /**< Количество динамических маршрутов */
uint64_t local_routes; /**< Количество локальных маршрутов */
uint64_t learned_routes; /**< Количество изученных маршрутов */
uint64_t routes_added; /**< Количество добавленных маршрутов */
uint64_t routes_deleted; /**< Количество удаленных маршрутов */
uint64_t lookup_count; /**< Количество запросов поиска */
uint64_t hit_count; /**< Количество успешных поисков */
uint64_t routes_lookup_hits; /**< Количество попаданий в поиск маршрутов */
uint64_t routes_lookup_misses; /**< Количество промахов в поиск маршрутов */
uint64_t validation_failures; /**< Количество неудачных валидаций */
} stats; /**< Статистика таблицы маршрутизации */
};
/**
* @brief Создает новую таблицу маршрутизации
*
* @return Указатель на созданную таблицу или NULL в случае ошибки
*/
struct route_table *route_table_create(void);
/**
* @brief Уничтожает таблицу маршрутизации и освобождает ресурсы
*
* @param table Указатель на таблицу маршрутизации
*/
void route_table_destroy(struct route_table *table);
/**
* @brief Вставляет новую запись в таблицу маршрутизации
*
* @param table Указатель на таблицу маршрутизации
* @param entry Указатель на вставляемую запись маршрута
* @return true если вставка успешна, false иначе
*/
bool route_table_insert(struct route_table *table, const struct route_entry *entry);
/**
* @brief Удаляет запись из таблицы маршрутизации
*
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса
* @param source_node_id Идентификатор источника узла
* @return true если удаление успешно, false иначе
*/
bool route_table_delete(struct route_table *table, uint32_t network, uint8_t prefix_length, uint32_t source_node_id);
/**
* @brief Выполняет поиск лучшего маршрута для заданного IP-адреса
*
* @param table Указатель на таблицу маршрутизации
* @param dest_ip Целевой IP-адрес
* @param best_route Указатель на структуру для хранения лучшего маршрута
* @return true если маршрут найден, false иначе
*/
bool route_table_lookup(struct route_table *table, uint32_t dest_ip, struct route_entry *best_route);
/**
* @brief Валидирует маршрут на основе типа и подсетей
*
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса
* @param route_type Тип маршрута
* @return true если маршрут валиден, false иначе
*/
bool route_validate_route(struct route_table *table, uint32_t network, uint8_t prefix_length, route_type_t route_type);
/**
* @brief Добавляет динамическую подсеть в таблицу
*
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса
* @return true если добавление успешно, false иначе
*/
bool route_add_dynamic_subnet(struct route_table *table, uint32_t network, uint8_t prefix_length);
/**
* @brief Добавляет локальную подсеть в таблицу
*
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса
* @return true если добавление успешно, false иначе
*/
bool route_add_local_subnet(struct route_table *table, uint32_t network, uint8_t prefix_length);
/**
* @brief Получает все маршруты для заданной сети и префикса
*
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса
* @param routes Указатель на массив маршрутов (будет выделен)
* @param count Указатель на количество найденных маршрутов
* @return true если операция успешна, false иначе
*/
bool route_get_all_routes(const struct route_table *table, uint32_t network, uint8_t prefix_length, struct route_entry **routes, size_t *count);
/**
* @brief Печатает содержимое таблицы маршрутизации
*
* @param table Указатель на таблицу маршрутизации
*/
void route_table_print(const struct route_table *table);
/**
* @brief Преобразует тип маршрута в строковое представление
*
* @param type Тип маршрута
* @return Строковое представление типа
*/
const char* route_type_to_string(route_type_t type);
/**
* @brief Преобразует IP-адрес в строковое представление
*
* @param ip IP-адрес в сетевом формате
* @param buffer Буфер для хранения строки (минимум 16 байт)
* @return Указатель на буфер с строкой
*/
char* ip_to_string(uint32_t ip, char *buffer);
#endif // ROUTE_LIB_H

0
!/routing.txt → src/route_lib.txt

504
src/routing.c

@ -1,369 +1,245 @@
/**
* @file enhanced_routing.c
* @brief Enhanced routing system with bandwidth support and route types
*/
// routing.c - Centralized routing module for utun
#include "routing.h"
#include "etcp_connections.h"
#include "route_lib.h"
#include "tun_if.h"
#include "etcp.h"
#include "pkt_normalizer.h"
#include "utun_instance.h"
#include "../lib/ll_queue.h"
#include "../lib/debug_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <time.h>
#define INITIAL_ROUTE_CAPACITY 100
#define ROUTE_EXPANSION_FACTOR 2
#define MAX_SUBNET_VALIDATION_RANGES 50
// Parse subnet string
int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_length) {
if (!subnet_str || !network || !prefix_length) return -1;
char ip[64];
int prefix;
if (sscanf(subnet_str, "%[^/]/%d", ip, &prefix) != 2) return -1;
if (prefix < 0 || prefix > 32) return -1;
struct in_addr addr;
if (inet_pton(AF_INET, ip, &addr) != 1) return -1;
*network = ntohl(addr.s_addr);
*prefix_length = (uint8_t)prefix;
return 0;
}
#define IPv4_VERSION 4
#define IPv6_VERSION 6
#define IP_HDR_VERSION_OFFSET 0
#define IP_HDR_DST_ADDR_OFFSET 16
#define IP_HDR_MIN_SIZE 20
static int compare_routes(const void *a, const void *b) {
const struct route_entry *route_a = (const struct route_entry *)a;
const struct route_entry *route_b = (const struct route_entry *)b;
// First compare by prefix length (longest prefix first)
if (route_b->prefix_length != route_a->prefix_length) {
return route_b->prefix_length - route_a->prefix_length;
}
// Then by network
if (route_a->network != route_b->network) {
return route_a->network < route_b->network ? -1 : 1;
// Extract destination IP from IPv4 packet
// Returns 0 if not IPv4 or packet too small
static uint32_t extract_dst_ip(uint8_t* data, size_t len) {
if (!data || len < IP_HDR_MIN_SIZE) {
return 0;
}
// Finally by metrics (best route first)
if (route_a->metrics.bandwidth_kbps != route_b->metrics.bandwidth_kbps) {
return route_b->metrics.bandwidth_kbps - route_a->metrics.bandwidth_kbps;
}
if (route_a->metrics.latency_ms != route_b->metrics.latency_ms) {
return route_a->metrics.latency_ms - route_b->metrics.latency_ms;
}
// Check IP version (first nibble)
uint8_t version = (data[IP_HDR_VERSION_OFFSET] >> 4) & 0x0F;
if (route_a->metrics.packet_loss_rate != route_b->metrics.packet_loss_rate) {
return route_a->metrics.packet_loss_rate - route_b->metrics.packet_loss_rate;
if (version == IPv6_VERSION) {
// IPv6 - drop for now
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "IPv6 packet detected, dropping");
return 0;
}
return route_a->metrics.hop_count - route_b->metrics.hop_count;
}
struct routing_table *routing_table_create(void) {
struct routing_table *table = calloc(1, sizeof(struct routing_table));
if (!table) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_create: failed to allocate memory for routing table");
return NULL;
if (version != IPv4_VERSION) {
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "Unknown IP version: %d", version);
return 0;
}
table->capacity = INITIAL_ROUTE_CAPACITY;
table->entries = calloc(table->capacity, sizeof(struct route_entry));
if (!table->entries) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_create: failed to allocate memory for %d route entries", table->capacity);
free(table);
return NULL;
}
// Initialize subnet validation arrays
table->dynamic_subnets = calloc(MAX_SUBNET_VALIDATION_RANGES, sizeof(uint32_t) * 2);
table->local_subnets = calloc(MAX_SUBNET_VALIDATION_RANGES, sizeof(uint32_t) * 2);
if (!table->dynamic_subnets || !table->local_subnets) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_create: failed to allocate memory for subnet validation arrays");
free(table->entries);
free(table->dynamic_subnets);
free(table->local_subnets);
free(table);
return NULL;
}
return table;
}
void routing_table_destroy(struct routing_table *table) {
if (!table) return;
// Free route entries
free(table->entries);
// Free validation ranges
free(table->dynamic_subnets);
free(table->local_subnets);
// Extract destination IP (offset 16, 4 bytes, network byte order)
uint32_t dst_ip;
memcpy(&dst_ip, data + IP_HDR_DST_ADDR_OFFSET, 4);
free(table);
return dst_ip; // Keep in network byte order
}
bool routing_table_insert(struct routing_table *table, const struct route_entry *entry) {
if (!table || !entry) return false;
// Validate the route
if (!routing_validate_route(table, entry->network, entry->prefix_length, entry->type)) {
char ip_str[16];
ip_to_string(entry->network, ip_str);
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Ignoring invalid route: %s/%d (type: %s)",
ip_str, entry->prefix_length, route_type_to_string(entry->type));
return false;
}
// Check if we need to expand the table
if (table->count >= table->capacity) {
size_t new_capacity = table->capacity * ROUTE_EXPANSION_FACTOR;
struct route_entry *new_entries = realloc(table->entries, new_capacity * sizeof(struct route_entry));
if (!new_entries) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "routing_table_insert: failed to expand routing table to %zu entries", new_capacity);
return false;
// Callback for packets from ETCP normalizer output queue
static void routing_pkt_from_etcp_cb(struct ll_queue* q, void* arg) {
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
if (!etcp || !etcp->instance) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_pkt_from_etcp_cb: invalid etcp or instance");
return;
}
struct UTUN_INSTANCE* instance = etcp->instance;
if (!instance->tun) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_pkt_from_etcp_cb: no TUN interface");
return;
}
struct ll_entry* entry = queue_data_get(q);
while (entry) {
if (entry->dgram && entry->len > 0) {
// Check MTU
if (entry->len > TUN_MAX_PACKET_SIZE) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Packet too large: %zu bytes (max %d)",
entry->len, TUN_MAX_PACKET_SIZE);
} else {
// Extract destination IP for future routing (currently ignored)
uint32_t dst_ip = extract_dst_ip(entry->dgram, entry->len);
if (dst_ip != 0) {
char ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &dst_ip, ip_str, sizeof(ip_str));
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "Packet from ETCP to %s", ip_str);
}
// Allocate ETCP_FRAGMENT for TUN input_queue
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(instance->tun->pool);
if (!pkt) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to allocate ETCP_FRAGMENT for TUN");
} else {
// Allocate packet data (TUN will free it)
uint8_t* packet_data = malloc(entry->len);
if (!packet_data) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to allocate packet data");
memory_pool_free(instance->tun->pool, pkt);
} else {
memcpy(packet_data, entry->dgram, entry->len);
pkt->seq = 0;
pkt->timestamp = 0;
pkt->ll.dgram = packet_data;
pkt->ll.len = entry->len;
// Put to TUN input_queue
if (queue_data_put(instance->tun->input_queue, (struct ll_entry*)pkt, 0) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to put packet to TUN input_queue");
free(packet_data);
memory_pool_free(instance->tun->pool, pkt);
} else {
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "Forwarded %zu bytes from ETCP to TUN", entry->len);
}
}
}
}
// Free original entry data
free(entry->dgram);
}
table->entries = new_entries;
table->capacity = new_capacity;
}
// Set timestamps and ensure route is active
struct route_entry new_entry = *entry;
new_entry.created_time = new_entry.last_update = (uint64_t)time(NULL) * 1000000; // Convert to microseconds
new_entry.last_used = 0;
// Ensure route is active by default
if (!(new_entry.flags & ROUTE_FLAG_ACTIVE)) {
new_entry.flags |= ROUTE_FLAG_ACTIVE;
// Free entry structure
queue_entry_free(entry);
// Get next packet
entry = queue_data_get(q);
}
queue_resume_callback(q);
}
// Insert and maintain sorted order
table->stats.routes_added++;
size_t insert_pos = table->count;
for (size_t i = 0; i < table->count; i++) {
if (compare_routes(&new_entry, &table->entries[i]) < 0) {
insert_pos = i;
break;
// Callback for packets from TUN output queue
static void routing_pkt_from_tun_cb(struct ll_queue* q, void* arg) {
(void)q;
struct UTUN_INSTANCE* instance = (struct UTUN_INSTANCE*)arg;
if (!instance) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_pkt_from_tun_cb: invalid instance");
return;
}
// For now, just drain the queue (routing to ETCP will be implemented later)
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_data_get(instance->tun->output_queue);
while (pkt) {
if (pkt->ll.dgram && pkt->ll.len > 0) {
// Extract destination IP for future routing
uint32_t dst_ip = extract_dst_ip(pkt->ll.dgram, pkt->ll.len);
if (dst_ip != 0) {
char ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &dst_ip, ip_str, sizeof(ip_str));
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "Packet from TUN to %s (dropped, routing not implemented yet)", ip_str);
}
// Free packet data
free(pkt->ll.dgram);
}
// Free fragment structure
memory_pool_free(instance->tun->pool, pkt);
// Get next packet
pkt = (struct ETCP_FRAGMENT*)queue_data_get(instance->tun->output_queue);
}
return false;
}
bool routing_table_lookup(struct routing_table *table, uint32_t dest_ip, struct route_entry *best_route) {
if (!table || !best_route) {
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "routing_table_lookup: invalid parameters (table=%p, best_route=%p)", table, best_route);
return false;
// Initialize routing module for instance
int routing_create(struct UTUN_INSTANCE* instance) {
if (!instance) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_create: instance is NULL");
return -1;
}
table->stats.lookup_count++;
// Find longest prefix match
for (size_t i = 0; i < table->count; i++) {
const struct route_entry *entry = &table->entries[i];
// Check if route is valid and matches destination
if (entry->flags & ROUTE_FLAG_ACTIVE) {
uint32_t mask = (entry->prefix_length == 0) ? 0 : (0xFFFFFFFFU << (32 - entry->prefix_length));
if ((dest_ip & mask) == (entry->network & mask)) {
*best_route = *entry;
best_route->last_update = (uint64_t)time(NULL) * 1000000;
char ip_str[16];
ip_to_string(dest_ip, ip_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Found route for %s: %s/%d -> %s",
ip_str, ip_to_string(entry->network, ip_str), entry->prefix_length,
ip_to_string(entry->next_hop_ip, ip_str));
table->stats.hit_count++;
table->stats.routes_lookup_hits++;
return true;
}
}
// Create route table
instance->rt = route_table_create();
if (!instance->rt) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to create route table");
return -1;
}
// No route found
char ip_str[16];
ip_to_string(dest_ip, ip_str);
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "routing_table_lookup: no route found for destination IP %s", ip_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing module initialized for instance");
return false;
return 0;
}
bool routing_validate_route(struct routing_table *table, uint32_t network, uint8_t prefix_length, route_type_t route_type) {
if (!table) return false;
uint32_t *validation_ranges = NULL;
size_t range_count = 0;
// Destroy routing module for instance
void routing_destroy(struct UTUN_INSTANCE* instance) {
if (!instance) return;
switch (route_type) {
case ROUTE_TYPE_STATIC:
case ROUTE_TYPE_LEARNED:
return true; // No validation for static or learned routes
case ROUTE_TYPE_DYNAMIC:
validation_ranges = table->dynamic_subnets;
range_count = table->dynamic_subnet_count;
break;
case ROUTE_TYPE_LOCAL:
validation_ranges = table->local_subnets;
range_count = table->local_subnet_count;
break;
default:
table->stats.routes_lookup_misses++;
return false;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Destroying routing module");
if (range_count == 0) return true; // No validation ranges configured
// Check if network falls within any validation range
for (size_t i = 0; i < range_count; i += 2) {
uint32_t range_network = validation_ranges[i];
uint8_t range_prefix = (uint8_t)validation_ranges[i + 1];
uint32_t mask = (range_prefix == 0) ? 0 : (0xFFFFFFFFU << (32 - range_prefix));
if ((network & mask) == (range_network & mask)) {
// Check if this route is more specific than or equal to the validation range
if (prefix_length >= range_prefix) {
return true;
}
}
// Clean up route table if exists
if (instance->rt) {
route_table_destroy(instance->rt);
instance->rt = NULL;
}
return false;
}
bool enhanced_routing_add_subnet_range(struct routing_table *table, uint32_t network, uint8_t prefix_length, uint32_t **ranges, size_t *count) {
if (!table || !ranges || !count) return false;
// Register ETCP connection with routing
void routing_add_conn(struct ETCP_CONN* etcp) {
if (!etcp) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_add_conn: etcp is NULL");
return;
}
if (*count >= MAX_SUBNET_VALIDATION_RANGES - 2) return false;
if (!etcp->instance) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_add_conn: etcp has no instance");
return;
}
// Expand array if needed
if (*ranges == NULL) {
*ranges = calloc(MAX_SUBNET_VALIDATION_RANGES, sizeof(uint32_t));
if (!*ranges) return false;
*count = 0;
if (!etcp->normalizer) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_add_conn: ETCP has no normalizer");
return;
}
// Add network and prefix length as a pair
(*ranges)[*count] = network;
(*ranges)[*count + 1] = prefix_length;
*count += 2;
struct PKTNORM* pn = (struct PKTNORM*)etcp->normalizer;
char ip_str[16];
ip_to_string(network, ip_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Added subnet validation range: %s/%d", ip_str, prefix_length);
// Set callback on normalizer output queue - pass etcp as arg
queue_set_callback(pn->output, routing_pkt_from_etcp_cb, etcp);
return true;
}
bool routing_add_dynamic_subnet(struct routing_table *table, uint32_t network, uint8_t prefix_length) {
return enhanced_routing_add_subnet_range(table, network, prefix_length, &table->dynamic_subnets, &table->dynamic_subnet_count);
}
bool routing_add_local_subnet(struct routing_table *table, uint32_t network, uint8_t prefix_length) {
return enhanced_routing_add_subnet_range(table, network, prefix_length, &table->local_subnets, &table->local_subnet_count);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Added ETCP connection to routing");
}
bool routing_get_all_routes(const struct routing_table *table, uint32_t network, uint8_t prefix_length,
struct route_entry **routes, size_t *count) {
if (!table || !routes || !count) return false;
*routes = NULL;
*count = 0;
// Count matching routes
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].network == network && table->entries[i].prefix_length == prefix_length) {
(*count)++;
}
// Unregister ETCP connection from routing
void routing_del_conn(struct ETCP_CONN* etcp) {
if (!etcp) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_del_conn: etcp is NULL");
return;
}
if (*count == 0) return true; // No routes found, but not an error
// Allocate result array
*routes = calloc(*count, sizeof(struct route_entry));
if (!*routes) {
*count = 0;
return false;
if (!etcp->normalizer) {
return; // Nothing to do
}
// Copy matching routes
size_t result_index = 0;
for (size_t i = 0; i < table->count; i++) {
if (table->entries[i].network == network && table->entries[i].prefix_length == prefix_length) {
(*routes)[result_index] = table->entries[i];
result_index++;
}
}
// Note: ll_queue doesn't have a direct way to clear callback
// The callback will be ignored when etcp is freed
return true;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Removed ETCP connection from routing");
}
void routing_table_print(const struct routing_table *table) {
if (!table) return;
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "=== Routing Table ===");
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Total routes: %zu", table->count);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Dynamic subnets: %zu, Local subnets: %zu",
table->dynamic_subnet_count / 2, table->local_subnet_count / 2);
if (table->count > 0) {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routes:");
for (size_t i = 0; i < table->count; i++) {
const struct route_entry *entry = &table->entries[i];
char network_str[16], next_hop_str[16];
ip_to_string(entry->network, network_str);
ip_to_string(entry->next_hop_ip, next_hop_str);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d -> %s [%s]",
i + 1, network_str, entry->prefix_length, next_hop_str,
route_type_to_string(entry->type));
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Bandwidth: %uK, Loss: %.2f%%, Latency: %ums, Hops: %d",
entry->metrics.bandwidth_kbps,
entry->metrics.packet_loss_rate / 100.0,
entry->metrics.latency_ms,
entry->metrics.hop_count);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " Flags: %s%s%s%s",
(entry->flags & ROUTE_FLAG_ACTIVE) ? "ACTIVE " : "",
(entry->flags & ROUTE_FLAG_VALIDATED) ? "VALIDATED " : "",
(entry->flags & ROUTE_FLAG_ADVERTISED) ? "ADVERTISED " : "",
(entry->flags & ROUTE_FLAG_LEARNED) ? "LEARNED" : "");
}
} else {
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "No routes configured.");
// Set TUN interface for routing
void routing_set_tun(struct UTUN_INSTANCE* instance) {
if (!instance) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_set_tun: instance is NULL");
return;
}
}
const char *route_type_to_string(route_type_t type) {
switch (type) {
case ROUTE_TYPE_STATIC: return "STATIC";
case ROUTE_TYPE_DYNAMIC: return "DYNAMIC";
case ROUTE_TYPE_LOCAL: return "LOCAL";
case ROUTE_TYPE_LEARNED: return "LEARNED";
default: return "UNKNOWN";
if (!instance->tun) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_set_tun: instance has no TUN");
return;
}
}
char *ip_to_string(uint32_t ip, char *buffer) {
if (!buffer) return NULL;
struct in_addr addr;
addr.s_addr = htonl(ip);
strncpy(buffer, inet_ntoa(addr), 15);
buffer[15] = '\0';
return buffer;
}
// Set callback on TUN output queue (packets from TUN to routing)
queue_set_callback(instance->tun->output_queue, routing_pkt_from_tun_cb, instance);
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "TUN interface registered in routing module");
}

201
src/routing.h

@ -1,201 +1,46 @@
// routing.h - Centralized routing module for utun
#ifndef ROUTING_H
#define ROUTING_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// Forward declarations
struct ETCP_CONNECTIONS;
/**
* @brief Типы маршрутов
*/
typedef enum {
ROUTE_TYPE_STATIC = 0, /**< Статический маршрут */
ROUTE_TYPE_DYNAMIC = 1, /**< Динамический маршрут */
ROUTE_TYPE_LOCAL = 2, /**< Локальный маршрут */
ROUTE_TYPE_LEARNED = 3 /**< Изученный маршрут */
} route_type_t;
/**
* @brief Флаги маршрута
*/
typedef enum {
ROUTE_FLAG_ACTIVE = (1 << 0), /**< Маршрут активен */
ROUTE_FLAG_VALIDATED = (1 << 1), /**< Маршрут валидирован */
ROUTE_FLAG_ADVERTISED = (1 << 2), /**< Маршрут анонсирован */
ROUTE_FLAG_LEARNED = (1 << 3) /**< Маршрут изучен */
} route_flags_t;
/**
* @brief Расширенные метрики маршрута
*
* Структура содержит дополнительные метрики для оценки качества маршрута.
*/
struct route_metrics {
uint32_t bandwidth_kbps; /**< Пропускная способность в Кбит/с */
uint16_t packet_loss_rate; /**< Уровень потери пакетов (в промилле) */
uint16_t latency_ms; /**< Задержка в миллисекундах */
uint8_t hop_count; /**< Количество прыжков */
uint64_t last_updated; /**< Время последнего обновления (в микросекундах) */
};
/**
* @brief Расширенная запись маршрута
*
* Структура представляет собой отдельную запись в таблице маршрутизации с детальной информацией о маршруте.
*/
struct route_entry {
uint32_t network; /**< Сетевой адрес */
uint8_t prefix_length; /**< Длина префикса подсети */
uint32_t next_hop_ip; /**< IP-адрес следующего хопа */
struct ETCP_SOCKET* next_hop; /**< Указатель на сокет следующего хопа */
route_type_t type; /**< Тип маршрута */
uint8_t flags; /**< Флаги маршрута */
struct route_metrics metrics; /**< Метрики маршрута */
uint64_t created_time; /**< Время создания (в микросекундах) */
uint64_t last_update; /**< Время последнего обновления (в микросекундах) */
uint64_t last_used; /**< Время последнего использования (в микросекундах) */
};
/**
* @brief Таблица маршрутизации
*
* Структура представляет собой таблицу маршрутизации, содержащую записи маршрутов, подсети и статистику.
*/
struct routing_table {
struct route_entry *entries; /**< Массив записей маршрутов */
size_t count; /**< Текущее количество записей */
size_t capacity; /**< Максимальная емкость таблицы */
uint32_t *dynamic_subnets; /**< Динамические подсети (массив пар: сеть, префикс) */
size_t dynamic_subnet_count; /**< Количество динамических подсетей */
uint32_t *local_subnets; /**< Локальные подсети (массив пар: сеть, префикс) */
size_t local_subnet_count; /**< Количество локальных подсетей */
struct {
uint64_t total_routes; /**< Общее количество маршрутов */
uint64_t static_routes; /**< Количество статических маршрутов */
uint64_t dynamic_routes; /**< Количество динамических маршрутов */
uint64_t local_routes; /**< Количество локальных маршрутов */
uint64_t learned_routes; /**< Количество изученных маршрутов */
uint64_t routes_added; /**< Количество добавленных маршрутов */
uint64_t routes_deleted; /**< Количество удаленных маршрутов */
uint64_t lookup_count; /**< Количество запросов поиска */
uint64_t hit_count; /**< Количество успешных поисков */
uint64_t routes_lookup_hits; /**< Количество попаданий в поиск маршрутов */
uint64_t routes_lookup_misses; /**< Количество промахов в поиск маршрутов */
uint64_t validation_failures; /**< Количество неудачных валидаций */
} stats; /**< Статистика таблицы маршрутизации */
};
/**
* @brief Создает новую таблицу маршрутизации
*
* @return Указатель на созданную таблицу или NULL в случае ошибки
*/
struct routing_table *routing_table_create(void);
/**
* @brief Уничтожает таблицу маршрутизации и освобождает ресурсы
*
* @param table Указатель на таблицу маршрутизации
*/
void routing_table_destroy(struct routing_table *table);
/**
* @brief Вставляет новую запись в таблицу маршрутизации
*
* @param table Указатель на таблицу маршрутизации
* @param entry Указатель на вставляемую запись маршрута
* @return true если вставка успешна, false иначе
*/
bool routing_table_insert(struct routing_table *table, const struct route_entry *entry);
/**
* @brief Удаляет запись из таблицы маршрутизации
*
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса
* @param source_node_id Идентификатор источника узла
* @return true если удаление успешно, false иначе
*/
bool routing_table_delete(struct routing_table *table, uint32_t network, uint8_t prefix_length, uint32_t source_node_id);
/**
* @brief Выполняет поиск лучшего маршрута для заданного IP-адреса
*
* @param table Указатель на таблицу маршрутизации
* @param dest_ip Целевой IP-адрес
* @param best_route Указатель на структуру для хранения лучшего маршрута
* @return true если маршрут найден, false иначе
*/
bool routing_table_lookup(struct routing_table *table, uint32_t dest_ip, struct route_entry *best_route);
/**
* @brief Валидирует маршрут на основе типа и подсетей
*
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса
* @param route_type Тип маршрута
* @return true если маршрут валиден, false иначе
*/
bool routing_validate_route(struct routing_table *table, uint32_t network, uint8_t prefix_length, route_type_t route_type);
/**
* @brief Добавляет динамическую подсеть в таблицу
*
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса
* @return true если добавление успешно, false иначе
*/
bool routing_add_dynamic_subnet(struct routing_table *table, uint32_t network, uint8_t prefix_length);
struct ETCP_CONN;
struct UTUN_INSTANCE;
/**
* @brief Добавляет локальную подсеть в таблицу
*
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса
* @return true если добавление успешно, false иначе
* @brief Initialize routing module for instance
* @param instance UTUN instance
* @return 0 on success, -1 on error
*/
bool routing_add_local_subnet(struct routing_table *table, uint32_t network, uint8_t prefix_length);
int routing_create(struct UTUN_INSTANCE* instance);
/**
* @brief Получает все маршруты для заданной сети и префикса
*
* @param table Указатель на таблицу маршрутизации
* @param network Сетевой адрес
* @param prefix_length Длина префикса
* @param routes Указатель на массив маршрутов (будет выделен)
* @param count Указатель на количество найденных маршрутов
* @return true если операция успешна, false иначе
* @brief Destroy routing module for instance
* @param instance UTUN instance
*/
bool routing_get_all_routes(const struct routing_table *table, uint32_t network, uint8_t prefix_length, struct route_entry **routes, size_t *count);
void routing_destroy(struct UTUN_INSTANCE* instance);
/**
* @brief Печатает содержимое таблицы маршрутизации
*
* @param table Указатель на таблицу маршрутизации
* @brief Register ETCP connection with routing
* Called from pn_init() to register connection's normalizer output queue
* @param etcp ETCP connection
*/
void routing_table_print(const struct routing_table *table);
void routing_add_conn(struct ETCP_CONN* etcp);
/**
* @brief Преобразует тип маршрута в строковое представление
*
* @param type Тип маршрута
* @return Строковое представление типа
* @brief Unregister ETCP connection from routing
* Called from pn_deinit() to unregister connection's normalizer output queue
* @param etcp ETCP connection
*/
const char* route_type_to_string(route_type_t type);
void routing_del_conn(struct ETCP_CONN* etcp);
/**
* @brief Преобразует IP-адрес в строковое представление
*
* @param ip IP-адрес в сетевом формате
* @param buffer Буфер для хранения строки (минимум 16 байт)
* @return Указатель на буфер с строкой
* @brief Set TUN interface for routing
* Called from utun_instance_init() after tun_init()
* @param instance UTUN instance with configured tun
*/
char* ip_to_string(uint32_t ip, char *buffer);
void routing_set_tun(struct UTUN_INSTANCE* instance);
#endif // ROUTING_H

7
src/routing.txt

@ -1,7 +0,0 @@
internal_routing.c/h:
это модуль роутинга пакетов.
пакеты приходят:
1. из очередей ETCP (struct ETCP_CONN output_queue).
при добавлении подключения (из конфига)) в вызовы etcp_connection_create/close надо добавить
2. из tun интерфейса

73
src/tun_if.c

@ -168,6 +168,44 @@ static int tun_set_mtu(const char *ifname, int mtu) {
return 0;
}
// Callback for input_queue - called when routing puts packet to send to TUN
static void tun_input_queue_callback(struct ll_queue* q, void* arg) {
(void)q;
struct tun_if* tun = (struct tun_if*)arg;
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_data_get(tun->input_queue);
while (pkt) {
if (pkt->ll.dgram && pkt->ll.len > 0) {
// Check MTU
if (pkt->ll.len > TUN_MAX_PACKET_SIZE) {
DEBUG_WARN(DEBUG_CATEGORY_TUN, "Packet too large for TUN: %zu bytes (max %d)",
pkt->ll.len, TUN_MAX_PACKET_SIZE);
} else {
ssize_t nwritten = write(tun->fd, pkt->ll.dgram, pkt->ll.len);
if (nwritten < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to write to TUN %s: %s",
tun->ifname, strerror(errno));
tun->write_errors++;
} else {
tun->bytes_written += nwritten;
tun->packets_written++;
DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "Wrote %zd bytes to TUN %s from input_queue",
nwritten, tun->ifname);
}
}
// Free packet data
free(pkt->ll.dgram);
}
// Free fragment structure
memory_pool_free(tun->pool, pkt);
// Get next packet
pkt = (struct ETCP_FRAGMENT*)queue_data_get(tun->input_queue);
}
}
// Internal read callback - called by uasync when data available on TUN
static void tun_read_callback(int fd, void* user_arg) {
struct tun_if* tun = (struct tun_if*)user_arg;
@ -209,7 +247,7 @@ static void tun_read_callback(int fd, void* user_arg) {
pkt->ll.len = nread;
// Add to input queue
if (queue_data_put(tun->input_queue, (struct ll_entry*)pkt, 0) != 0) {
if (queue_data_put(tun->output_queue, (struct ll_entry*)pkt, 0) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to add packet to input queue");
free(packet_data);
memory_pool_free(tun->pool, pkt);
@ -306,21 +344,35 @@ struct tun_if* tun_init(struct UASYNC* ua, struct utun_config* config) {
return NULL;
}
// Create input queue
// Create output queue (packets from TUN to routing)
tun->output_queue = queue_new(ua, 0); // hash_size=0 - no ID lookup needed
if (!tun->output_queue) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create output queue");
memory_pool_destroy(tun->pool);
close(tun->fd);
free(tun);
return NULL;
}
// Create input queue (packets from routing to TUN)
tun->input_queue = queue_new(ua, 0); // hash_size=0 - no ID lookup needed
if (!tun->input_queue) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create input queue");
queue_free(tun->output_queue);
memory_pool_destroy(tun->pool);
close(tun->fd);
free(tun);
return NULL;
}
// Set callback for input_queue - routing will put packets here
queue_set_callback(tun->input_queue, tun_input_queue_callback, tun);
// Register TUN socket with uasync
tun->socket_id = uasync_add_socket(ua, tun->fd, tun_read_callback, NULL, NULL, tun);
if (!tun->socket_id) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to register TUN socket with uasync");
queue_free(tun->input_queue);
queue_free(tun->output_queue);
memory_pool_destroy(tun->pool);
close(tun->fd);
free(tun);
@ -344,7 +396,7 @@ void tun_close(struct tun_if* tun) {
tun->socket_id = NULL;
}
// Drain and free all packets from input queue
// Drain and free all packets from input_queue (packets from routing to TUN)
if (tun->input_queue) {
struct ETCP_FRAGMENT* pkt;
while ((pkt = (struct ETCP_FRAGMENT*)queue_data_get(tun->input_queue)) != NULL) {
@ -357,6 +409,19 @@ void tun_close(struct tun_if* tun) {
tun->input_queue = NULL;
}
// Drain and free all packets from output_queue (packets from TUN to routing)
if (tun->output_queue) {
struct ETCP_FRAGMENT* pkt;
while ((pkt = (struct ETCP_FRAGMENT*)queue_data_get(tun->output_queue)) != NULL) {
if (pkt->ll.dgram) {
free(pkt->ll.dgram);
}
memory_pool_free(tun->pool, pkt);
}
queue_free(tun->output_queue);
tun->output_queue = NULL;
}
// Destroy memory pool
if (tun->pool) {
memory_pool_destroy(tun->pool);

19
src/tun_if.h

@ -26,7 +26,8 @@ struct tun_if {
struct UASYNC* ua; // uasync instance
void* socket_id; // Socket ID from uasync_add_socket
struct memory_pool* pool; // Pool for ETCP_FRAGMENT structures
struct ll_queue* input_queue; // Queue for incoming packets (ETCP_FRAGMENT)
struct ll_queue* output_queue; // Queue for incoming packets from TUN (to routing)
struct ll_queue* input_queue; // Queue for outgoing packets to TUN (from routing)
// Statistics
uint64_t bytes_read; // Bytes read from TUN
uint64_t bytes_written; // Bytes written to TUN
@ -50,7 +51,7 @@ struct tun_if* tun_init(struct UASYNC* ua, struct utun_config* config);
/**
* @brief Close TUN interface and free resources
* Unregisters socket from uasync, closes fd, drains and frees
* all packets from input_queue, destroys pool and queue.
* all packets from output_queue, destroys pool and queue.
* @param tun TUN interface handle
*/
void tun_close(struct tun_if* tun);
@ -64,20 +65,6 @@ void tun_close(struct tun_if* tun);
*/
ssize_t tun_write(struct tun_if* tun, const uint8_t* buf, size_t len);
/**
* @brief Get input queue for reading packets
* User should call queue_data_get(tun->input_queue) to retrieve packets.
* Each packet is struct ETCP_FRAGMENT with:
* - ll.dgram: malloc'd packet data (user must free)
* - ll.len: packet length
* User must free dgram and return fragment to pool via memory_pool_free().
* @param tun TUN interface handle
* @return Pointer to input queue
*/
static inline struct ll_queue* tun_get_input_queue(struct tun_if* tun) {
return tun ? tun->input_queue : NULL;
}
#ifdef __cplusplus
}
#endif

2
src/utun.c

@ -5,7 +5,7 @@
#include "etcp_connections.h"
#include "tun_if.h"
#include "secure_channel.h"
#include "routing.h"
#include "route_lib.h"
#include "utun_instance.h"
#include "u_async.h"
#include "debug_config.h"

48
src/utun_instance.c

@ -3,6 +3,7 @@
#include "config_parser.h"
#include "config_updater.h"
#include "tun_if.h"
#include "route_lib.h"
#include "routing.h"
#include "etcp_connections.h"
#include "etcp.h"
@ -79,6 +80,44 @@ struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char *config
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");
free_config(instance->config);
free(instance);
return NULL;
}
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing module created");
// Add local subnets from config as static routes
struct CFG_ROUTE_ENTRY* subnet = instance->config->my_subnets;
while (subnet) {
struct route_entry entry = {0};
entry.network = subnet->ip.addr.v4.s_addr; // Network byte order
entry.prefix_length = subnet->netmask;
entry.next_hop = NULL; // Local route
entry.type = ROUTE_TYPE_LOCAL;
entry.flags = ROUTE_FLAG_ACTIVE | ROUTE_FLAG_VALIDATED;
entry.metrics.bandwidth_kbps = UINT32_MAX; // Unlimited
entry.metrics.latency_ms = 0;
entry.metrics.packet_loss_rate = 0;
entry.metrics.hop_count = 0;
if (route_table_insert(instance->rt, &entry)) {
char ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &entry.network, ip_str, sizeof(ip_str));
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Added local route: %s/%d",
ip_str, entry.prefix_length);
} else {
char ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &entry.network, ip_str, sizeof(ip_str));
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "Failed to add local route: %s/%d (skipping)",
ip_str, entry.prefix_length);
}
subnet = subnet->next;
}
// Initialize TUN device only if enabled
if (g_tun_init_enabled) {
instance->tun = tun_init(ua, instance->config);
@ -141,6 +180,9 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
instance->tun = NULL;
}
// Cleanup routing module
routing_destroy(instance);
// Cleanup config
if (instance->config) {
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Freeing configuration");
@ -200,6 +242,12 @@ void utun_instance_stop(struct UTUN_INSTANCE *instance) {
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

5
src/utun_instance.h

@ -10,7 +10,7 @@
// Forward declarations
struct utun_config;
struct uasync_s;
struct routing_table;
struct route_table;
struct ETCP_CONN;
struct ETCP_SOCKET;
struct tun_if;
@ -22,6 +22,8 @@ struct UTUN_INSTANCE {
// TUN interface
struct tun_if* tun;
struct route_table* rt;
// Identification
uint64_t node_id;
@ -44,6 +46,7 @@ struct UTUN_INSTANCE {
// Active sockets
struct ETCP_SOCKET* etcp_sockets;// linked-list
};
// Functions

5
tests/Makefile.am

@ -66,6 +66,7 @@ ETCP_CORE_OBJS = \
ETCP_FULL_OBJS = \
$(top_builddir)/src/utun-config_parser.o \
$(top_builddir)/src/utun-config_updater.o \
$(top_builddir)/src/utun-route_lib.o \
$(top_builddir)/src/utun-routing.o \
$(top_builddir)/src/utun-tun_if.o \
$(top_builddir)/src/utun-utun_instance.o \
@ -99,7 +100,7 @@ test_etcp_simple_traffic_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYP
# ETCP minimal test
test_etcp_minimal_SOURCES = test_etcp_minimal.c
test_etcp_minimal_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
test_etcp_minimal_LDADD = $(ETCP_CORE_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS)
test_etcp_minimal_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYPTO_LIBS) $(COMMON_LIBS)
# ETCP 100 packets test
test_etcp_100_packets_SOURCES = test_etcp_100_packets.c
@ -114,7 +115,7 @@ test_pkt_normalizer_etcp_LDADD = $(ETCP_FULL_OBJS) $(SECURE_CHANNEL_OBJS) $(CRYP
# Standalone pkt_normalizer test with mock ETCP loopback
test_pkt_normalizer_standalone_SOURCES = test_pkt_normalizer_standalone.c
test_pkt_normalizer_standalone_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib -I$(top_srcdir)/tinycrypt/lib/include -I$(top_srcdir)/tinycrypt/lib/source
test_pkt_normalizer_standalone_LDADD = $(top_builddir)/src/utun-pkt_normalizer.o $(TINYCRYPT_OBJS) $(COMMON_LIBS)
test_pkt_normalizer_standalone_LDADD = $(top_builddir)/src/utun-pkt_normalizer.o $(top_builddir)/src/utun-route_lib.o $(top_builddir)/src/utun-routing.o $(TINYCRYPT_OBJS) $(COMMON_LIBS)
# Basic crypto test (TinyCrypt only - no secure_channel)
test_crypto_SOURCES = test_crypto.c

BIN
tests/test_etcp_100_packets

Binary file not shown.

8
tests/test_etcp_100_packets.c

@ -149,6 +149,9 @@ static void check_received_packets_fwd(void) {
struct ETCP_CONN* conn = server_instance->connections;
if (!conn || !conn->output_queue) return;
// Disable routing callback to keep packets in output_queue for test verification
queue_set_callback(conn->output_queue, NULL, NULL);
struct ETCP_FRAGMENT* pkt;
while ((pkt = (struct ETCP_FRAGMENT*)queue_data_get(conn->output_queue)) != NULL) {
if (pkt->ll.len >= PACKET_SIZE) {
@ -178,7 +181,10 @@ static void check_received_packets_back(void) {
struct ETCP_CONN* conn = client_instance->connections;
if (!conn || !conn->output_queue) return;
// Disable routing callback to keep packets in output_queue for test verification
queue_set_callback(conn->output_queue, NULL, NULL);
struct ETCP_FRAGMENT* pkt;
while ((pkt = (struct ETCP_FRAGMENT*)queue_data_get(conn->output_queue)) != NULL) {
if (pkt->ll.len >= PACKET_SIZE) {

BIN
tests/test_etcp_minimal

Binary file not shown.

BIN
tests/test_etcp_simple_traffic

Binary file not shown.

5
tests/test_etcp_simple_traffic.c

@ -159,6 +159,9 @@ static void check_packet_received(void) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "check_packet_received: Checking server connection %p", conn);
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "check_packet_received: Server output_queue count: %d", queue_entry_count(conn->output_queue));
// Disable routing callback to keep packets in output_queue for test verification
queue_set_callback(conn->output_queue, NULL, NULL);
// Check if there's any packet in output queue
struct ETCP_FRAGMENT* pkt = (struct ETCP_FRAGMENT*)queue_data_get(conn->output_queue);
if (pkt) {
@ -376,7 +379,7 @@ int main() {
conn = conn->next;
}
printf("=== End Debug ===\n\n");
// Start monitoring and packet transmission
printf("Starting packet transmission test...\n");
packet_timeout_id = uasync_set_timeout(ua, 500, NULL, monitor_and_send);

BIN
tests/test_etcp_two_instances

Binary file not shown.

BIN
tests/test_pkt_normalizer_etcp

Binary file not shown.

4
tests/test_pkt_normalizer_etcp.c

@ -469,10 +469,10 @@ int main() {
if (global_timeout_id) uasync_cancel_timeout(ua, global_timeout_id);
if (server_pn) {
pn_pair_deinit(server_pn);
pn_deinit(server_pn);
}
if (client_pn) {
pn_pair_deinit(client_pn);
pn_deinit(client_pn);
}
if (server_instance) {

BIN
tests/test_pkt_normalizer_standalone

Binary file not shown.

2
tests/test_pkt_normalizer_standalone.c

@ -198,7 +198,7 @@ static int init_mock_etcp(void) {
// Cleanup
static void cleanup(void) {
if (pn) {
pn_pair_deinit(pn);
pn_deinit(pn);
}
if (mock_etcp.input_queue) {

Loading…
Cancel
Save