Test suite cleanup and reorganization

- Deleted 11 obsolete debug/isolation test files
- Restored test_pkt_normalizer.c from git repository
- Added test_ecc_encrypt and test_routing to Makefile (2 new functional tests)
- Removed settings.o from PN_OBJS (settings.c deleted)
- Added crc32.o to SC_LIB_OBJS and ETCP_OBJS to fix linker errors
- Removed unused routing_get_stats() function (undefined routing_stats_t type)
- Updated changelog with detailed cleanup summary

Current test count: 16 tests in Makefile (14 original + 2 new)Active test files in tests/: 25 files (was 35)test_ecc_encrypt: PASStest_routing: Compilation OK
This commit is contained in:
Evgeny committed 2026-01-18 09:24:13 +03:00
1 parent 4e544156fd
commit 2c58c008ce
52 files changed
+2488 -6138

No files matched your search

+276 -541
View File
@@ -1,4 +1,4 @@
// config_parser.c - Configuration parser for utun application
// config_parser.c - Configuration parser for utun application (simplified)
#define _POSIX_C_SOURCE 200809L
#include "config_parser.h"
#include <stdio.h>
@@ -6,43 +6,38 @@
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>
#include <arpa/inet.h>
#define MAX_LINE_LEN 1024
#define INITIAL_CONNECTION_CAPACITY 4
#define INITIAL_SERVER_CAPACITY 4
#define INITIAL_CLIENT_CAPACITY 4
#define INITIAL_CONNECTION_V2_CAPACITY 4
#define INITIAL_ARRAY_CAPACITY 8
typedef enum {
SECTION_UNKNOWN,
SECTION_GLOBAL,
SECTION_SERVER,
SECTION_CLIENT,
SECTION_CONNECTION
} section_type_t;
// Helper function to trim whitespace
static char* trim(char *str) {
if (!str) return NULL;
// Trim leading spaces
while (isspace((unsigned char)*str)) str++;
// Trim trailing spaces
char *end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) end--;
*(end + 1) = '\0';
return str;
}
// Parse a key-value pair
static int parse_key_value(const char *line, char *key, size_t key_len,
char *value, size_t value_len) {
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;
// Extract key
size_t key_size = equal - line;
if (key_size >= key_len) return -1;
strncpy(key, line, key_size);
key[key_size] = '\0';
trim(key);
// Extract value
const char *val_start = equal + 1;
size_t val_len = strlen(val_start);
if (val_len >= value_len) return -1;
@@ -52,115 +47,262 @@ static int parse_key_value(const char *line, char *key, size_t key_len,
return 0;
}
// Add a server to the config
static int add_server(utun_config_t *config, const server_config_t *server) {
if (config->server_count >= config->server_capacity) {
// Resize array
int new_capacity = config->server_capacity * 2;
server_config_t *new_servers = realloc(config->servers,
new_capacity * sizeof(server_config_t));
if (!new_servers) return -1;
config->servers = new_servers;
config->server_capacity = new_capacity;
}
static int ensure_capacity(void **array, int *capacity, int item_size) {
if (!array || !capacity) return -1;
// Copy server data
memcpy(&config->servers[config->server_count], server, sizeof(server_config_t));
config->server_count++;
int new_capacity = *capacity == 0 ? INITIAL_ARRAY_CAPACITY : *capacity * 2;
void *new_array = realloc(*array, new_capacity * item_size);
if (!new_array) return -1;
*array = new_array;
*capacity = new_capacity;
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_port(const char *addr, uint32_t *ip, uint16_t *port) {
char addr_copy[MAX_ADDR_LEN];
if (strlen(addr) >= MAX_ADDR_LEN) return -1;
strcpy(addr_copy, addr);
char *colon = strrchr(addr_copy, ':');
if (!colon) return -1;
*colon = '\0';
*port = (uint16_t)atoi(colon + 1);
struct in_addr ina;
if (inet_pton(AF_INET, addr_copy, &ina) != 1) return -1;
*ip = ina.s_addr;
return 0;
}
// Add a client to the config
static int add_client(utun_config_t *config, const client_config_t *client) {
if (config->client_count >= config->client_capacity) {
// Resize array
int new_capacity = config->client_capacity * 2;
client_config_t *new_clients = realloc(config->clients,
new_capacity * sizeof(client_config_t));
if (!new_clients) return -1;
config->clients = new_clients;
config->client_capacity = new_capacity;
static int add_server(utun_config_t *cfg, const server_config_t *srv) {
if (cfg->server_count >= cfg->server_capacity) {
if (ensure_capacity((void**)&cfg->servers, &cfg->server_capacity, sizeof(server_config_t)) < 0) return -1;
}
memcpy(&cfg->servers[cfg->server_count++], srv, sizeof(server_config_t));
return 0;
}
static int add_client(utun_config_t *cfg, const client_config_t *cli) {
if (cfg->client_count >= cfg->client_capacity) {
if (ensure_capacity((void**)&cfg->clients, &cfg->client_capacity, sizeof(client_config_t)) < 0) return -1;
}
memcpy(&cfg->clients[cfg->client_count++], cli, sizeof(client_config_t));
return 0;
}
static int add_route(utun_config_t *cfg, const char *server, const char *client) {
if (!cfg->connections_v2) {
cfg->connections_v2 = calloc(1, sizeof(connection_config_v2_t));
if (!cfg->connections_v2) return -1;
cfg->connection_v2_capacity = 1;
}
// Copy client data
memcpy(&config->clients[config->client_count], client, sizeof(client_config_t));
config->client_count++;
connection_config_v2_t *conn = &cfg->connections_v2[cfg->connection_v2_count];
if (conn->route_count >= conn->route_capacity) {
if (ensure_capacity((void**)&conn->routes, &conn->route_capacity, sizeof(route_pair_t)) < 0) return -1;
}
route_pair_t *route = &conn->routes[conn->route_count++];
if (assign_string(route->server_name, MAX_CONN_NAME_LEN, server) < 0) return -1;
if (assign_string(route->client_name, MAX_CONN_NAME_LEN, client) < 0) return -1;
return 0;
}
// Add a route to connection v2
static int add_route_to_connection(connection_config_v2_t *conn_v2, const route_pair_t *route) {
if (conn_v2->route_count >= conn_v2->route_capacity) {
// Resize array
int new_capacity = conn_v2->route_capacity == 0 ? INITIAL_CONNECTION_V2_CAPACITY : conn_v2->route_capacity * 2;
route_pair_t *new_routes = realloc(conn_v2->routes,
new_capacity * sizeof(route_pair_t));
if (!new_routes) return -1;
conn_v2->routes = new_routes;
conn_v2->route_capacity = new_capacity;
}
// Copy route data
memcpy(&conn_v2->routes[conn_v2->route_count], route, sizeof(route_pair_t));
conn_v2->route_count++;
static int parse_global(const char *key, const char *value, global_config_t *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, "tun_ip") == 0) return assign_string(global->tun_ip, MAX_ADDR_LEN, value);
if (strcmp(key, "mtu") == 0) { global->mtu = atoi(value); return 0; }
if (strcmp(key, "control_ip") == 0) return assign_string(global->control_ip, MAX_ADDR_LEN, value);
if (strcmp(key, "control_port") == 0) { global->control_port = (uint16_t)atoi(value); return 0; }
if (strcmp(key, "net_debug") == 0) { global->net_debug = atoi(value); return 0; }
return 0;
}
// Add a connection v2 to the config
static int add_connection_v2(utun_config_t *config, const connection_config_v2_t *conn_v2) {
if (config->connection_v2_count >= config->connection_v2_capacity) {
// Resize array
int new_capacity = config->connection_v2_capacity * 2;
connection_config_v2_t *new_connections = realloc(config->connections_v2,
new_capacity * sizeof(connection_config_v2_t));
if (!new_connections) return -1;
config->connections_v2 = new_connections;
config->connection_v2_capacity = new_capacity;
}
// Copy connection data
memcpy(&config->connections_v2[config->connection_v2_count], conn_v2, sizeof(connection_config_v2_t));
config->connection_v2_count++;
static int parse_server(const char *key, const char *value, server_config_t *srv) {
if (strcmp(key, "addr") == 0) return assign_string(srv->addr, MAX_ADDR_LEN, value);
if (strcmp(key, "so_mark") == 0) { srv->so_mark = atoi(value); return 0; }
if (strcmp(key, "netif") == 0) return assign_string(srv->netif, MAX_NETIF_LEN, value);
if (strcmp(key, "type") == 0) return 0; // Ignored: not stored or used
return 0;
}
// Parse configuration file
static int parse_client(const char *key, const char *value, client_config_t *cli) {
if (strcmp(key, "from") == 0) return assign_string(cli->from, MAX_CONN_NAME_LEN, value);
if (strcmp(key, "to_addr") == 0) return assign_string(cli->to_addr, MAX_ADDR_LEN, value);
// Support for test config format: addr1, addr2
if (strncmp(key, "addr", 4) == 0) return assign_string(cli->to_addr, MAX_ADDR_LEN, value);
return 0;
}
static int parse_connection(const char *key, const char *value, connection_config_v2_t *conn) {
if (strcmp(key, "link") == 0) {
char link_copy[MAX_CONN_NAME_LEN * 2];
if (strlen(value) >= sizeof(link_copy)) return -1;
strcpy(link_copy, value);
char *colon = strchr(link_copy, ':');
if (!colon) return -1;
*colon = '\0';
return add_route(NULL, link_copy, colon + 1); // NULL cfg - will use global
}
if (strcmp(key, "keepalive") == 0) { conn->keepalive = atoi(value); return 0; }
if (strcmp(key, "peer_public_key") == 0) return assign_string(conn->peer_public_key_hex, MAX_KEY_LEN, value);
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;
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;
if (strcasecmp(type, "connection") == 0) return SECTION_CONNECTION;
return SECTION_UNKNOWN;
}
static utun_config_t* parse_config_internal(FILE *fp, const char *filename) {
utun_config_t *cfg = calloc(1, sizeof(utun_config_t));
if (!cfg) return NULL;
section_type_t cur_section = SECTION_UNKNOWN;
server_config_t cur_server = {0};
client_config_t cur_client = {0};
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] == '[') {
if (cur_section == SECTION_SERVER && cur_server.name[0]) {
if (add_server(cfg, &cur_server) < 0) goto error;
}
if (cur_section == SECTION_CLIENT && cur_client.name[0]) {
if (add_client(cfg, &cur_client) < 0) goto error;
}
char name[MAX_CONN_NAME_LEN];
cur_section = parse_section_header(trimmed, name, sizeof(name));
if (cur_section == SECTION_SERVER) {
memset(&cur_server, 0, sizeof(cur_server));
strcpy(cur_server.name, name);
} else if (cur_section == SECTION_CLIENT) {
memset(&cur_client, 0, sizeof(cur_client));
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) {
fprintf(stderr, "%s:%d: Invalid key=value format\n", filename, line_num);
continue;
}
switch (cur_section) {
case SECTION_GLOBAL:
if (parse_global(key, value, &cfg->global) < 0) {
fprintf(stderr, "%s:%d: Invalid global key '%s'\n", filename, line_num, key);
}
break;
case SECTION_SERVER:
if (parse_server(key, value, &cur_server) < 0) {
fprintf(stderr, "%s:%d: Invalid server key '%s'\n", filename, line_num, key);
}
break;
case SECTION_CLIENT:
if (parse_client(key, value, &cur_client) < 0) {
fprintf(stderr, "%s:%d: Invalid client key '%s'\n", filename, line_num, key);
}
break;
case SECTION_CONNECTION:
if (cfg->connection_v2_count == 0) {
cfg->connection_v2_count = 1;
}
if (parse_connection(key, value, &cfg->connections_v2[0]) < 0) {
fprintf(stderr, "%s:%d: Invalid connection key '%s'\n", filename, line_num, key);
}
break;
default:
fprintf(stderr, "%s:%d: Key outside section: %s\n", filename, line_num, key);
break;
}
}
if (cur_section == SECTION_SERVER && cur_server.name[0]) {
if (add_server(cfg, &cur_server) < 0) goto error;
}
if (cur_section == SECTION_CLIENT && cur_client.name[0]) {
if (add_client(cfg, &cur_client) < 0) goto error;
}
return cfg;
error:
free_config(cfg);
return NULL;
}
utun_config_t* parse_config(const char *filename) {
/* Parse only v2 format */
return parse_config_v2(filename);
FILE *fp = fopen(filename, "r");
if (!fp) {
fprintf(stderr, "Failed to open config file: %s\n", filename);
return NULL;
}
utun_config_t *config = parse_config_internal(fp, filename);
fclose(fp);
return config;
}
// Free configuration structure
void free_config(utun_config_t *config) {
if (!config) return;
if (config->servers) {
free(config->servers);
}
if (config->clients) {
free(config->clients);
}
free(config->servers);
free(config->clients);
if (config->connections_v2) {
for (int i = 0; i < config->connection_v2_count; i++) {
if (config->connections_v2[i].routes) {
free(config->connections_v2[i].routes);
}
free(config->connections_v2[i].routes);
}
free(config->connections_v2);
}
@@ -168,458 +310,51 @@ void free_config(utun_config_t *config) {
free(config);
}
// Print configuration for debugging
void print_config(const utun_config_t *config) {
if (!config) {
printf("Configuration is NULL\n");
return;
void print_config(const utun_config_t *cfg) {
if (!cfg) return;
const global_config_t *g = &cfg->global;
printf("Global:\n");
printf(" Private key: %s\n", g->my_private_key_hex);
printf(" Public key: %s\n", g->my_public_key_hex);
printf(" TUN IP: %s\n", g->tun_ip);
printf(" MTU: %d\n", g->mtu);
printf(" Control: %s:%u\n", g->control_ip, g->control_port);
printf(" Net debug: %d\n", g->net_debug);
printf("\nServers (%d):\n", cfg->server_count);
for (int i = 0; i < cfg->server_count; i++) {
const server_config_t *s = &cfg->servers[i];
printf(" %s: %s (mark=%d, netif=%s)\n", s->name, s->addr, s->so_mark, s->netif);
}
printf("Global configuration:\n");
printf(" my_private_key: %s\n", config->global.my_private_key_hex);
printf(" my_public_key: %s\n", config->global.my_public_key_hex);
printf(" option: %s\n", config->global.option_value);
printf(" control_ip: %s\n", config->global.control_ip);
printf(" control_port: %u\n", config->global.control_port);
printf(" net_debug: %d\n", config->global.net_debug);
printf("\n");
printf("Allowed subnets (%d):\n", config->allowed_subnet_count);
for (int i = 0; i < config->allowed_subnet_count; i++) {
printf(" %s\n", config->allowed_subnets[i].subnet);
printf("\nClients (%d):\n", cfg->client_count);
for (int i = 0; i < cfg->client_count; i++) {
const client_config_t *c = &cfg->clients[i];
printf(" %s: %s (from %s)\n", c->name, c->to_addr, c->from);
}
printf("\n");
printf("Servers (%d):\n", config->server_count);
for (int i = 0; i < config->server_count; i++) {
const server_config_t *server = &config->servers[i];
printf(" [%d] %s:\n", i, server->name);
printf(" addr: %s\n", server->addr);
printf(" so_mark: %d\n", server->so_mark);
printf(" netif: %s\n", server->netif);
}
printf("\n");
printf("Clients (%d):\n", config->client_count);
for (int i = 0; i < config->client_count; i++) {
const client_config_t *client = &config->clients[i];
printf(" [%d] %s:\n", i, client->name);
printf(" from: %s\n", client->from);
printf(" to_addr: %s\n", client->to_addr);
}
printf("\n");
printf("Connections v2 (%d):\n", config->connection_v2_count);
for (int i = 0; i < config->connection_v2_count; i++) {
const connection_config_v2_t *conn = &config->connections_v2[i];
printf(" [%d]:\n", i);
printf(" peer_public_key: %s\n", conn->peer_public_key_hex);
printf(" keepalive: %d\n", conn->keepalive);
printf(" routes (%d):\n", conn->route_count);
printf("\nConnections v2 (%d):\n", cfg->connection_v2_count);
for (int i = 0; i < cfg->connection_v2_count; i++) {
const connection_config_v2_t *conn = &cfg->connections_v2[i];
printf(" Connection %d:\n", i);
for (int j = 0; j < conn->route_count; j++) {
printf(" %s:%s\n", conn->routes[j].server_name, conn->routes[j].client_name);
printf(" %s -> %s\n", conn->routes[j].server_name, conn->routes[j].client_name);
}
printf(" Peer key: %s\n", conn->peer_public_key_hex);
printf(" Keepalive: %d\n", conn->keepalive);
}
printf("\n");
}
// Update keys in configuration file
int update_config_keys(const char *filename,
const char *private_key_hex,
const char *public_key_hex) {
if (!filename || !private_key_hex || !public_key_hex) {
errno = EINVAL;
return -1;
}
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;
// Read entire file
FILE *fp = fopen(filename, "r");
if (!fp) {
// Try to create new file
fp = fopen(filename, "w");
if (!fp) return -1;
fprintf(fp, "[global]\n");
fprintf(fp, "my_private_key=%s\n", private_key_hex);
fprintf(fp, "my_public_key=%s\n", public_key_hex);
fclose(fp);
return 0;
}
// Read all lines
char **lines = NULL;
size_t line_count = 0;
size_t capacity = 0;
char line[MAX_LINE_LEN];
int in_global_section = 0;
int private_key_found = 0;
int public_key_found = 0;
size_t private_key_line = 0;
size_t public_key_line = 0;
fprintf(fp, "\n[global]\n");
fprintf(fp, "my_private_key=%s\n", priv_key);
fprintf(fp, "my_public_key=%s\n", pub_key);
while (fgets(line, sizeof(line), fp)) {
// Add line to array
if (line_count >= capacity) {
size_t new_capacity = capacity ? capacity * 2 : 16;
char **new_lines = realloc(lines, new_capacity * sizeof(char *));
if (!new_lines) {
// Cleanup
for (size_t i = 0; i < line_count; i++) free(lines[i]);
free(lines);
fclose(fp);
return -1;
}
lines = new_lines;
capacity = new_capacity;
}
lines[line_count] = strdup(line);
if (!lines[line_count]) {
// Cleanup
for (size_t i = 0; i < line_count; i++) free(lines[i]);
free(lines);
fclose(fp);
return -1;
}
char *trimmed = trim(line);
// Track global section
if (trimmed[0] == '[' && trimmed[strlen(trimmed) - 1] == ']') {
char section[128];
strncpy(section, trimmed + 1, sizeof(section) - 1);
section[sizeof(section) - 1] = '\0';
section[strcspn(section, "]")] = '\0';
trim(section);
in_global_section = (strcasecmp(section, "global") == 0);
}
// Check for keys in global section
if (in_global_section) {
char key[256], value[256];
if (parse_key_value(trimmed, key, sizeof(key), value, sizeof(value)) == 0) {
if (strcasecmp(key, "my_private_key") == 0) {
private_key_found = 1;
private_key_line = line_count;
} else if (strcasecmp(key, "my_public_key") == 0) {
public_key_found = 1;
public_key_line = line_count;
}
}
}
line_count++;
}
fclose(fp);
// Update or add keys
if (private_key_found) {
free(lines[private_key_line]);
char new_line[MAX_LINE_LEN];
snprintf(new_line, sizeof(new_line), "my_private_key=%s\n", private_key_hex);
lines[private_key_line] = strdup(new_line);
} else {
// Add after [global] line or at beginning
// Simplified: add at end
// We'll need to insert after [global] but for simplicity, append
}
if (public_key_found) {
free(lines[public_key_line]);
char new_line[MAX_LINE_LEN];
snprintf(new_line, sizeof(new_line), "my_public_key=%s\n", public_key_hex);
lines[public_key_line] = strdup(new_line);
}
// Write back to file
fp = fopen(filename, "w");
if (!fp) {
// Cleanup
for (size_t i = 0; i < line_count; i++) free(lines[i]);
free(lines);
return -1;
}
for (size_t i = 0; i < line_count; i++) {
fputs(lines[i], fp);
free(lines[i]);
}
// Add missing keys
if (!private_key_found) {
fprintf(fp, "my_private_key=%s\n", private_key_hex);
}
if (!public_key_found) {
fprintf(fp, "my_public_key=%s\n", public_key_hex);
}
free(lines);
fclose(fp);
return 0;
}
// Parse new format configuration file
utun_config_t* parse_config_v2(const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) {
fprintf(stderr, "Failed to open config file: %s\n", filename);
return NULL;
}
// Allocate config structure
utun_config_t *config = calloc(1, sizeof(utun_config_t));
if (!config) {
fclose(fp);
return NULL;
}
// Initialize arrays
config->server_capacity = INITIAL_SERVER_CAPACITY;
config->servers = malloc(config->server_capacity * sizeof(server_config_t));
if (!config->servers) {
free(config);
fclose(fp);
return NULL;
}
config->client_capacity = INITIAL_CLIENT_CAPACITY;
config->clients = malloc(config->client_capacity * sizeof(client_config_t));
if (!config->clients) {
free(config->servers);
free(config);
fclose(fp);
return NULL;
}
config->connection_v2_capacity = INITIAL_CONNECTION_V2_CAPACITY;
config->connections_v2 = malloc(config->connection_v2_capacity * sizeof(connection_config_v2_t));
if (!config->connections_v2) {
free(config->clients);
free(config->servers);
free(config);
fclose(fp);
return NULL;
}
char line[MAX_LINE_LEN];
char current_section[128] = "";
char current_server_name[MAX_CONN_NAME_LEN] = "";
char current_client_name[MAX_CONN_NAME_LEN] = "";
server_config_t current_server = {0};
client_config_t current_client = {0};
connection_config_v2_t current_conn_v2 = {0};
int in_server_section = 0;
int in_client_section = 0;
int in_connection_section = 0;
while (fgets(line, sizeof(line), fp)) {
// Remove newline
line[strcspn(line, "\n")] = '\0';
char *trimmed = trim(line);
// Skip empty lines and comments
if (strlen(trimmed) == 0 || trimmed[0] == ';' || trimmed[0] == '#') {
continue;
}
// Check for section header
if (trimmed[0] == '[' && trimmed[strlen(trimmed) - 1] == ']') {
// End previous sections if any
if (in_server_section) {
if (strlen(current_server.name) > 0) {
if (add_server(config, &current_server) != 0) {
fprintf(stderr, "Failed to add server: %s\n", current_server.name);
}
}
memset(&current_server, 0, sizeof(current_server));
in_server_section = 0;
}
if (in_client_section) {
if (strlen(current_client.name) > 0) {
if (add_client(config, &current_client) != 0) {
fprintf(stderr, "Failed to add client: %s\n", current_client.name);
}
}
memset(&current_client, 0, sizeof(current_client));
in_client_section = 0;
}
if (in_connection_section) {
if (current_conn_v2.route_count > 0) {
if (add_connection_v2(config, &current_conn_v2) != 0) {
fprintf(stderr, "Failed to add connection v2\n");
}
}
memset(&current_conn_v2, 0, sizeof(current_conn_v2));
in_connection_section = 0;
}
// Extract section name
strncpy(current_section, trimmed + 1, sizeof(current_section) - 1);
current_section[sizeof(current_section) - 1] = '\0';
current_section[strcspn(current_section, "]")] = '\0';
trim(current_section);
// Check section type
if (strncmp(current_section, "server:", 7) == 0) {
in_server_section = 1;
char *name = trim(current_section + 7);
if (strlen(name) > 0) {
strncpy(current_server.name, name, sizeof(current_server.name) - 1);
strncpy(current_server_name, name, sizeof(current_server_name) - 1);
}
} else if (strncmp(current_section, "client:", 7) == 0) {
in_client_section = 1;
char *name = trim(current_section + 7);
if (strlen(name) > 0) {
strncpy(current_client.name, name, sizeof(current_client.name) - 1);
strncpy(current_client_name, name, sizeof(current_client_name) - 1);
}
} else if (strcasecmp(current_section, "connection") == 0) {
in_connection_section = 1;
} else if (strcasecmp(current_section, "global") == 0) {
// Already handled by current_section
} else if (strcasecmp(current_section, "routing") == 0) {
// Already handled by current_section
} else {
fprintf(stderr, "Unknown section: %s\n", current_section);
}
continue;
}
// Parse key-value pair
char key[256], value[256];
if (parse_key_value(trimmed, key, sizeof(key), value, sizeof(value)) != 0) {
fprintf(stderr, "Invalid key-value line: %s\n", trimmed);
continue;
}
// Process based on current section
if (strcasecmp(current_section, "global") == 0) {
if (strcasecmp(key, "my_private_key") == 0) {
strncpy(config->global.my_private_key_hex, value, sizeof(config->global.my_private_key_hex) - 1);
} else if (strcasecmp(key, "my_public_key") == 0) {
strncpy(config->global.my_public_key_hex, value, sizeof(config->global.my_public_key_hex) - 1);
} else if (strcasecmp(key, "option") == 0) {
strncpy(config->global.option_value, value, sizeof(config->global.option_value) - 1);
} else if (strcasecmp(key, "control_ip") == 0) {
strncpy(config->global.control_ip, value, sizeof(config->global.control_ip) - 1);
} else if (strcasecmp(key, "control_port") == 0) {
config->global.control_port = atoi(value);
} else if (strcasecmp(key, "net_debug") == 0) {
config->global.net_debug = atoi(value);
} else if (strcasecmp(key, "tun_ip") == 0) {
strncpy(config->global.tun_ip, value, sizeof(config->global.tun_ip) - 1);
}
} else if (strcasecmp(current_section, "routing") == 0) {
if (strcasecmp(key, "allowed_subnet") == 0) {
if (config->allowed_subnet_count < MAX_ALLOWED_SUBNETS) {
strncpy(config->allowed_subnets[config->allowed_subnet_count].subnet, value, sizeof(config->allowed_subnets[0].subnet) - 1);
config->allowed_subnet_count++;
} else {
fprintf(stderr, "Too many allowed subnets, maximum is %d\n", MAX_ALLOWED_SUBNETS);
}
}
} else if (in_server_section) {
if (strcasecmp(key, "addr") == 0) {
strncpy(current_server.addr, value, sizeof(current_server.addr) - 1);
} else if (strcasecmp(key, "so_mark") == 0) {
current_server.so_mark = atoi(value);
} else if (strcasecmp(key, "netif") == 0) {
strncpy(current_server.netif, value, sizeof(current_server.netif) - 1);
}
} else if (in_client_section) {
if (strcasecmp(key, "from") == 0) {
strncpy(current_client.from, value, sizeof(current_client.from) - 1);
} else if (strcasecmp(key, "to_addr") == 0) {
strncpy(current_client.to_addr, value, sizeof(current_client.to_addr) - 1);
}
} else if (in_connection_section) {
if (strcasecmp(key, "link") == 0) {
// Parse comma-separated route pairs: server1:client1,server2:client2
char *token = strtok(value, ",");
while (token != NULL) {
char *pair = trim(token);
char *colon = strchr(pair, ':');
if (colon) {
route_pair_t route = {0};
size_t server_len = colon - pair;
if (server_len >= sizeof(route.server_name)) server_len = sizeof(route.server_name) - 1;
strncpy(route.server_name, pair, server_len);
route.server_name[server_len] = '\0';
trim(route.server_name);
char *client_name = colon + 1;
strncpy(route.client_name, client_name, sizeof(route.client_name) - 1);
trim(route.client_name);
if (add_route_to_connection(&current_conn_v2, &route) != 0) {
fprintf(stderr, "Failed to add route: %s:%s\n", route.server_name, route.client_name);
}
} else {
fprintf(stderr, "Invalid route pair format: %s, expected 'server:client'\n", pair);
}
token = strtok(NULL, ",");
}
} else if (strcasecmp(key, "keepalive") == 0) {
current_conn_v2.keepalive = atoi(value);
} else if (strcasecmp(key, "peer_public_key") == 0) {
strncpy(current_conn_v2.peer_public_key_hex, value, sizeof(current_conn_v2.peer_public_key_hex) - 1);
}
}
}
// Add last sections if any
if (in_server_section && strlen(current_server.name) > 0) {
if (add_server(config, &current_server) != 0) {
fprintf(stderr, "Failed to add server: %s\n", current_server.name);
}
}
if (in_client_section && strlen(current_client.name) > 0) {
if (add_client(config, &current_client) != 0) {
fprintf(stderr, "Failed to add client: %s\n", current_client.name);
}
}
if (in_connection_section && current_conn_v2.route_count > 0) {
if (add_connection_v2(config, &current_conn_v2) != 0) {
fprintf(stderr, "Failed to add connection v2\n");
}
}
fclose(fp);
// Validate references
for (int i = 0; i < config->connection_v2_count; i++) {
connection_config_v2_t *conn = &config->connections_v2[i];
for (int j = 0; j < conn->route_count; j++) {
route_pair_t *route = &conn->routes[j];
// Check server exists
int server_found = 0;
for (int k = 0; k < config->server_count; k++) {
if (strcmp(config->servers[k].name, route->server_name) == 0) {
server_found = 1;
break;
}
}
if (!server_found) {
fprintf(stderr, "Warning: server '%s' referenced in route not found\n", route->server_name);
}
// Check client exists
int client_found = 0;
for (int k = 0; k < config->client_count; k++) {
if (strcmp(config->clients[k].name, route->client_name) == 0) {
client_found = 1;
break;
}
}
if (!client_found) {
fprintf(stderr, "Warning: client '%s' referenced in route not found\n", route->client_name);
}
}
}
return config;
}
-496
View File
@@ -1,496 +0,0 @@
// config_parser.c - Configuration parser for utun application
#define _POSIX_C_SOURCE 200809L
#include "config_parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>
#define MAX_LINE_LEN 1024
#define INITIAL_CONNECTION_CAPACITY 4
#define INITIAL_SERVER_CAPACITY 4
#define INITIAL_CLIENT_CAPACITY 4
#define INITIAL_CONNECTION_V2_CAPACITY 4
// Helper function to trim whitespace
static char* trim(char *str) {
if (!str) return NULL;
// Trim leading spaces
while (isspace((unsigned char)*str)) str++;
// Trim trailing spaces
char *end = str + strlen(str) - 1;
while (end > str && isspace((unsigned char)*end)) end--;
*(end + 1) = '\0';
return str;
}
// Parse a key-value pair
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;
// Extract key
size_t key_size = equal - line;
if (key_size >= key_len) return -1;
strncpy(key, line, key_size);
key[key_size] = '\0';
trim(key);
// Extract value
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;
}
// Parse connection mode string
static config_conn_mode_t parse_mode(const char *mode_str) {
if (!mode_str) return CONFIG_MODE_UNKNOWN;
if (strcasecmp(mode_str, "client") == 0) {
return CONFIG_MODE_CLIENT;
} else if (strcasecmp(mode_str, "server") == 0) {
return CONFIG_MODE_SERVER;
}
return CONFIG_MODE_UNKNOWN;
}
// Add a connection to the config
static int add_connection(utun_config_t *config, const connection_config_t *conn) {
if (config->connection_count >= config->connection_capacity) {
// Resize array
int new_capacity = config->connection_capacity * 2;
connection_config_t *new_connections = realloc(config->connections,
new_capacity * sizeof(connection_config_t));
if (!new_connections) return -1;
config->connections = new_connections;
config->connection_capacity = new_capacity;
}
// Copy connection data
memcpy(&config->connections[config->connection_count], conn, sizeof(connection_config_t));
config->connection_count++;
return 0;
}
// Parse configuration file
utun_config_t* parse_config(const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) {
fprintf(stderr, "Failed to open config file: %s\n", filename);
return NULL;
}
// Allocate config structure
utun_config_t *config = calloc(1, sizeof(utun_config_t));
if (!config) {
fclose(fp);
return NULL;
}
// Initialize connection array
config->connection_capacity = INITIAL_CONNECTION_CAPACITY;
config->connections = malloc(config->connection_capacity * sizeof(connection_config_t));
if (!config->connections) {
free(config);
fclose(fp);
return NULL;
}
char line[MAX_LINE_LEN];
char current_section[128] = "";
connection_config_t current_conn = {0};
int in_connection_section = 0;
while (fgets(line, sizeof(line), fp)) {
// Remove newline
line[strcspn(line, "\n")] = '\0';
char *trimmed = trim(line);
// Skip empty lines and comments
if (strlen(trimmed) == 0 || trimmed[0] == ';' || trimmed[0] == '#') {
continue;
}
// Check for section header
if (trimmed[0] == '[' && trimmed[strlen(trimmed) - 1] == ']') {
// End previous connection section if any
if (in_connection_section) {
if (strlen(current_conn.name) > 0) {
if (add_connection(config, &current_conn) != 0) {
fprintf(stderr, "Failed to add connection: %s\n", current_conn.name);
}
}
memset(&current_conn, 0, sizeof(current_conn));
in_connection_section = 0;
}
// Extract section name
strncpy(current_section, trimmed + 1, sizeof(current_section) - 1);
current_section[sizeof(current_section) - 1] = '\0';
current_section[strcspn(current_section, "]")] = '\0';
trim(current_section);
// Check if it's a connection section
if (strncmp(current_section, "connection:", 11) == 0) {
in_connection_section = 1;
// Extract connection name
char *name = trim(current_section + 11);
if (strlen(name) > 0) {
strncpy(current_conn.name, name, sizeof(current_conn.name) - 1);
}
}
continue;
}
// Parse key-value pair
char key[256], value[256];
if (parse_key_value(trimmed, key, sizeof(key), value, sizeof(value)) != 0) {
fprintf(stderr, "Invalid key-value line: %s\n", trimmed);
continue;
}
// Process based on current section
if (strcasecmp(current_section, "global") == 0) {
if (strcasecmp(key, "my_private_key") == 0) {
strncpy(config->global.my_private_key_hex, value, sizeof(config->global.my_private_key_hex) - 1);
} else if (strcasecmp(key, "my_public_key") == 0) {
strncpy(config->global.my_public_key_hex, value, sizeof(config->global.my_public_key_hex) - 1);
} else if (strcasecmp(key, "option") == 0) {
strncpy(config->global.option_value, value, sizeof(config->global.option_value) - 1);
} else if (strcasecmp(key, "control_ip") == 0) {
strncpy(config->global.control_ip, value, sizeof(config->global.control_ip) - 1);
} else if (strcasecmp(key, "control_port") == 0) {
config->global.control_port = atoi(value);
} else if (strcasecmp(key, "net_debug") == 0) {
config->global.net_debug = atoi(value);
}
} else if (strcasecmp(current_section, "routing") == 0) {
if (strcasecmp(key, "allowed_subnet") == 0) {
if (config->allowed_subnet_count < MAX_ALLOWED_SUBNETS) {
strncpy(config->allowed_subnets[config->allowed_subnet_count].subnet, value, sizeof(config->allowed_subnets[0].subnet) - 1);
config->allowed_subnet_count++;
} else {
fprintf(stderr, "Too many allowed subnets, maximum is %d\n", MAX_ALLOWED_SUBNETS);
}
}
} else if (in_connection_section) {
if (strcasecmp(key, "mode") == 0) {
current_conn.mode = parse_mode(value);
} else if (strcasecmp(key, "addr") == 0) {
// For server mode
if (current_conn.mode == CONFIG_MODE_SERVER) {
strncpy(current_conn.local_addr, value, sizeof(current_conn.local_addr) - 1);
}
} else if (strcasecmp(key, "from_addr") == 0) {
// For client mode
if (current_conn.mode == CONFIG_MODE_CLIENT) {
strncpy(current_conn.local_addr, value, sizeof(current_conn.local_addr) - 1);
}
} else if (strcasecmp(key, "to_addr") == 0) {
// For client mode
if (current_conn.mode == CONFIG_MODE_CLIENT) {
strncpy(current_conn.remote_addr, value, sizeof(current_conn.remote_addr) - 1);
}
} else if (strcasecmp(key, "peer_public_key") == 0) {
strncpy(current_conn.peer_public_key_hex, value, sizeof(current_conn.peer_public_key_hex) - 1);
} else if (strcasecmp(key, "so_mark") == 0) {
current_conn.so_mark = atoi(value);
} else if (strcasecmp(key, "netif") == 0) {
strncpy(current_conn.netif, value, sizeof(current_conn.netif) - 1);
} else if (strcasecmp(key, "tun") == 0) {
strncpy(current_conn.tun_ifname, value, sizeof(current_conn.tun_ifname) - 1);
} else if (strcasecmp(key, "tun_ip") == 0) {
strncpy(current_conn.tun_ip, value, sizeof(current_conn.tun_ip) - 1);
}
}
}
// Add last connection if any
if (in_connection_section && strlen(current_conn.name) > 0) {
if (add_connection(config, &current_conn) != 0) {
fprintf(stderr, "Failed to add connection: %s\n", current_conn.name);
}
}
fclose(fp);
return config;
}
// Free configuration structure
void free_config(utun_config_t *config) {
if (!config) return;
if (config->connections) {
free(config->connections);
}
free(config);
}
// Print configuration for debugging
void print_config(const utun_config_t *config) {
if (!config) {
printf("Configuration is NULL\n");
return;
}
printf("Global configuration:\n");
printf(" my_private_key: %s\n", config->global.my_private_key_hex);
printf(" my_public_key: %s\n", config->global.my_public_key_hex);
printf(" option: %s\n", config->global.option_value);
printf(" control_ip: %s\n", config->global.control_ip);
printf(" control_port: %u\n", config->global.control_port);
printf(" net_debug: %d\n", config->global.net_debug);
printf("\n");
printf("Allowed subnets (%d):\n", config->allowed_subnet_count);
for (int i = 0; i < config->allowed_subnet_count; i++) {
printf(" %s\n", config->allowed_subnets[i].subnet);
}
printf("\n");
printf("Connections (%d):\n", config->connection_count);
for (int i = 0; i < config->connection_count; i++) {
const connection_config_t *conn = &config->connections[i];
printf(" [%d] %s:\n", i, conn->name);
printf(" mode: %s\n",
conn->mode == CONFIG_MODE_CLIENT ? "client" :
conn->mode == CONFIG_MODE_SERVER ? "server" : "unknown");
printf(" local_addr: %s\n", conn->local_addr);
printf(" remote_addr: %s\n", conn->remote_addr);
printf(" peer_public_key: %s\n", conn->peer_public_key_hex);
printf(" so_mark: %d\n", conn->so_mark);
printf(" netif: %s\n", conn->netif);
printf(" tun: %s\n", conn->tun_ifname);
printf(" tun_ip: %s\n", conn->tun_ip);
}
}
// Update keys in configuration file
int update_config_keys(const char *filename,
const char *private_key_hex,
const char *public_key_hex) {
if (!filename || !private_key_hex || !public_key_hex) {
errno = EINVAL;
return -1;
}
// Read entire file
FILE *fp = fopen(filename, "r");
if (!fp) {
// Try to create new file
fp = fopen(filename, "w");
if (!fp) return -1;
fprintf(fp, "[global]\n");
fprintf(fp, "my_private_key=%s\n", private_key_hex);
fprintf(fp, "my_public_key=%s\n", public_key_hex);
fclose(fp);
return 0;
}
// Add a server to the config
static int add_server(utun_config_t *config, const server_config_t *server) {
if (config->server_count >= config->server_capacity) {
// Resize array
int new_capacity = config->server_capacity * 2;
server_config_t *new_servers = realloc(config->servers,
new_capacity * sizeof(server_config_t));
if (!new_servers) return -1;
config->servers = new_servers;
config->server_capacity = new_capacity;
}
// Copy server data
memcpy(&config->servers[config->server_count], server, sizeof(server_config_t));
config->server_count++;
return 0;
}
// Add a client to the config
static int add_client(utun_config_t *config, const client_config_t *client) {
if (config->client_count >= config->client_capacity) {
// Resize array
int new_capacity = config->client_capacity * 2;
client_config_t *new_clients = realloc(config->clients,
new_capacity * sizeof(client_config_t));
if (!new_clients) return -1;
config->clients = new_clients;
config->client_capacity = new_capacity;
}
// Copy client data
memcpy(&config->clients[config->client_count], client, sizeof(client_config_t));
config->client_count++;
return 0;
}
// Add a route to connection v2
static int add_route_to_connection(connection_config_v2_t *conn_v2, const route_pair_t *route) {
if (conn_v2->route_count >= conn_v2->route_capacity) {
// Resize array
int new_capacity = conn_v2->route_capacity * 2;
route_pair_t *new_routes = realloc(conn_v2->routes,
new_capacity * sizeof(route_pair_t));
if (!new_routes) return -1;
conn_v2->routes = new_routes;
conn_v2->route_capacity = new_capacity;
}
// Copy route data
memcpy(&conn_v2->routes[conn_v2->route_count], route, sizeof(route_pair_t));
conn_v2->route_count++;
return 0;
}
// Add a connection v2 to the config
static int add_connection_v2(utun_config_t *config, const connection_config_v2_t *conn_v2) {
if (config->connection_v2_count >= config->connection_v2_capacity) {
// Resize array
int new_capacity = config->connection_v2_capacity * 2;
connection_config_v2_t *new_connections = realloc(config->connections_v2,
new_capacity * sizeof(connection_config_v2_t));
if (!new_connections) return -1;
config->connections_v2 = new_connections;
config->connection_v2_capacity = new_capacity;
}
// Copy connection data
memcpy(&config->connections_v2[config->connection_v2_count], conn_v2, sizeof(connection_config_v2_t));
config->connection_v2_count++;
return 0;
}
// Read all lines
char **lines = NULL;
size_t line_count = 0;
size_t capacity = 0;
char line[MAX_LINE_LEN];
int in_global_section = 0;
int private_key_found = 0;
int public_key_found = 0;
size_t private_key_line = 0;
size_t public_key_line = 0;
while (fgets(line, sizeof(line), fp)) {
// Add line to array
if (line_count >= capacity) {
size_t new_capacity = capacity ? capacity * 2 : 16;
char **new_lines = realloc(lines, new_capacity * sizeof(char *));
if (!new_lines) {
// Cleanup
for (size_t i = 0; i < line_count; i++) free(lines[i]);
free(lines);
fclose(fp);
return -1;
}
lines = new_lines;
capacity = new_capacity;
}
lines[line_count] = strdup(line);
if (!lines[line_count]) {
// Cleanup
for (size_t i = 0; i < line_count; i++) free(lines[i]);
free(lines);
fclose(fp);
return -1;
}
char *trimmed = trim(line);
// Track global section
if (trimmed[0] == '[' && trimmed[strlen(trimmed) - 1] == ']') {
char section[128];
strncpy(section, trimmed + 1, sizeof(section) - 1);
section[sizeof(section) - 1] = '\0';
section[strcspn(section, "]")] = '\0';
trim(section);
in_global_section = (strcasecmp(section, "global") == 0);
}
// Check for keys in global section
if (in_global_section) {
char key[256], value[256];
if (parse_key_value(trimmed, key, sizeof(key), value, sizeof(value)) == 0) {
if (strcasecmp(key, "my_private_key") == 0) {
private_key_found = 1;
private_key_line = line_count;
} else if (strcasecmp(key, "my_public_key") == 0) {
public_key_found = 1;
public_key_line = line_count;
}
}
}
line_count++;
}
fclose(fp);
// Update or add keys
if (private_key_found) {
free(lines[private_key_line]);
char new_line[MAX_LINE_LEN];
snprintf(new_line, sizeof(new_line), "my_private_key=%s\n", private_key_hex);
lines[private_key_line] = strdup(new_line);
} else {
// Add after [global] line or at beginning
// Simplified: add at end
// We'll need to insert after [global] but for simplicity, append
}
if (public_key_found) {
free(lines[public_key_line]);
char new_line[MAX_LINE_LEN];
snprintf(new_line, sizeof(new_line), "my_public_key=%s\n", public_key_hex);
lines[public_key_line] = strdup(new_line);
}
// Write back to file
fp = fopen(filename, "w");
if (!fp) {
// Cleanup
for (size_t i = 0; i < line_count; i++) free(lines[i]);
free(lines);
return -1;
}
for (size_t i = 0; i < line_count; i++) {
fputs(lines[i], fp);
free(lines[i]);
}
// Add missing keys
if (!private_key_found) {
fprintf(fp, "my_private_key=%s\n", private_key_hex);
}
if (!public_key_found) {
fprintf(fp, "my_public_key=%s\n", public_key_hex);
}
free(lines);
fclose(fp);
return 0;
}
+13 -86
View File
@@ -9,45 +9,36 @@
extern "C" {
#endif
// Maximum lengths for strings in config
#define MAX_CONN_NAME_LEN 64
#define MAX_KEY_LEN 256 // HEX key length (64 bytes * 2 chars + null)
#define MAX_KEY_LEN 256
#define MAX_ADDR_LEN 64
#define MAX_NETIF_LEN 16
#define MAX_OPTION_VALUE_LEN 256
#define MAX_ALLOWED_SUBNETS 32
// Subnet entry for allowed routing
typedef struct {
char subnet[MAX_ADDR_LEN]; // Format: "192.168.0.0/24"
char subnet[MAX_ADDR_LEN];
} subnet_entry_t;
// Server configuration (new format)
typedef struct {
char name[MAX_CONN_NAME_LEN];
char addr[MAX_ADDR_LEN];
int so_mark;
char netif[MAX_NETIF_LEN];
char type[16]; // public/nat/private - parsed but not currently used
} server_config_t;
// Client configuration (new format)
typedef struct {
char name[MAX_CONN_NAME_LEN];
char from[MAX_CONN_NAME_LEN]; // server name
char from[MAX_CONN_NAME_LEN];
char to_addr[MAX_ADDR_LEN];
} client_config_t;
// Route pair (server:client)
typedef struct {
char server_name[MAX_CONN_NAME_LEN];
char client_name[MAX_CONN_NAME_LEN];
} route_pair_t;
// Connection configuration v2 (new format)
typedef struct {
route_pair_t *routes;
int route_count;
@@ -56,102 +47,38 @@ typedef struct {
int keepalive;
} connection_config_v2_t;
// Global configuration
typedef struct {
char my_private_key_hex[MAX_KEY_LEN]; // Our private key in HEX
char my_public_key_hex[MAX_KEY_LEN]; // Our public key in HEX
char option_value[MAX_OPTION_VALUE_LEN]; // Global option value
char control_ip[MAX_ADDR_LEN]; // Control socket IP (empty for disabled)
uint16_t control_port; // Control socket port
int net_debug; // Network debug mode: 1 = enable network emulator
char tun_ip[MAX_ADDR_LEN]; // TUN interface IP address with prefix (e.g., 10.0.0.1/24)
char my_private_key_hex[MAX_KEY_LEN];
char my_public_key_hex[MAX_KEY_LEN];
char tun_ip[MAX_ADDR_LEN];
int mtu;
char control_ip[MAX_ADDR_LEN];
uint16_t control_port;
int net_debug;
} global_config_t;
// Complete configuration
typedef struct {
global_config_t global;
// New format configurations
server_config_t *servers;
int server_count;
int server_capacity;
client_config_t *clients;
int client_count;
int client_capacity;
connection_config_v2_t *connections_v2;
int connection_v2_count;
int connection_v2_capacity;
subnet_entry_t allowed_subnets[MAX_ALLOWED_SUBNETS];
int allowed_subnet_count;
} utun_config_t;
/**
* @brief Parse configuration file (new format only)
* @param filename Path to configuration file
* @return Pointer to parsed configuration, NULL on error
*/
utun_config_t* parse_config(const char *filename);
/**
* @brief Parse new format configuration file
* @param filename Path to configuration file
* @return Pointer to parsed configuration, NULL on error
*
* Parses new format configuration file:
* [global]
* option=value
* my_private_key=HEX
* my_public_key=HEX
* tun_ip=1.2.3.4
*
* [server: wired1_fast]
* addr=192.168.0.10:1234
* so_mark=100
* netif=lo0
*
* [client: wireless]
* from=wired1_fast
* to_addr=192.168.0.20:1234
*
* [connection]
* link=wired1_fast:wireless
* keepalive=1
* peer_public_key=HEX
*/
utun_config_t* parse_config_v2(const char *filename);
/**
* @brief Free configuration structure
* @param config Configuration to free
*/
void free_config(utun_config_t *config);
/**
* @brief Print configuration for debugging
* @param config Configuration to print
*/
void print_config(const utun_config_t *config);
/**
* @brief Update keys in configuration file
* @param filename Path to configuration file
* @param private_key_hex Private key in HEX format
* @param public_key_hex Public key in HEX format
* @return 0 on success, -1 on error
*
* Updates or adds my_private_key and my_public_key lines in [global] section
*/
int update_config_keys(const char *filename,
const char *private_key_hex,
const char *public_key_hex);
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key);
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_PARSER_H */
#endif
-1106
View File
File diff suppressed because it is too large. Load diff
-170
View File
@@ -1,170 +0,0 @@
// connection.h - Минималистичный API для защищенных UDP подключений
#ifndef CONNECTION_H
#define CONNECTION_H
#include <stdint.h>
#include <stddef.h>
#include "config_parser.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Непрозрачный дескриптор подключения */
typedef struct conn_handle conn_handle_t;
/* Forward declaration для uasync */
typedef struct uasync_s uasync_t;
/* Forward declaration для очередей */
typedef struct ll_queue ll_queue_t;
/* Режим подключения */
typedef enum {
CONN_MODE_CLIENT, /* Инициируем подключение к указанному удаленному адресу */
CONN_MODE_SERVER /* Ожидаем входящие подключения */
} conn_mode_t;
/* Callback для входящих данных */
typedef void (*conn_recv_callback_t)(conn_handle_t* conn,
const uint8_t* data,
size_t len,
void* user_data);
/*
* Создание дескриптора подключения (только выделение памяти).
* ua - экземпляр uasync для таймеров (обязательный параметр)
* Возвращает NULL при ошибке.
*/
conn_handle_t* conn_create(uasync_t* ua);
/*
* Установка криптографических ключей.
* Должна быть вызвана до conn_connect().
*
* @param conn Дескриптор подключения
* @param my_pub_key Публичный ключ (64 байта для secp256r1), NULL для авто-генерации
* @param my_priv_key Приватный ключ (32 байта), NULL для авто-генерации
* @param peer_pub_key Публичный ключ пира (64 байта), NULL для сервера (получит из первого пакета)
*
* @return 0 при успехе, -1 при ошибке
*/
int conn_set_keys(conn_handle_t* conn,
const uint8_t* my_pub_key,
const uint8_t* my_priv_key,
const uint8_t* peer_pub_key);
/*
* Подключение к удаленному узлу или начало ожидания входящих подключений.
*
* @param conn Дескриптор подключения
* @param local_ip Локальный IP для bind (NULL для "0.0.0.0")
* @param local_port Локальный порт (0 для авто-выбора)
* @param remote_ip Удаленный IP (NULL для серверного режима)
* @param remote_port Удаленный порт (игнорируется если remote_ip NULL)
* @param mode Режим подключения (CONN_MODE_CLIENT/SERVER)
*
* @return 0 при успехе, -1 при ошибке
*/
int conn_connect(conn_handle_t* conn,
const char* local_ip,
uint16_t local_port,
const char* remote_ip,
uint16_t remote_port,
conn_mode_t mode);
/*
* Установка callback'а для входящих данных.
* Callback будет вызываться при получении полных собранных пакетов.
*/
void conn_set_recv_callback(conn_handle_t* conn,
conn_recv_callback_t callback,
void* user_data);
/*
* Отправка данных.
* Данные будут автоматически фрагментированы, зашифрованы и отправлены.
*
* @return 0 при успехе, -1 при ошибке
*/
int conn_send(conn_handle_t* conn, const uint8_t* data, size_t len);
/*
* Закрытие подключения (немедленное, без протокола завершения).
* После вызова дескриптор можно уничтожить через conn_destroy().
*/
void conn_close(conn_handle_t* conn);
/*
* Сброс соединения с использованием служебных пакетов.
* Отправляет пакет сброса через ETCP и сбрасывает состояние всех компонентов.
* Предназначено для тестирования и восстановления после сбоев.
*/
void conn_reset(conn_handle_t* conn);
/*
* Полное уничтожение дескриптора подключения и освобождение всех ресурсов.
* Автоматически вызывает conn_close() если подключение активно.
*/
void conn_destroy(conn_handle_t* conn);
/*
* Получение статистики подключения (опционально).
*
* @return 0 при успехе, -1 при ошибке
*/
typedef struct {
uint64_t bytes_sent;
uint64_t bytes_received;
uint32_t packets_sent;
uint32_t packets_received;
uint32_t retransmissions;
uint32_t fragments_assembled;
uint16_t current_rtt_ms; /* Текущее RTT в миллисекундах */
uint16_t jitter_ms; /* Джиттер в миллисекундах */
uint32_t encryption_errors; /* Ошибки шифрования */
uint32_t decryption_errors; /* Ошибки расшифрования */
uint32_t crc_errors; /* Ошибки CRC32 */
} conn_stats_t;
int conn_get_stats(conn_handle_t* conn, conn_stats_t* stats);
/**
* @brief Получить очередь отправки (данные от приложения для передачи)
* @param conn Дескриптор подключения
* @return Указатель на очередь отправки или NULL при ошибке
*
* Очередь отправки (app_input_queue) содержит данные от приложения,
* которые ожидают обработки packer'ом и отправки через ETCP.
* Используется для контроля потока и pacing.
*/
ll_queue_t* conn_get_output_queue(conn_handle_t* conn);
/**
* @brief Получить очередь приема (собранные данные для приложения)
* @param conn Дескриптор подключения
* @return Указатель на очередь приема или NULL при ошибке
*
* Очередь приема (app_output_queue) содержит собранные пакеты,
* готовые для передачи приложению через recv_callback.
*/
ll_queue_t* conn_get_input_queue(conn_handle_t* conn);
/**
* @brief Initialize connection from v2 configuration
* @param conn Connection handle (must be created with conn_create)
* @param config Parsed configuration (must contain at least one connection_v2)
* @param conn_idx Index of connection_v2 in config (usually 0)
* @return 0 on success, -1 on error
*
* Sets up multiple routes based on server:client pairs, creates sockets with
* options (SO_MARK, SO_BINDTODEVICE), sets cryptographic keys, and prepares
* connection for data transmission using first route as active.
*/
int conn_init_v2(conn_handle_t* conn, const utun_config_t* config, int conn_idx);
#ifdef __cplusplus
}
#endif
#endif /* CONNECTION_H */
-457
View File
@@ -1,457 +0,0 @@
/**
* @file control_socket.c
* @brief Control socket implementation for statistics and diagnostics
*/
#include "control_socket.h"
#include "connection.h"
#include "etcp.h"
#include "pkt_normalizer.h"
#include "routing.h"
#include "tun_if.h"
#include "config_parser.h"
#include "utun_state.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/time.h>
// Internal control socket structure
struct control_socket {
int sockfd;
struct sockaddr_in addr;
uint16_t port;
void *app_state; // Pointer to utun_state_t
uint16_t sequence_counter;
// Statistics counters (for cumulative stats)
uint64_t total_requests;
uint64_t total_errors;
// Timestamps
struct timeval start_time;
};
// Forward declarations
static int collect_statistics(control_socket_t *cs, control_stats_packet_t *stats);
static int collect_system_status(control_socket_t *cs, system_status_t *status);
static void get_etcp_stats(epkt_t *etcp, etcp_stats_t *etcp_stats);
static void get_pkt_normalizer_stats(pkt_normalizer_pair *pair, pkt_normalizer_stats_t *pn_stats);
static int handle_stats_request(control_socket_t *cs, struct sockaddr_in *client, uint16_t sequence);
static int handle_status_request(control_socket_t *cs, struct sockaddr_in *client, uint16_t sequence);
static int handle_reset_request(control_socket_t *cs, struct sockaddr_in *client, uint16_t sequence);
static int send_error_response(control_socket_t *cs, struct sockaddr_in *client, uint16_t sequence, control_err_t error);
static int send_response(control_socket_t *cs, struct sockaddr_in *client, void *data, size_t len);
// Create UDP control socket
control_socket_t* control_socket_create(const char *ip, uint16_t port) {
control_socket_t *cs = calloc(1, sizeof(control_socket_t));
if (!cs) {
return NULL;
}
// Create UDP socket
cs->sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (cs->sockfd < 0) {
perror("control_socket: socket");
free(cs);
return NULL;
}
// Set non-blocking
int flags = fcntl(cs->sockfd, F_GETFL, 0);
if (flags < 0) {
perror("control_socket: fcntl GETFL");
close(cs->sockfd);
free(cs);
return NULL;
}
if (fcntl(cs->sockfd, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("control_socket: fcntl SETFL");
close(cs->sockfd);
free(cs);
return NULL;
}
// Enable address reuse
int opt = 1;
if (setsockopt(cs->sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
perror("control_socket: setsockopt SO_REUSEADDR");
// Continue anyway
}
// Bind to address
memset(&cs->addr, 0, sizeof(cs->addr));
cs->addr.sin_family = AF_INET;
cs->addr.sin_port = htons(port);
if (ip && ip[0] != '\0') {
if (inet_pton(AF_INET, ip, &cs->addr.sin_addr) != 1) {
fprintf(stderr, "control_socket: invalid IP address: %s\n", ip);
close(cs->sockfd);
free(cs);
return NULL;
}
} else {
cs->addr.sin_addr.s_addr = INADDR_ANY;
}
if (bind(cs->sockfd, (struct sockaddr*)&cs->addr, sizeof(cs->addr)) < 0) {
perror("control_socket: bind");
close(cs->sockfd);
free(cs);
return NULL;
}
cs->port = port;
cs->sequence_counter = 0;
cs->total_requests = 0;
cs->total_errors = 0;
gettimeofday(&cs->start_time, NULL);
printf("[CONTROL] Control socket listening on %s:%u\n",
ip ? ip : "0.0.0.0", port);
return cs;
}
// Destroy control socket
void control_socket_destroy(control_socket_t *cs) {
if (!cs) return;
if (cs->sockfd >= 0) {
close(cs->sockfd);
}
free(cs);
}
// Get socket file descriptor for polling
int control_socket_get_fd(const control_socket_t *cs) {
return cs ? cs->sockfd : -1;
}
// Set application state pointer
void control_socket_set_state(control_socket_t *cs, void *state) {
if (cs) {
cs->app_state = state;
}
}
// Process incoming requests
int control_socket_process(control_socket_t *cs, void *state) {
if (!cs || !state) return -1;
// Use provided state or stored state
if (state) {
cs->app_state = state;
}
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
control_request_packet_t request;
// Receive request
ssize_t n = recvfrom(cs->sockfd, &request, sizeof(request), 0,
(struct sockaddr*)&client_addr, &addr_len);
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return 0; // No data available
}
perror("control_socket: recvfrom");
return -1;
}
if (n != sizeof(request)) {
fprintf(stderr, "control_socket: invalid request size: %zd\n", n);
return 0;
}
cs->total_requests++;
// Handle request based on command
switch (request.command) {
case CONTROL_CMD_GET_STATS:
return handle_stats_request(cs, &client_addr, request.sequence);
case CONTROL_CMD_GET_STATUS:
return handle_status_request(cs, &client_addr, request.sequence);
case CONTROL_CMD_RESET_STATS:
return handle_reset_request(cs, &client_addr, request.sequence);
default:
return send_error_response(cs, &client_addr, request.sequence,
CONTROL_ERR_INVALID_CMD);
}
}
// Handle statistics request
static int handle_stats_request(control_socket_t *cs, struct sockaddr_in *client,
uint16_t sequence) {
control_stats_packet_t stats_packet = {0};
// Fill response header
stats_packet.response_type = CONTROL_RESP_STATS;
stats_packet.error_code = CONTROL_ERR_NONE;
stats_packet.sequence = sequence;
// Get current timestamp
struct timeval tv;
gettimeofday(&tv, NULL);
stats_packet.timestamp = (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
// Collect statistics from all modules
if (collect_statistics(cs, &stats_packet) < 0) {
return send_error_response(cs, client, sequence, CONTROL_ERR_INTERNAL);
}
// Send response
return send_response(cs, client, &stats_packet, sizeof(stats_packet));
}
// Handle status request
static int handle_status_request(control_socket_t *cs, struct sockaddr_in *client,
uint16_t sequence) {
control_status_packet_t status_packet = {0};
status_packet.response_type = CONTROL_RESP_STATUS;
status_packet.error_code = CONTROL_ERR_NONE;
status_packet.sequence = sequence;
// Collect system status
if (collect_system_status(cs, &status_packet.system) < 0) {
status_packet.system.uptime_seconds = 0;
}
// Set running flag
if (cs->app_state) {
// Assume utun_state_t has 'running' field at same offset
// We'll fix this when integrating with utun.c
status_packet.running = 1;
}
snprintf(status_packet.status_msg, sizeof(status_packet.status_msg),
"utun control socket active on port %u", cs->port);
return send_response(cs, client, &status_packet, sizeof(status_packet));
}
// Handle reset statistics request
static int handle_reset_request(control_socket_t *cs, struct sockaddr_in *client,
uint16_t sequence) {
// TODO: Implement statistics reset
// For now, just send success response
control_status_packet_t response = {0};
response.response_type = CONTROL_RESP_STATUS;
response.error_code = CONTROL_ERR_NONE;
response.sequence = sequence;
// Get basic system status
collect_system_status(cs, &response.system);
snprintf(response.status_msg, sizeof(response.status_msg),
"Statistics reset requested (not implemented)");
return send_response(cs, client, &response, sizeof(response));
}
// Send error response
static int send_error_response(control_socket_t *cs, struct sockaddr_in *client,
uint16_t sequence, control_err_t error) {
control_status_packet_t response = {0};
response.response_type = CONTROL_RESP_ERROR;
response.error_code = error;
response.sequence = sequence;
const char *error_msg = "Unknown error";
switch (error) {
case CONTROL_ERR_INVALID_CMD:
error_msg = "Invalid command";
break;
case CONTROL_ERR_INTERNAL:
error_msg = "Internal error";
break;
default:
break;
}
snprintf(response.status_msg, sizeof(response.status_msg), "%s", error_msg);
cs->total_errors++;
return send_response(cs, client, &response, sizeof(response));
}
// Send response to client
static int send_response(control_socket_t *cs, struct sockaddr_in *client,
void *data, size_t len) {
ssize_t sent = sendto(cs->sockfd, data, len, 0,
(struct sockaddr*)client, sizeof(*client));
if (sent != (ssize_t)len) {
perror("control_socket: sendto");
return -1;
}
return 0;
}
// Collect statistics from all modules
static int collect_statistics(control_socket_t *cs, control_stats_packet_t *stats) {
if (!cs->app_state) {
return -1;
}
utun_state_t *state = (utun_state_t*)cs->app_state;
// Zero out stats
memset(&stats->pkt_normalizer, 0, sizeof(stats->pkt_normalizer));
memset(&stats->etcp, 0, sizeof(stats->etcp));
memset(&stats->routing, 0, sizeof(stats->routing));
memset(&stats->tun, 0, sizeof(stats->tun));
stats->conn_count = 0;
// Collect connection statistics
if (state->connections && state->connection_count > 0) {
int max_conn = state->connection_count;
if (max_conn > MAX_CONN_STATS) max_conn = MAX_CONN_STATS;
for (int i = 0; i < max_conn; i++) {
conn_handle_t *conn = state->connections[i];
if (!conn) continue;
conn_stats_t conn_stats;
if (conn_get_stats(conn, &conn_stats) == 0) {
// Aggregate connection stats
stats->etcp.bytes_sent_total += conn_stats.bytes_sent;
stats->etcp.bytes_received_total += conn_stats.bytes_received;
stats->etcp.retransmissions += conn_stats.retransmissions;
stats->pkt_normalizer.fragments_assembled += conn_stats.fragments_assembled;
// Note: etcp and pkt_normalizer stats are aggregated across connections
// Additional per-connection stats could be stored in conn_stats_entry_t
}
// Fill connection entry (if we have array)
if (i < MAX_CONN_STATS) {
snprintf(stats->connections[i].name, sizeof(stats->connections[i].name), "conn%d", i);
stats->connections[i].bytes_sent = conn_stats.bytes_sent;
stats->connections[i].bytes_received = conn_stats.bytes_received;
stats->connections[i].packets_sent = conn_stats.packets_sent;
stats->connections[i].packets_received = conn_stats.packets_received;
stats->connections[i].retransmissions = conn_stats.retransmissions;
stats->connections[i].fragments_assembled = conn_stats.fragments_assembled;
stats->connections[i].current_rtt_ms = conn_stats.current_rtt_ms;
stats->connections[i].jitter_ms = conn_stats.jitter_ms;
stats->connections[i].is_active = 1;
}
}
stats->conn_count = max_conn;
}
// Collect routing statistics
if (state->routing_table) {
routing_get_stats(state->routing_table, &stats->routing);
}
// Collect TUN statistics
stats->tun.bytes_read = state->tun.bytes_read;
stats->tun.bytes_written = state->tun.bytes_written;
stats->tun.packets_read = state->tun.packets_read;
stats->tun.packets_written = state->tun.packets_written;
stats->tun.read_errors = state->tun.read_errors;
stats->tun.write_errors = state->tun.write_errors;
strncpy(stats->tun.ifname, state->tun.ifname, sizeof(stats->tun.ifname) - 1);
stats->tun.ifname[sizeof(stats->tun.ifname) - 1] = '\0';
strncpy(stats->tun.ip_addr, state->tun.ip_addr, sizeof(stats->tun.ip_addr) - 1);
stats->tun.ip_addr[sizeof(stats->tun.ip_addr) - 1] = '\0';
// Collect system status
return collect_system_status(cs, &stats->system);
}
// Collect system status
static int collect_system_status(control_socket_t *cs, system_status_t *status) {
if (!cs) return -1;
memset(status, 0, sizeof(*status));
// Calculate uptime
struct timeval now;
gettimeofday(&now, NULL);
status->uptime_seconds = now.tv_sec - cs->start_time.tv_sec;
// Get memory usage (simplified)
FILE *meminfo = fopen("/proc/self/statm", "r");
if (meminfo) {
unsigned long pages;
if (fscanf(meminfo, "%lu", &pages) == 1) {
status->memory_usage_kb = pages * 4; // Assume 4KB pages
}
fclose(meminfo);
}
// CPU usage (placeholder)
status->cpu_usage_percent = 0;
// Version info
snprintf(status->version, sizeof(status->version), "utun v0.1");
// Control port
status->control_port = cs->port;
// Running flag and connection count will be set by caller
return 0;
}
// Get ETCP statistics
static void get_etcp_stats(epkt_t *etcp, etcp_stats_t *etcp_stats) {
if (!etcp || !etcp_stats) return;
memset(etcp_stats, 0, sizeof(*etcp_stats));
// Use etcp_get_stats if available
// etcp_get_stats(etcp, &retransmissions, ...);
// Direct access to epkt fields (assuming struct is visible)
etcp_stats->retransmissions = etcp->retransmissions_count;
etcp_stats->total_packets_sent = etcp->total_packets_sent;
etcp_stats->unique_packets_sent = etcp->unique_packets_sent;
etcp_stats->bytes_sent_total = etcp->bytes_sent_total;
etcp_stats->bytes_received_total = etcp->bytes_received_total;
etcp_stats->ack_packets_count = etcp->ack_packets_count;
etcp_stats->control_packets_count = etcp->control_packets_count;
etcp_stats->current_rtt = etcp->rtt_avg_10;
etcp_stats->jitter = etcp->jitter;
etcp_stats->window_size = etcp->window_size;
etcp_stats->unacked_bytes = etcp->unacked_bytes;
}
// Get packet normalizer statistics
static void get_pkt_normalizer_stats(pkt_normalizer_pair *pair,
pkt_normalizer_stats_t *pn_stats) {
if (!pair || !pn_stats) return;
memset(pn_stats, 0, sizeof(*pn_stats));
if (pair->packer) {
pn_stats->packer_errors = pkt_normalizer_get_error_count(pair->packer);
}
if (pair->unpacker) {
pn_stats->unpacker_errors = pkt_normalizer_get_error_count(pair->unpacker);
// Fragments assembled count might need to be tracked separately
}
}
-208
View File
@@ -1,208 +0,0 @@
/**
* @file control_socket.h
* @brief Control socket for statistics and diagnostics over UDP
* @details Provides remote monitoring of utun VPN tunnel statistics
*/
#ifndef CONTROL_SOCKET_H
#define CONTROL_SOCKET_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
// Forward declarations for application state
struct utun_state;
#ifdef __cplusplus
extern "C" {
#endif
// Maximum connections for statistics
#define MAX_CONN_STATS 16
#define MAX_ROUTE_STATS 100
// Control socket command types
typedef enum {
CONTROL_CMD_GET_STATS = 1, // Request statistics
CONTROL_CMD_RESET_STATS = 2, // Reset statistics counters
CONTROL_CMD_GET_STATUS = 3, // Request status information
} control_cmd_t;
// Response types
typedef enum {
CONTROL_RESP_STATS = 1, // Statistics response
CONTROL_RESP_STATUS = 2, // Status response
CONTROL_RESP_ERROR = 0xFF, // Error response
} control_resp_t;
// Error codes
typedef enum {
CONTROL_ERR_NONE = 0,
CONTROL_ERR_INVALID_CMD = 1,
CONTROL_ERR_INTERNAL = 2,
} control_err_t;
// Packet normalizer statistics
typedef struct __attribute__((packed)) {
uint32_t packer_errors; // Packer error count
uint32_t unpacker_errors; // Unpacker error count
uint32_t fragments_assembled; // Fragments assembled successfully
uint32_t service_packets; // Service packets processed
} pkt_normalizer_stats_t;
// ETCP statistics
typedef struct __attribute__((packed)) {
uint32_t retransmissions; // Retransmission count
uint32_t total_packets_sent; // Total packets sent (including retransmissions)
uint32_t unique_packets_sent; // Unique packets sent (excluding retransmissions)
uint32_t bytes_sent_total; // Total bytes sent
uint32_t bytes_received_total;// Total bytes received
uint32_t ack_packets_count; // ACK packets sent
uint32_t control_packets_count; // Control packets sent
uint16_t current_rtt; // Current RTT (0.1ms units)
uint16_t jitter; // Jitter (0.1ms units)
uint32_t window_size; // Current window size (bytes)
uint32_t unacked_bytes; // Unacknowledged bytes in flight
} etcp_stats_t;
// Connection statistics
typedef struct __attribute__((packed)) {
uint64_t bytes_sent;
uint64_t bytes_received;
uint32_t packets_sent;
uint32_t packets_received;
uint32_t retransmissions;
uint32_t fragments_assembled;
uint16_t current_rtt_ms;
uint16_t jitter_ms;
uint8_t is_active; // 1 if connection is active
char name[32]; // Connection name from config
} conn_stats_entry_t;
// Routing statistics
typedef struct __attribute__((packed)) {
uint32_t total_routes; // Total routes in table
uint32_t static_routes; // Static routes count
uint32_t dynamic_routes; // Dynamic routes count
uint32_t local_routes; // Local routes count
uint32_t learned_routes; // Learned routes count
uint32_t lookup_count; // Total lookups performed
uint32_t hit_count; // Successful lookups
uint32_t miss_count; // Failed lookups (no route)
} routing_stats_t;
// TUN interface statistics
typedef struct __attribute__((packed)) {
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
char ifname[16]; // Interface name
char ip_addr[32]; // IP address with prefix
} tun_stats_t;
// System status
typedef struct __attribute__((packed)) {
uint32_t uptime_seconds; // Seconds since startup
uint32_t memory_usage_kb; // Memory usage in KB (approximate)
uint16_t cpu_usage_percent; // CPU usage percentage (0-100)
uint8_t running; // 1 if main loop is running
uint8_t connection_count; // Number of active connections
uint16_t control_port; // Control socket port
char version[32]; // Version string
} system_status_t;
// Complete statistics packet
typedef struct __attribute__((packed)) {
uint8_t response_type; // CONTROL_RESP_STATS
uint8_t error_code; // CONTROL_ERR_NONE if success
uint16_t sequence; // Sequence number (echoed from request)
// Timestamps
uint64_t timestamp; // Unix timestamp in microseconds
// Module statistics
pkt_normalizer_stats_t pkt_normalizer;
etcp_stats_t etcp;
routing_stats_t routing;
tun_stats_t tun;
// Connection statistics (variable length, up to MAX_CONN_STATS)
uint8_t conn_count; // Number of connection entries
conn_stats_entry_t connections[MAX_CONN_STATS];
// System status
system_status_t system;
// Reserved for future expansion
uint8_t reserved[64];
} control_stats_packet_t;
// Status response packet
typedef struct __attribute__((packed)) {
uint8_t response_type; // CONTROL_RESP_STATUS
uint8_t error_code; // CONTROL_ERR_NONE if success
uint16_t sequence; // Sequence number
system_status_t system;
uint8_t running; // Main loop running flag
char status_msg[128]; // Human-readable status message
} control_status_packet_t;
// Request packet (from client)
typedef struct __attribute__((packed)) {
uint8_t command; // CONTROL_CMD_*
uint16_t sequence; // Sequence number for response matching
uint8_t reserved; // Reserved, must be 0
} control_request_packet_t;
// Control socket handle (opaque)
typedef struct control_socket control_socket_t;
/**
* @brief Create and initialize control socket
* @param ip IP address to bind to (NULL for any)
* @param port UDP port to listen on
* @return Control socket handle or NULL on error
*/
control_socket_t* control_socket_create(const char *ip, uint16_t port);
/**
* @brief Destroy control socket and free resources
* @param cs Control socket handle
*/
void control_socket_destroy(control_socket_t *cs);
/**
* @brief Get file descriptor for polling
* @param cs Control socket handle
* @return Socket file descriptor or -1 if invalid
*/
int control_socket_get_fd(const control_socket_t *cs);
/**
* @brief Process incoming control socket requests
* @param cs Control socket handle
* @param state Application state (for collecting statistics)
* @return 0 on success, -1 on error
*
* This function should be called when the socket is readable.
* It will handle incoming requests and send responses.
*/
int control_socket_process(control_socket_t *cs, void *state);
/**
* @brief Set application state pointer for statistics collection
* @param cs Control socket handle
* @param state Application state (utun_state_t*)
*/
void control_socket_set_state(control_socket_t *cs, void *state);
#ifdef __cplusplus
}
#endif
#endif /* CONTROL_SOCKET_H */
+66
View File
@@ -0,0 +1,66 @@
// 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
View File
@@ -0,0 +1,19 @@
#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
+183 -1246
View File
File diff suppressed because it is too large. Load diff
+35 -226
View File
@@ -1,250 +1,59 @@
// etcp.h - Расширенный протокол управления передачей (Extended Transmission Control Protocol)
// etcp.h - Расширенный протокол управления передачей (ETCP Protocol)
#ifndef ETCP_H
#define ETCP_H
#include <stdint.h>
#include <stddef.h>
#include "etcp_sockets.h"
#include "etcp_connections.h"
#include "packet_pool.h"
#include "ll_queue.h"
#include "u_async.h"
// Отладочное логирование
#ifdef ETCP_DEBUG
#include <stdio.h>
#define ETCP_LOG(fmt, ...) printf("[ETCP] " fmt, ##__VA_ARGS__)
#ifdef ETCP_DEBUG_EXT
#define ETCP_DEBUG_LOG(fmt, ...) printf("[ETCP_DEBUG] " fmt, ##__VA_ARGS__)
#else
#define ETCP_DEBUG_LOG(fmt, ...) ((void)0)
#endif
#else
#define ETCP_LOG(fmt, ...) ((void)0)
#define ETCP_DEBUG_LOG(fmt, ...) ((void)0)
#endif
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// Предварительные объявления
typedef struct epkt epkt_t;
// Forward declarations
typedef struct utun_instance utun_instance_t;
typedef struct ETCP_CONN etcp_t;
// Тип обратного вызова для отправки пакетов через UDP
typedef void (*etcp_tx_callback_t)(epkt_t* epkt, uint8_t* pkt, uint16_t len, void* arg);
struct ETCP_CONN {
// Node ID (64-bit, генерируется при первом запуске)
uint64_t node_id;
int mtu;
// Тип обратного вызова для уведомления о reset
typedef void (*etcp_reset_callback_t)(epkt_t* epkt, void* arg);
utun_instance_t *instance;
// Основная структура ETCP
struct epkt {
// Очереди
ll_queue_t* tx_queue; // Очередь данных для отправки
ll_queue_t* output_queue; // Выходная очередь (собранные данные)
// Каналы (соединения) - linked list
struct ETCP_LINK* channels;
// Список полученных пакетов (отсортированный связанный список)
struct rx_packet* rx_list;
// Пул пакетов (общий для всего ETCP instance)
packet_pool_t packet_pool;
// Отправленные пакеты (для повторной передачи)
struct sent_packet* sent_list;
// Метрики
uint16_t rtt_last; // Последнее RTT (в единицах времени 0.1 мкс)
uint16_t rtt_avg_10; // Среднее RTT за последние 10 пакетов
uint16_t rtt_avg_100; // Среднее RTT за последние 100 пакетов
uint16_t jitter; // Джиттер (усредненный)
uint16_t bandwidth; // Текущая пропускная способность (байты за единицу времени)
uint32_t bytes_sent_total; // Общее количество отправленных байт
uint16_t last_sent_timestamp; // Временная метка последнего отправленного пакета
uint32_t bytes_allowed; // Рассчитанное количество разрешенных к отправке байт
// Статистика
uint32_t retransmissions_count; // Количество ретрансмиссий
uint32_t ack_packets_count; // Количество отправленных пакетов подтверждения
uint32_t control_packets_count; // Количество отправленных управляющих пакетов (ACK + запросы ретрансмиссии)
uint32_t total_packets_sent; // Общее количество отправленных пакетов (включая ретрансмиссии)
uint32_t unique_packets_sent; // Количество уникальных отправленных пакетов (без ретрансмиссий)
uint32_t bytes_received_total; // Общее количество полученных байт
// Состояние
uint16_t next_tx_id; // Следующий ID для передачи
uint16_t last_sent_id; // Последний отправленный ID (для ретрансмиссии самого нового пакета)
uint16_t last_rx_id; // Последний полученный ID (для подтверждения)
uint16_t last_delivered_id; // Последний ID, переданный в output_queue
// Таймеры
void* next_tx_timer; // Таймер для следующей передачи
void* retransmit_timer; // Таймер для повторных передач
uasync_t* ua; // Экземпляр uasync для таймеров
// Обратный вызов
etcp_tx_callback_t tx_callback;
void* tx_callback_arg;
// История RTT для усреднения
uint16_t rtt_history[100];
uint8_t rtt_history_idx;
uint8_t rtt_history_count;
// Ожидающие подтверждения
uint16_t pending_ack_ids[32];
uint16_t pending_ack_timestamps[32];
uint8_t pending_ack_count;
// Ожидающие запросы на повторную передачу
uint16_t pending_retransmit_ids[32];
uint8_t pending_retransmit_count;
// Управление окном
uint32_t unacked_bytes; // Количество байт, отправленных но еще не подтвержденных
uint32_t window_size; // Текущий размер окна в байтах (рассчитывается)
uint16_t last_acked_id; // Последний подтвержденный ID пакета
uint16_t last_rx_ack_id; // Последний полученный ID подтверждения от получателя
uint16_t retrans_timer_period; // Текущий период таймера повторной передачи (в единицах времени)
uint16_t next_retrans_time; // Время следующей проверки повторной передачи
uint8_t window_blocked; // Флаг: передача заблокирована из-за ограничения окна
// Forward progress tracking
uint16_t oldest_missing_id; // Oldest missing packet ID
uint16_t missing_since_time; // Time when oldest missing packet was first detected
// Reset state
uint8_t reset_pending; // Reset packet sent, waiting for ACK
uint8_t reset_ack_received; // Reset ACK received
void* reset_timer; // Timer for reset retransmission
uint16_t reset_retry_count; // Number of reset retries
// Reset callback and initialization state
uint8_t initialized; // 1 = connection initialized after first reset handshake
etcp_reset_callback_t reset_callback; // Callback for reset notification
void* reset_callback_arg; // Callback argument
uint8_t state; // 0 - just created, 1 - initialized, 2 - connection established
// Криптография и состояние (TODO: определить sc_context_t)
// void* crypto_ctx;
ll_queue_t* input_queue;// отправитель -> input_queue -> etcp -> etcp_channel -> отправка (etcp_sockets)
ll_queue_t* output_queue;// etcp_sockets -> etcp_channel -> etcp -> output_queue -> получатель
};
// Функции API
// Создание пустого ETCP instance (после надо добавить ключи, каналы)
struct ETCP_CONN* etcp_create(utun_instance_t *instance);
/**
* @brief Инициализировать новый экземпляр ETCP
* @param ua Экземпляр uasync для таймеров (обязательный параметр)
* @return Указатель на новый экземпляр или NULL в случае ошибки
*/
epkt_t* etcp_init(uasync_t* ua);
// Уничтожение ETCP instance
void etcp_destroy(struct ETCP_CONN* etcp);
/**
* @brief Освободить экземпляр ETCP и все связанные ресурсы
* @param epkt Экземпляр для освобождения
*/
void etcp_free(epkt_t* epkt);
// Обработать входящий пакет (вызывается из etcp_connections)
void etcp_conn_input(struct ETCP_CONN* etcp, packet_buffer_t* pkt);
/**
* @brief Установить обратный вызов для отправки пакетов через UDP
* @param epkt Экземпляр ETCP
* @param cb Функция обратного вызова
* @param arg Пользовательский аргумент, передаваемый в обратный вызов
*/
void etcp_set_callback(epkt_t* epkt, etcp_tx_callback_t cb, void* arg);
/**
* @brief Установить обратный вызов для уведомления о reset
* @param epkt Экземпляр ETCP
* @param cb Функция обратного вызова
* @param arg Пользовательский аргумент, передаваемый в обратный вызов
*/
void etcp_set_reset_callback(epkt_t* epkt, etcp_reset_callback_t cb, void* arg);
/**
* @brief Обработать полученный UDP пакет
* @param epkt Экземпляр ETCP
* @param pkt Данные пакета
* @param len Длина пакета
* @return 0 при успехе, -1 при ошибке
*/
int etcp_rx_input(epkt_t* epkt, uint8_t* pkt, uint16_t len);
/**
* @brief Получить общее количество пакетов, ожидающих в очередях передачи
* @param epkt Экземпляр ETCP
* @return Количество пакетов
*/
int etcp_tx_queue_size(epkt_t* epkt);
/**
* @brief Поместить данные в очередь передачи
* @param epkt Экземпляр ETCP
* @param data Данные для отправки
* @param len Длина данных
* @return 0 при успехе, -1 при ошибке
*/
int etcp_tx_put(epkt_t* epkt, uint8_t* data, uint16_t len);
/**
* @brief Получить выходную очередь для чтения полученных данных
* @param epkt Экземпляр ETCP
* @return Указатель на выходную очередь (ll_queue_t*)
*/
ll_queue_t* etcp_get_output_queue(epkt_t* epkt);
/**
* @brief Установить ограничение пропускной способности
* @param epkt Экземпляр ETCP
* @param bandwidth Байты за единицу времени (0.1 мкс)
*/
void etcp_set_bandwidth(epkt_t* epkt, uint16_t bandwidth);
/**
* @brief Обновить размер окна на основе текущего RTT и пропускной способности
* @param epkt Экземпляр ETCP
* Размер окна = RTT * пропускная способность * 2 (байт в пути)
*/
void etcp_update_window(epkt_t* epkt);
/**
* @brief Получить текущее RTT
* @param epkt Экземпляр ETCP
* @return RTT в единицах времени
*/
uint16_t etcp_get_rtt(epkt_t* epkt);
/**
* @brief Получить текущий джиттер
* @param epkt Экземпляр ETCP
* @return Джиттер в единицах времени
*/
uint16_t etcp_get_jitter(epkt_t* epkt);
/**
* @brief Сбросить состояние соединения (очистить очереди, метрики, таймеры)
* @param epkt Экземпляр ETCP
* Примечание: Сохраняет настройки пропускной способности и обратного вызова
*/
void etcp_reset(epkt_t* epkt);
/**
* @brief Инициировать сброс соединения через служебные пакеты
* @param epkt Экземпляр ETCP
* Отправляет пакет сброса (0x02) и ждет подтверждения (0x03) с повторными попытками каждые 100мс
*/
void etcp_reset_connection(epkt_t* epkt);
/**
* @brief Получить статистику ETCP
* @param epkt Экземпляр ETCP
* @param retransmissions Указатель для возврата количества ретрансмиссий
* @param total_packets_sent Указатель для возврата общего количества отправленных пакетов
* @param unique_packets_sent Указатель для возврата количества уникальных отправленных пакетов
* @param bytes_sent_total Указатель для возврата общего количества отправленных байт
* @param bytes_received_total Указатель для возврата общего количества полученных байт
* @param ack_packets_count Указатель для возврата количества отправленных пакетов подтверждения
* @param control_packets_count Указатель для возврата количества отправленных управляющих пакетов
*/
void etcp_get_stats(epkt_t* epkt,
uint32_t* retransmissions,
uint32_t* total_packets_sent,
uint32_t* unique_packets_sent,
uint32_t* bytes_sent_total,
uint32_t* bytes_received_total,
uint32_t* ack_packets_count,
uint32_t* control_packets_count);
// Получение статистики
void etcp_get_stats(struct ETCP_CONN* etcp, size_t* packets_sent, size_t* packets_recv,
size_t* pool_allocs, size_t* pool_reuse);
#ifdef __cplusplus
}
#endif
#endif // ETCP_H
#endif // ETCP_H
+530
View File
@@ -0,0 +1,530 @@
#include "etcp_connections.h"
#include <arpa/inet.h>
#include "routing.h"
#include "utun_instance.h"
#include "crc32.h"
#include "etcp.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
// Forward declaration
struct utun_instance;
// Бинарный поиск линка по ip_port_hash
static int find_link_index(struct ETCP_CONNECTIONS* conns, uint32_t hash) {
if (!conns || conns->num_channels == 0) return -1;
int left = 0;
int right = conns->num_channels - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (conns->links[mid]->ip_port_hash == hash) {
return mid;
} else if (conns->links[mid]->ip_port_hash < hash) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -(left + 1);
}
// Реалокация массива линков с увеличением в 2 раза
static int realloc_links(struct ETCP_CONNECTIONS* conns) {
size_t new_max = conns->max_channels == 0 ? 8 : conns->max_channels * 2;
struct ETCP_LINK** new_links = realloc(conns->links, new_max * sizeof(struct ETCP_LINK*));
if (!new_links) return -1;
conns->links = new_links;
conns->max_channels = new_max;
return 0;
}
// Вставка линка в отсортированный массив
static int insert_link(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link) {
if (!conns || !link) return -1;
if (conns->num_channels >= conns->max_channels) {
if (realloc_links(conns) < 0) return -1;
}
int idx = find_link_index(conns, link->ip_port_hash);
if (idx >= 0) return -1;
idx = -(idx + 1);
if (idx < (int)conns->num_channels) {
memmove(&conns->links[idx + 1], &conns->links[idx],
(conns->num_channels - idx) * sizeof(struct ETCP_LINK*));
}
conns->links[idx] = link;
conns->num_channels++;
return 0;
}
// Удаление линка из массива
static void remove_link(struct ETCP_CONNECTIONS* conns, uint32_t hash) {
if (!conns || conns->num_channels == 0) return;
int idx = find_link_index(conns, hash);
if (idx < 0) return;
if (idx < (int)conns->num_channels - 1) {
memmove(&conns->links[idx], &conns->links[idx + 1],
(conns->num_channels - idx - 1) * sizeof(struct ETCP_LINK*));
}
conns->num_channels--;
}
struct ETCP_CONNECTIONS* etcp_connections_init(etcp_socket_t* socket) {
if (!socket) return NULL;
struct ETCP_CONNECTIONS* conns = calloc(1, sizeof(struct ETCP_CONNECTIONS));
if (!conns) return NULL;
memcpy(&conns->socket, socket, sizeof(struct ETCP_SOCKET));
return conns;
}
void etcp_connections_destroy(struct ETCP_CONNECTIONS* conns) {
if (!conns) return;
for (size_t i = 0; i < conns->num_channels; i++) {
etcp_link_close(conns->links[i]);
}
free(conns->links);
free(conns);
}
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, etcp_socket_t* socket,
const struct sockaddr* remote_addr, socklen_t addr_len) {
if (!etcp || !socket || !remote_addr || addr_len == 0) return NULL;
struct ETCP_LINK* link = calloc(1, sizeof(struct ETCP_LINK));
if (!link) return NULL;
link->socket = (struct ETCP_SOCKET*)socket;
link->etcp = etcp;
memcpy(&link->remote_addr, remote_addr, addr_len);
link->remote_addr_len = addr_len;
link->last_activity = time(NULL);
uint8_t* addr_bytes = (uint8_t*)remote_addr;
link->ip_port_hash = crc32(0, addr_bytes, addr_len);
return link;
}
void etcp_link_close(struct ETCP_LINK* link) {
if (!link) return;
free(link);
}
int etcp_input(packet_buffer_t* pkt, etcp_socket_t* socket, struct ETCP_CONNECTIONS* conns) {
if (!pkt || !socket || !conns) return -1;
return 0;
}
int etcp_link_send(struct ETCP_CONN* etcp, struct ETCP_LINK* link,
const uint8_t* data, size_t len) {
if (!etcp || !link || !data || len == 0) return -1;
link->last_activity = time(NULL);
return etcp_socket_send((etcp_socket_t*)link->socket, data, len,
(struct sockaddr*)&link->remote_addr);
}
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_CONNECTIONS* conns,
const struct sockaddr* addr, socklen_t addr_len) {
if (!conns || !addr || addr_len == 0) return NULL;
uint32_t hash = crc32(0, (uint8_t*)addr, addr_len);
int idx = find_link_index(conns, hash);
if (idx >= 0) {
return conns->links[idx];
}
return NULL;
}
int etcp_link_add_to_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link) {
if (!conns || !link) return -1;
return insert_link(conns, link);
}
void etcp_link_remove_from_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link) {
if (!conns || !link) return;
remove_link(conns, link->ip_port_hash);
}
// SOCKET FUNCTIONS (moved from etcp_sockets.c)
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <arpa/inet.h>
etcp_socket_t* etcp_socket_create(struct ETCP_CONN* etcp, const char* bind_addr, uint16_t port) {
if (!etcp) return NULL;
etcp_socket_t* sock = calloc(1, sizeof(etcp_socket_t));
if (!sock) return NULL;
sock->etcp = etcp;
sock->socket_id = (uint32_t)(uintptr_t)sock;
sock->fd = socket(AF_INET, SOCK_DGRAM, 0);
if (sock->fd < 0) {
free(sock);
return NULL;
}
int flags = fcntl(sock->fd, F_GETFL, 0);
fcntl(sock->fd, F_SETFL, flags | O_NONBLOCK);
struct sockaddr_in* addr = (struct sockaddr_in*)&sock->local_addr;
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
sock->local_addr_len = sizeof(struct sockaddr_in);
if (bind_addr) {
inet_pton(AF_INET, bind_addr, &addr->sin_addr);
} else {
addr->sin_addr.s_addr = INADDR_ANY;
}
if (bind(sock->fd, (struct sockaddr*)addr, sock->local_addr_len) < 0) {
close(sock->fd);
free(sock);
return NULL;
}
if (getsockname(sock->fd, (struct sockaddr*)addr, &sock->local_addr_len) < 0) {
close(sock->fd);
free(sock);
return NULL;
}
return sock;
}
void etcp_socket_destroy(etcp_socket_t* sock) {
if (!sock) return;
if (sock->fd >= 0) {
close(sock->fd);
}
free(sock);
}
int etcp_socket_send(etcp_socket_t* sock, const uint8_t* data, size_t len, const struct sockaddr* to_addr) {
if (!sock || sock->fd < 0 || !data || !to_addr) {
return -1;
}
socklen_t addr_len = (to_addr->sa_family == AF_INET) ?
sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6);
return sendto(sock->fd, data, len, 0, to_addr, addr_len);
}
int etcp_socket_get_fd(const etcp_socket_t* sock) {
if (!sock) return -1;
return sock->fd;
}
void etcp_socket_get_local_addr(const etcp_socket_t* sock, struct sockaddr_storage* addr, socklen_t* addr_len) {
if (!sock || !addr || !addr_len) return;
memcpy(addr, &sock->local_addr, sock->local_addr_len);
*addr_len = sock->local_addr_len;
}
int etcp_socket_set_option(etcp_socket_t* sock, int level, int optname, const void* optval, socklen_t optlen) {
if (!sock || sock->fd < 0) return -1;
return setsockopt(sock->fd, level, optname, optval, optlen);
}
// Connection management functions
void conn_destroy(conn_handle_t* handle) {
if (!handle) return;
if (handle->conns) {
etcp_socket_destroy((etcp_socket_t*)&handle->conns->socket);
etcp_connections_destroy(handle->conns);
}
if (handle->etcp) {
etcp_destroy(handle->etcp);
}
free(handle);
}
int conn_send(conn_handle_t* handle, const uint8_t* data, size_t len) {
if (!handle || !handle->conns || handle->conns->num_channels == 0 || !data || len == 0) {
return -1;
}
struct ETCP_LINK* link = handle->conns->links[0];
if (!link || !link->etcp) return -1;
return etcp_link_send(link->etcp, link, data, len);
}
// Parse address in format "IP:port" or "[IPv6]:port" and fill sockaddr
static int parse_addr_port(const char* addr_str, struct sockaddr_storage* addr, socklen_t* addr_len) {
if (!addr_str || !addr || !addr_len) return -1;
char addr_copy[MAX_ADDR_LEN];
strncpy(addr_copy, addr_str, MAX_ADDR_LEN - 1);
addr_copy[MAX_ADDR_LEN - 1] = '\0';
// Check for IPv6 format: [address]:port
if (addr_copy[0] == '[') {
char* closing_bracket = strchr(addr_copy, ']');
if (!closing_bracket) return -1;
// Find port after ']:'
char* colon = strchr(closing_bracket + 1, ':');
if (!colon) return -1;
*closing_bracket = '\0';
uint16_t port = (uint16_t)atoi(colon + 1);
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr;
memset(sin6, 0, sizeof(struct sockaddr_in6));
sin6->sin6_family = AF_INET6;
sin6->sin6_port = htons(port);
// Extract IP string and port from sockaddr_storage
static int sockaddr_to_ip_port(const struct sockaddr_storage* addr, char* ip_out, size_t ip_out_len, uint16_t* port_out) {
if (!addr || !ip_out || !port_out) return -1;
if (addr->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)addr;
*port_out = ntohs(sin->sin_port);
if (inet_ntop(AF_INET, &sin->sin_addr, ip_out, ip_out_len) == NULL) return -1;
} else if (addr->ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr;
*port_out = ntohs(sin6->sin6_port);
if (inet_ntop(AF_INET6, &sin6->sin6_addr, ip_out, ip_out_len) == NULL) return -1;
} else {
return -1;
}
return 0;
}
for (int i = 0; i < config->server_count; i++) {
server_config_t* server = &config->servers[i];
char bind_addr[INET_ADDRSTRLEN];
struct sockaddr_storage server_addr;
socklen_t server_addr_len;
if (parse_addr_port(server->addr, &server_addr, &server_addr_len) < 0) {
fprintf(stderr, "Invalid server address '%s'\n", server->addr);
goto cleanup;
}
char bind_addr_str[INET6_ADDRSTRLEN];
uint16_t bind_port;
if (sockaddr_to_ip_port(&server_addr, bind_addr_str, sizeof(bind_addr_str), &bind_port) < 0) {
etcp_destroy(etcp);
goto cleanup;
}
}
etcp_socket_t* sock = etcp_socket_create(etcp, bind_addr, bind_port);
if (!sock) {
fprintf(stderr, "Failed to create socket for %s\n", server->name);
etcp_destroy(etcp);
goto cleanup;
}
if (i == 0) {
instance->first_listen_socket = (struct ETCP_SOCKET*)sock;
}
struct ETCP_CONNECTIONS* conns = etcp_connections_init(sock);
if (!conns) {
etcp_socket_destroy(sock);
etcp_destroy(etcp);
goto cleanup;
}
conn_handle_t* handle = calloc(1, sizeof(conn_handle_t));
if (!handle) {
etcp_connections_destroy(conns);
etcp_socket_destroy(sock);
etcp_destroy(etcp);
goto cleanup;
}
handle->etcp = etcp;
handle->conns = conns;
for (int j = 0; j < config->client_count; j++) {
client_config_t* client = &config->clients[j];
if (strcmp(client->from, server->name) != 0) continue;
struct sockaddr_storage client_addr;
socklen_t client_addr_len;
if (parse_addr_port(client->to_addr, &client_addr, &client_addr_len) < 0) {
fprintf(stderr, "Invalid client address '%s'\n", client->to_addr);
continue;
}
memcpy(&addr, &client_addr, client_addr_len);
addr_len = client_addr_len;
struct sockaddr_in* sin = (struct sockaddr_in*)&addr;
memset(sin, 0, sizeof(struct sockaddr_in));
sin->sin_family = AF_INET;
sin->sin_port = htons(client_port);
if (inet_pton(AF_INET, client_addr, &sin->sin_addr) != 1) continue;
addr_len = sizeof(struct sockaddr_in);
struct ETCP_LINK* link = etcp_link_new(etcp, sock, (struct sockaddr*)&addr, addr_len);
if (link) {
etcp_link_add_to_connections(conns, link);
}
}
instance->connections[i] = handle;
}
instance->connection_count = config->server_count;
return 0;
cleanup:
if (instance->connections) {
for (int j = 0; j < 0; j++) {
if (instance->connections[j]) {
conn_destroy(instance->connections[j]);
}
}
free(instance->connections);
instance->connections = NULL;
}
instance->first_listen_socket = NULL;
instance->connection_count = 0;
return -1;
}
#include "etcp_connections.h"
#include "etcp.h"
#include "config_parser.h"
#include "utun_instance.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
// Parse address in format "IP:port" or "[IPv6]:port" and fill sockaddr
if (!addr || !ip_out || !port_out) return -1;
if (addr->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)addr;
*port_out = ntohs(sin->sin_port);
if (inet_ntop(AF_INET, &sin->sin_addr, ip_out, ip_out_len) == NULL) return -1;
} else if (addr->ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr;
*port_out = ntohs(sin6->sin6_port);
if (inet_ntop(AF_INET6, &sin6->sin6_addr, ip_out, ip_out_len) == NULL) return -1;
} else {
return -1;
}
return 0;
int init_connections(utun_instance_t* instance) {
if (!instance || !instance->config) {
fprintf(stderr, "Invalid instance or config\n");
return -1;
}
utun_config_t* config = instance->config;
int total_connections = config->server_count, i = 0;
if (total_connections == 0) {
fprintf(stderr, "No servers found in configuration\n");
return -1;
}
instance->connections = calloc(total_connections, sizeof(conn_handle_t*));
if (!instance->connections) {
fprintf(stderr, "Failed to allocate connections array\n");
return -1;
}
for (i = 0; i < config->server_count; i++) {
server_config_t* server = &config->servers[i];
struct sockaddr_storage server_addr;
socklen_t server_addr_len;
if (parse_addr_port(server->addr, &server_addr, &server_addr_len) < 0) {
fprintf(stderr, "Invalid server address '%s'\n", server->addr);
goto cleanup;
}
struct ETCP_CONN* etcp = etcp_create(instance);
if (!etcp) {
fprintf(stderr, "Failed to create ETCP for server %s\n", server->name);
goto cleanup;
}
char bind_addr_str[INET6_ADDRSTRLEN];
}
conn_handle_t* handle = calloc(1, sizeof(conn_handle_t));
if (!handle) {
etcp_connections_destroy(conns);
etcp_socket_destroy(sock);
etcp_destroy(etcp);
goto cleanup;
}
handle->etcp = etcp;
handle->conns = conns;
for (int j = 0; j < config->client_count; j++) {
client_config_t* client = &config->clients[j];
if (strcmp(client->from, server->name) != 0) continue;
struct sockaddr_storage client_addr;
socklen_t client_addr_len;
if (parse_addr_port(client->to_addr, &client_addr, &client_addr_len) < 0) {
fprintf(stderr, "Invalid client address '%s'\n", client->to_addr);
continue;
}
struct sockaddr_storage addr;
socklen_t addr_len = client_addr_len;
struct ETCP_LINK* link = etcp_link_new(etcp, sock, (struct sockaddr*)&addr, addr_len);
if (link) {
etcp_link_add_to_connections(conns, link);
}
}
instance->connections[i] = handle;
}
instance->connection_count = config->server_count;
return 0;
cleanup:
if (instance->connections) {
for (int j = 0; j < i; j++) {
if (instance->connections[j]) {
conn_destroy(instance->connections[j]);
}
}
free(instance->connections);
instance->connections = NULL;
}
instance->first_listen_socket = NULL;
instance->connection_count = 0;
return -1;
+78
View File
@@ -0,0 +1,78 @@
#ifndef ETCP_CONNECTIONS_H
#define ETCP_CONNECTIONS_H
#include "packet_buffer.h"
#include <stdint.h>
#include <sys/socket.h>
// Forward declarations
typedef struct utun_instance utun_instance_t;
typedef struct etcp_socket etcp_socket_t;
struct ETCP_SOCKET {
int fd; // Файловый дескриптор UDP сокета
struct sockaddr_storage local_addr; // Локальный адрес
socklen_t local_addr_len; // Длина локального адреса
struct ETCP_CONN* etcp; // Родительский ETCP instance
uint32_t socket_id; // Уникальный ID сокета
// Callback при получении данных не нужен. вызываем сразу etcp_input -> etcp_conn_input
};
// список активных подключений которые обслуживает сокет. каждый сокет может обслуживать много подключений
struct ETCP_CONNECTIONS {
struct ETCP_CONNECTIONS* next; // Linked list для всех соединений
// сокет для отправки/приема
struct ETCP_SOCKET socket; // Сокет через который идет трафик
// для входящих подключений - массив упорядоченный по ip_port_hash
size_t max_channels; // сколько выделено памяти
size_t num_channels; // сколько активно
struct ETCP_LINK** links;// массив указателей на линки, сортированный по ip_port_hash
};
// ETCP Link - одно динамическое соединение (один путь)
struct ETCP_LINK {
uint32_t ip_port_hash; // crc32 для быстрого поиска
struct ETCP_LINK* next; // Linked list подключений для ETCP_CONN
struct ETCP_SOCKET* socket; // Сокет через который идет трафик
struct ETCP_CONN* etcp; // Родительский ETCP instance
// Путь соединения
struct sockaddr_storage remote_addr; // Удалённый адрес
socklen_t remote_addr_len;
uint64_t last_activity; // Время последней активности
uint8_t initialized;
};
// SOCKET FUNCTIONS (moved from etcp_sockets)
etcp_socket_t* etcp_socket_create(struct ETCP_CONN* etcp, const char* bind_addr, uint16_t port);
void etcp_socket_destroy(etcp_socket_t* sock);
int etcp_socket_send(etcp_socket_t* sock, const uint8_t* data, size_t len, const struct sockaddr* to_addr);
void etcp_socket_set_recv_callback(etcp_socket_t* sock, void (*callback)(struct etcp_socket*, packet_buffer_t*, void*), void* user_data);
int etcp_socket_get_fd(const etcp_socket_t* sock);
void etcp_socket_get_local_addr(const etcp_socket_t* sock, struct sockaddr_storage* addr, socklen_t* addr_len);
int etcp_socket_set_option(etcp_socket_t* sock, int level, int optname, const void* optval, socklen_t optlen);
// CONNECTIONS FUNCTIONS
struct ETCP_CONNECTIONS* etcp_connections_init(etcp_socket_t* socket);
void etcp_connections_destroy(struct ETCP_CONNECTIONS* conns);
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, etcp_socket_t* socket, const struct sockaddr* remote_addr, socklen_t addr_len);
void etcp_link_close(struct ETCP_LINK* link);
int etcp_input(packet_buffer_t* pkt, etcp_socket_t* socket, struct ETCP_CONNECTIONS* conns);
int etcp_link_send(struct ETCP_CONN* etcp, struct ETCP_LINK* link, const uint8_t* data, size_t len);
// CONNECTION MANAGEMENT
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_CONNECTIONS* conns, const struct sockaddr* addr, socklen_t addr_len);
int etcp_link_add_to_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link);
void etcp_link_remove_from_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link);
// INITIALIZATION
int init_connections(struct utun_instance* instance);
#endif // ETCP_CONNECTIONS_H
+49
View File
@@ -0,0 +1,49 @@
Протокол ETCP. Как работает
протокол похож на QUICK но с небольшими отличиями:
- устанавливает между клиентом и сервером только одно ETCP соединение, но для доставки пакетов может использовать разные маршруты (несколько разных сокетов и адресов назначения).
ETCP соединение не должно накапливать очереди (где это возможно отправлять пакет сразу, в остальных случаях pacing - он есть в ll_queue). только держать отправленные но не подтвержденные пакеты.
utun при запуске генерирует свой рандом ID (64bit). назовём его ID узла. если его нет в конфиге, он прописывается в конфиг. т.е. при перезапуске не меняется.
- сокеты открываются при запуске, при работе не меняются. исходящие подключения - через эти же сокеты.
лучше сделать так - пакет полуен из сокета -> в функции обработки сразу выделение буфера, расшифровка в этот буфер, вызов из etcp_channel функции разбора пакета с аргументом структуры и ip_port.
в etcp_chanel функция разобра пакета:
- осуществит подсчет хеша, поиск канала
- если найден - добавляем пакет в очередь,
- не найден и пакет не init - отправляем RST (надо переподключиться)
- init и не найден - создание инстанса и добавление пакета в его очередь. (создаём новое подключение)
в ETCP:
Если получили RST:
- очищаем все состояния, буфера итд, но сохраняем исходящую очередь,
- инициируем подключение заново (периодически отправляем init request и ждем init_response)
ip_port:
uinion{ addr (v4 или v6)}
u16 port
u8 ip_type (v4/v6)
packet bufer:
*etcp_channel (заполняется при нахождении или создании)
payload расшифрованного пакета
etcp_channel:
- *next (linked-list каналов для одного etcp инстанса) чтобы быстро удалять подключение.
- ip:port удаленной стороны в бинарном виде
- указатель на etcp_socket (сокеты открываются при запуске те что в конфиге и через них весь обмен в т.ч. исходящий трафик. к каждому узлу может быть только одно etcp подключение)
- хеш ip:port (32bit, crc32)
- указатель на инстанс etcp
etcp_socket:
socket
uasync *socket
записи etcp_channel при создании записываются в массив и сортируются по хешу ip:port для быстрого поиска.
etcp содержит:
- вместо unique id подключения - ID удалённого узла (которое передается в init request и init response)
- *etcp_first_channel
- очереди и прочее необходимое для работы
+49 -1
View File
@@ -1,4 +1,6 @@
В etcp надо доработать механизм reset:
Устаревшее:
В etcp надо доработать механизм reset:
- если клиент или сервер запущен, то он при инициализации подключения должен послать сигнал сброса удаленной стороне, при этом сам ожидать подтверждения. после получения подтверждения переходить в нормальный режим работы без локального сброса (если это первичная инициализация) и со сбросом (если повторная инициализация).
т.е. вводим флаг initialized=0 и устанавливаем его в 1 в конце первичного сброса.
@@ -10,3 +12,49 @@
connectin.c при запросе ресета должен вызывать очистку очередей в pkt_normalizer.
это позволяет корректно сбрасывать соединение если перезагрузился клиент или сервер.
Доработки:
utun при запуске генерирует свой рандом ID (64bit). назовём его ID узла. если его нет в конфиге, он прописывается в конфиг. т.е. при перезапуске не меняется.
- сокеты открываются при запуске, при работе не меняются. исходящие подключения - через эти же сокеты.
лучше сделать так - пакет полуен из сокета -> в функции обработки сразу выделение буфера, расшифровка в этот буфер, вызов из etcp_channel функции разбора пакета с аргументом структуры и ip_port.
в etcp_chanel функция разобра пакета:
- осуществит подсчет хеша, поиск канала
- если найден - добавляем пакет в очередь,
- не найден и пакет не init - отправляем RST (надо переподключиться)
- init и не найден - создание инстанса и добавление пакета в его очередь. (создаём новое подключение)
в ETCP:
Если получили RST:
- очищаем все состояния, буфера итд, но сохраняем исходящую очередь,
- инициируем подключение заново (периодически отправляем init request и ждем init_response)
ip_port:
uinion{ addr (v4 или v6)}
u16 port
u8 ip_type (v4/v6)
packet bufer:
*etcp_channel (заполняется при нахождении или создании)
payload расшифрованного пакета
etcp_channel:
- *next (linked-list каналов для одного etcp инстанса) чтобы быстро удалять подключение.
- ip:port удаленной стороны в бинарном виде
- указатель на etcp_socket (сокеты открываются при запуске те что в конфиге и через них весь обмен в т.ч. исходящий трафик. к каждому узлу может быть только одно etcp подключение)
- хеш ip:port (32bit, crc32)
- указатель на инстанс etcp
etcp_socket:
socket
uasync *socket
записи etcp_channel при создании записываются в массив и сортируются по хешу ip:port для быстрого поиска.
etcp содержит:
- вместо unique id подключения - ID удалённого узла (которое передается в init request и init response)
- *etcp_first_channel
- очереди и прочее необходимое для работы
+40
View File
@@ -0,0 +1,40 @@
#ifndef ETCP_SOCKET_H
#define ETCP_SOCKET_H
#include "packet_buffer.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdint.h>
// Forward declarations
struct ETCP_CONN;
struct ETCP_CONNECTIONS;
typedef struct utun_instance utun_instance_t;
// Handle для одного соединения (peer)
typedef struct conn_handle {
struct ETCP_CONN* etcp;
struct ETCP_CONNECTIONS* conns;
} conn_handle_t;
// ETCP Socket - интегрированный UDP сокет с ETCP функциональностью
typedef struct etcp_socket {
int fd; // Файловый дескриптор UDP сокета
struct sockaddr_storage local_addr; // Локальный адрес
socklen_t local_addr_len; // Длина локального адреса
struct ETCP_CONN* etcp; // Родительский ETCP instance
uint32_t socket_id; // Уникальный ID сокета
// Callback для получения данных
void (*recv_callback)(struct etcp_socket* sock, packet_buffer_t* pkt, void* user_data);
void* recv_user_data; // User data для callback
} etcp_socket_t;
// Callback для получения данных
typedef void (*etcp_socket_recv_cb)(struct etcp_socket* sock,
packet_buffer_t* pkt,
void* user_data);
#endif // ETCP_SOCKET_H
-56
View File
@@ -9,62 +9,6 @@
// Предварительное объявление для отложенного возобновления
static void queue_resume_timeout_cb(void* arg);
// ==================== Управление пулами памяти ====================
void memory_pool_init(memory_pool_t* pool, size_t object_size) {
pool->object_size = object_size;
pool->free_count = 0;
pool->allocations = 0;
pool->reuse_count = 0;
memset(pool->free_list, 0, sizeof(pool->free_list));
}
void* memory_pool_alloc(memory_pool_t* pool) {
pool->allocations++;
// Если есть свободные объекты в пуле, использовать их
if (pool->free_count > 0) {
pool->free_count--;
pool->reuse_count++;
void* obj = pool->free_list[pool->free_count];
pool->free_list[pool->free_count] = NULL;
return obj;
}
// Иначе выделить через malloc
return malloc(pool->object_size);
}
void memory_pool_free(memory_pool_t* pool, void* obj) {
if (!obj) return;
// Если пул не заполнен, сохранить объект для повторного использования
if (pool->free_count < LL_QUEUE_POOL_SIZE) {
pool->free_list[pool->free_count] = obj;
pool->free_count++;
return;
}
// Иначе освободить через free
free(obj);
}
void memory_pool_get_stats(memory_pool_t* pool, size_t* allocations, size_t* reuse_count) {
if (allocations) *allocations = pool->allocations;
if (reuse_count) *reuse_count = pool->reuse_count;
}
void memory_pool_destroy(memory_pool_t* pool) {
// Освободить все объекты в пуле
for (int i = 0; i < pool->free_count; i++) {
if (pool->free_list[i]) {
free(pool->free_list[i]);
pool->free_list[i] = NULL;
}
}
pool->free_count = 0;
}
// Проверить и запустить ожидающие коллбэки
static void check_waiters(ll_queue_t* q) {
if (!q || !q->waiters) return;
+4 -32
View File
@@ -2,6 +2,7 @@
#define LL_QUEUE_H
#include <stddef.h> // для size_t
#include "memory_pool.h" // для memory_pool_t
// Предварительные объявления
typedef struct ll_queue ll_queue_t;
@@ -19,17 +20,6 @@ struct ll_entry {
int ref_count; // Счетчик ссылок для предотвращения double-free
};
// Структура пула памяти для оптимизации аллокаций
#define LL_QUEUE_POOL_SIZE 64 // Размер пула для объектов одинакового размера
typedef struct memory_pool {
void* free_list[LL_QUEUE_POOL_SIZE]; // Список свободных блоков
int free_count; // Количество свободных блоков
size_t object_size; // Размер объектов в пуле
size_t allocations; // Статистика: всего аллокаций
size_t reuse_count; // Статистика: повторное использование
} memory_pool_t;
// Структура условия ожидания (waiter)
struct queue_waiter {
int max_packets; // Максимальное количество пакетов
@@ -61,27 +51,9 @@ struct ll_queue {
// Пулы памяти для оптимизации аллокаций
memory_pool_t waiter_pool; // Пул для структур queue_waiter_t
memory_pool_t entry_pool; // Пул для структур ll_entry_t (фиксированный размер)
int use_pools; // Флаг использования пулов (включается при частых аллокациях)
};
// ==================== Управление пулами памяти ====================
// Инициализировать пул памяти
void memory_pool_init(memory_pool_t* pool, size_t object_size);
// Выделить объект из пула или из malloc
void* memory_pool_alloc(memory_pool_t* pool);
// Освободить объект в пул или в free
void memory_pool_free(memory_pool_t* pool, void* obj);
// Получить статистику пула
void memory_pool_get_stats(memory_pool_t* pool, size_t* allocations, size_t* reuse_count);
// Очистить пул памяти
void memory_pool_destroy(memory_pool_t* pool);
// ==================== Управление очередью ====================
// Создать новую пустую очередь
@@ -99,9 +71,9 @@ void queue_free(ll_queue_t* q);
// ==================== Конфигурация очереди ====================
// Установить функцию и аргумент коллбэка для очереди
// Коллбэк вызывается при добавлении элемента в пустую очередь (разрешенные коллбэки)
// обработчик должен обработать этот пакет и когда будет готов к приёму следующего - вызывает resume_callback. обработка строго по одному пакету.
// Установить функцию и аргумент коллбэка для автозабора из очереди
// Коллбэк вызывается когда в очереди есть элемент и разрешен коллбэк
// обработчик должен обработать этот пакет (может использовать асинхронное ожидание). Когда будет готов к приёму следующего - должен вызвать resume_callback. обработка строго по одному пакету.
void queue_set_callback(ll_queue_t* q, queue_callback_t cbk_fn, void* arg);
// Возобновить коллбэки после обработки элемента переданного в коллбэке (тянуть дополнительные элементы из очереди не предусмотернные api нельзя).
+62
View File
@@ -0,0 +1,62 @@
#include "memory_pool.h"
#include <stdlib.h>
#include <string.h>
// Инициализировать пул памяти
void memory_pool_init(memory_pool_t* pool, size_t object_size) {
pool->object_size = object_size;
pool->free_count = 0;
pool->allocations = 0;
pool->reuse_count = 0;
memset(pool->free_list, 0, sizeof(pool->free_list));
}
// Выделить объект из пула или из malloc
void* memory_pool_alloc(memory_pool_t* pool) {
pool->allocations++;
// Если есть свободные объекты в пуле, использовать их
if (pool->free_count > 0) {
pool->free_count--;
pool->reuse_count++;
void* obj = pool->free_list[pool->free_count];
pool->free_list[pool->free_count] = NULL;
return obj;
}
// Иначе выделить через malloc
return malloc(pool->object_size);
}
// Освободить объект в пул или в free
void memory_pool_free(memory_pool_t* pool, void* obj) {
if (!obj) return;
// Если пул не заполнен, сохранить объект для повторного использования
if (pool->free_count < MEMORY_POOL_SIZE) {
pool->free_list[pool->free_count] = obj;
pool->free_count++;
return;
}
// Иначе освободить через free
free(obj);
}
// Получить статистику пула
void memory_pool_get_stats(memory_pool_t* pool, size_t* allocations, size_t* reuse_count) {
if (allocations) *allocations = pool->allocations;
if (reuse_count) *reuse_count = pool->reuse_count;
}
// Очистить пул памяти
void memory_pool_destroy(memory_pool_t* pool) {
// Освободить все объекты в пуле
for (int i = 0; i < pool->free_count; i++) {
if (pool->free_list[i]) {
free(pool->free_list[i]);
pool->free_list[i] = NULL;
}
}
pool->free_count = 0;
}
+33
View File
@@ -0,0 +1,33 @@
#ifndef MEMORY_POOL_H
#define MEMORY_POOL_H
#include <stddef.h> // для size_t
// Размер пула для объектов одинакового размера
#define MEMORY_POOL_SIZE 64
// Структура пула памяти для оптимизации аллокаций
typedef struct memory_pool {
void* free_list[MEMORY_POOL_SIZE]; // Список свободных блоков
int free_count; // Количество свободных блоков
size_t object_size; // Размер объектов в пуле
size_t allocations; // Статистика: всего аллокаций
size_t reuse_count; // Статистика: повторное использование
} memory_pool_t;
// Инициализировать пул памяти
void memory_pool_init(memory_pool_t* pool, size_t object_size);
// Выделить объект из пула или из malloc
void* memory_pool_alloc(memory_pool_t* pool);
// Освободить объект в пул или в free
void memory_pool_free(memory_pool_t* pool, void* obj);
// Получить статистику пула
void memory_pool_get_stats(memory_pool_t* pool, size_t* allocations, size_t* reuse_count);
// Очистить пул памяти
void memory_pool_destroy(memory_pool_t* pool);
#endif // MEMORY_POOL_H
+76
View File
@@ -0,0 +1,76 @@
#ifndef PACKET_BUFFER_H
#define PACKET_BUFFER_H
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
// Стадии обработки пакета
typedef enum {
PACKET_STAGE_RX_SOCKET, // Получен из сокета (зашифрован)
PACKET_STAGE_RX_DECRYPTED, // Расшифрован (plain-text)
PACKET_STAGE_RX_ETCP, // Обработан ETCP (собран фрагмент)
PACKET_STAGE_RX_APP, // Передан в приложение
PACKET_STAGE_TX_APP, // Из приложения на отправку
PACKET_STAGE_TX_ETCP, // Обработан ETCP (фрагментирован)
PACKET_STAGE_TX_ENCRYPTED // Зашифрован, готов к отправке
} packet_stage_t;
// Флаги пакета
#define PACKET_FLAG_ENCRYPTED (1 << 0)
#define PACKET_FLAG_FRAGMENTED (1 << 1)
#define PACKET_FLAG_RETRANSMIT (1 << 2)
#define PACKET_FLAG_CONTROL (1 << 3) // Служебный пакет (RST, INIT)
// Размеры
#define PACKET_METADATA_SIZE 64
#define PACKET_DATA_SIZE 1536
#define PACKET_TOTAL_SIZE 1600
#define PACKET_POOL_SIZE 64
// Основная структура пакета
typedef struct packet_buffer {
// Метаданные (64 байта)
struct {
struct ETCP_SOCKET* s; // Сокет через который получен/ планируется отправить
struct etcp_channel* channel; // Канал ETCP (NULL до поиска)
struct sockaddr_storage remote_addr;// Адрес удаленного endpoint
uint64_t timestamp_us; // Время создания пакета
uint16_t data_len; // Длина данных в буфере
uint8_t stage; // Стадия обработки пакета
} metadata;
struct packet_buffer* next_free; // Для пула - используется только когда пакет свободен
uint8_t data[0]; // Данные (variable size)
} packet_buffer_t;
// Инлайн функции для удобного доступа
// Доступ к данным
static inline uint8_t* packet_data(packet_buffer_t* pkt) {
return pkt->data;
}
static inline struct sockaddr* packet_remote_addr(packet_buffer_t* pkt) {
return (struct sockaddr*)&pkt->metadata.remote_addr;
}
static inline uint16_t packet_remote_port(const packet_buffer_t* pkt) {
if (pkt->metadata.remote_addr.ss_family == AF_INET) {
return ((struct sockaddr_in*)&pkt->metadata.remote_addr)->sin_port;
}
return 0;
}
// Инициализация
static inline void packet_init(packet_buffer_t* pkt) {
memset(&pkt->metadata, 0, sizeof(pkt->metadata));
pkt->metadata.timestamp_us = 0; // TODO: get current time
pkt->metadata.stage = PACKET_STAGE_RX_SOCKET;
}
#endif // PACKET_BUFFER_H
+85
View File
@@ -0,0 +1,85 @@
#include "packet_pool.h"
#include "packet_buffer.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void packet_pool_init(packet_pool_t* pool) {
pool->free_count = 0;
pool->allocations = 0;
pool->reuse_count = 0;
pool->overflow_count = 0;
memset(pool->free_list, 0, sizeof(pool->free_list));
// Предварительно аллоцируем пакеты
for (int i = 0; i < PACKET_POOL_SIZE / 2; i++) {
packet_buffer_t* pkt = calloc(1, PACKET_TOTAL_SIZE);
if (pkt) {
pool->free_list[pool->free_count++] = pkt;
}
}
}
packet_buffer_t* packet_pool_get(packet_pool_t* pool) {
pool->allocations++;
// Если есть свободные пакеты в пуле, использовать их
if (pool->free_count > 0) {
pool->reuse_count++;
pool->free_count--;
packet_buffer_t* pkt = pool->free_list[pool->free_count];
pool->free_list[pool->free_count] = NULL;
// Инициализировать метаданные
packet_init(pkt);
pkt->next_free = NULL;
return pkt;
}
// Иначе выделить через malloc
pool->overflow_count++;
packet_buffer_t* pkt = calloc(1, PACKET_TOTAL_SIZE);
if (pkt) {
packet_init(pkt);
}
return pkt;
}
void packet_pool_put(packet_pool_t* pool, packet_buffer_t* pkt) {
if (!pkt) return;
// Если пул не заполнен, сохранить пакет для повторного использования
if (pool->free_count < PACKET_POOL_SIZE) {
pool->free_list[pool->free_count] = pkt;
pool->free_count++;
return;
}
// Иначе освободить через free (переполнение)
free(pkt);
}
void packet_pool_get_stats(const packet_pool_t* pool,
size_t* allocations,
size_t* reuse_count,
size_t* overflow_count) {
if (allocations) *allocations = pool->allocations;
if (reuse_count) *reuse_count = pool->reuse_count;
if (overflow_count) *overflow_count = pool->overflow_count;
}
void packet_pool_destroy(packet_pool_t* pool) {
// Освободить все пакеты в пуле
for (int i = 0; i < pool->free_count; i++) {
if (pool->free_list[i]) {
free(pool->free_list[i]);
pool->free_list[i] = NULL;
}
}
pool->free_count = 0;
pool->allocations = 0;
pool->reuse_count = 0;
pool->overflow_count = 0;
}
+35
View File
@@ -0,0 +1,35 @@
#ifndef PACKET_POOL_H
#define PACKET_POOL_H
#include "packet_buffer.h"
// Размер пула пакетов
#define PACKET_POOL_SIZE 64
typedef struct packet_pool {
packet_buffer_t* free_list[PACKET_POOL_SIZE]; // Список свободных пакетов
int free_count; // Количество свободных пакетов
size_t allocations; // Всего аллокаций
size_t reuse_count; // Повторное использование
size_t overflow_count; // Переполнения пула
} packet_pool_t;
// Инициализация пула
void packet_pool_init(packet_pool_t* pool);
// Получить пакет из пула
packet_buffer_t* packet_pool_get(packet_pool_t* pool);
// Вернуть пакет в пул
void packet_pool_put(packet_pool_t* pool, packet_buffer_t* pkt);
// Получить статистику
void packet_pool_get_stats(const packet_pool_t* pool,
size_t* allocations,
size_t* reuse_count,
size_t* overflow_count);
// Очистить пул и освободить всю память
void packet_pool_destroy(packet_pool_t* pool);
#endif // PACKET_POOL_H
+46 -112
View File
@@ -1,22 +1,17 @@
#include "pkt_normalizer.h"
#include "u_async.h"
#include "settings.h"
#include "../u_async/u_async.h"
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
static void packer_handler(ll_queue_t* q, ll_entry_t* unused, void* arg);
static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg);
static void send_buf(pn_struct* pn);
static int get_header(uint8_t* header, size_t L);
/* Calculate maximum regular block size that fits in max_fragment_size */
pn_struct* pkt_normalizer_init(uasync_t* ua, int is_packer) {
/* Calculate fragment size from mtu */
pn_struct* pkt_normalizer_init(uasync_t* ua, int is_packer, int mtu) {
pn_struct* pn = malloc(sizeof(pn_struct));
if (!pn) return NULL;
pn->ua = ua;
pn->input = queue_new(ua);
if (!pn->input) {
@@ -30,9 +25,11 @@ pn_struct* pkt_normalizer_init(uasync_t* ua, int is_packer) {
return NULL;
}
pn->is_packer = is_packer;
if (is_packer) {
pn->u.packer.cap = settings.max_fragment_size;
// 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);
@@ -49,56 +46,46 @@ pn_struct* pkt_normalizer_init(uasync_t* ua, int is_packer) {
pn->u.unpacker.total_len = 0;
pn->u.unpacker.cap = 0;
pn->u.unpacker.error_count = 0;
pn->u.unpacker.in_fragment = 0;
pn->u.unpacker.service_buf = NULL;
pn->u.unpacker.service_len = 0;
pn->u.unpacker.service_cap = 0;
pn->u.unpacker.service_type = 0;
pn->u.unpacker.in_service = 0;
queue_set_callback(pn->input, unpacker_handler, pn);
}
return pn;
}
void pkt_normalizer_deinit(pn_struct* pn) {
if (!pn) return;
queue_free(pn->input);
queue_free(pn->output);
if (pn->is_packer) {
free(pn->u.packer.buf);
void pkt_normalizer_deinit(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);
}
pkt_normalizer_pair* pkt_normalizer_pair_init(uasync_t* ua) {
free(pn);
}
pkt_normalizer_pair* pkt_normalizer_pair_init(uasync_t* ua, int mtu) {
pkt_normalizer_pair* pair = malloc(sizeof(pkt_normalizer_pair));
if (!pair) return NULL;
pair->packer = pkt_normalizer_init(ua, 1);
pair->packer = pkt_normalizer_init(ua, 1, mtu);
if (!pair->packer) {
free(pair);
return NULL;
}
pair->unpacker = pkt_normalizer_init(ua, 0);
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(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) {
}
void pkt_normalizer_pair_deinit(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;
@@ -110,8 +97,7 @@ static int get_header(uint8_t* header, size_t L) {
header[1] = (uint8_t)(L & 0xFF);
return 2;
}
}
}
/* Сбросить состояние сборки фрагментов */
static void reset_fragment_state(pn_struct* pn) {
if (!pn->is_packer) {
@@ -120,10 +106,7 @@ static void reset_fragment_state(pn_struct* pn) {
pn->u.unpacker.in_fragment = 0;
}
}
/* Таймаут для сборки фрагментов */
static void send_buf(pn_struct* pn) {
if (pn->u.packer.len == 0) return;
size_t payload_len = pn->u.packer.len;
@@ -135,28 +118,20 @@ static void send_buf(pn_struct* pn) {
queue_entry_put(pn->output, out);
pn->u.packer.len = 0;
}
static void packer_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
(void)unused;
pn_struct* pn = arg;
size_t max = (size_t)settings.max_fragment_size;
size_t max = (size_t)1400;
ll_entry_t* 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) {
@@ -172,7 +147,6 @@ static void packer_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
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
@@ -210,30 +184,24 @@ static void packer_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
// Не удалось отправить как обычный блок - разбиваем на 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++;
@@ -261,7 +229,6 @@ static void packer_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
pos += chunk;
remaining -= chunk;
fragment_count++;
// Обновляем оставшиеся данные для обычного блока
// (цикл продолжит обработку на следующей итерации)
continue;
@@ -297,25 +264,19 @@ static void packer_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
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(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(pn_struct* pn) {
if (!pn || pn->is_packer) return;
if (pn->u.unpacker.in_service) {
// Deliver pending service packet
if (pn->service_callback) {
@@ -331,10 +292,8 @@ void pkt_normalizer_reset_service_state(pn_struct* pn) {
pn->u.unpacker.in_service = 0;
}
}
void pkt_normalizer_reset_state(pn_struct* pn) {
if (!pn) return;
if (pn->is_packer) {
// Flush packer buffer
if (pn->u.packer.len > 0) {
@@ -347,26 +306,20 @@ void pkt_normalizer_reset_state(pn_struct* pn) {
pkt_normalizer_reset_service_state(pn);
}
}
int pkt_normalizer_send_service(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)settings.max_fragment_size;
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+тип)
@@ -383,13 +336,10 @@ int pkt_normalizer_send_service(pn_struct* pn, uint8_t type, const void* data, s
chunk = max_cont_data;
service_header = 0xFD;
}
if (chunk == 0) break;
size_t payload_len = 1 + chunk + (pos == 0 ? 1 : 0); // +1 байт типа для первого пакета
ll_entry_t* 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;
@@ -398,16 +348,12 @@ int pkt_normalizer_send_service(pn_struct* pn, uint8_t type, const void* data, s
*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 pn_struct* pn) {
if (!pn) return 0;
if (pn->is_packer) {
@@ -415,7 +361,6 @@ int pkt_normalizer_get_error_count(const pn_struct* pn) {
}
return pn->u.unpacker.error_count;
}
void pkt_normalizer_reset_error_count(pn_struct* pn) {
if (!pn) return;
if (pn->is_packer) {
@@ -424,32 +369,29 @@ void pkt_normalizer_reset_error_count(pn_struct* pn) {
pn->u.unpacker.error_count = 0;
}
}
void pkt_normalizer_flush(pn_struct* pn) {
if (!pn || !pn->is_packer) return;
if (pn->u.packer.len > 0) {
send_buf(pn);
}
}
static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
(void)unused;
pn_struct* pn = arg;
while (queue_entry_count(q) > 0) {
ll_entry_t* 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;
ll_entry_t* 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) {
@@ -495,7 +437,6 @@ static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
}
continue;
}
if (byte == 0xFE) {
/* Продолжение фрагментированного пакета */
if (!pn->u.unpacker.in_fragment) {
@@ -530,7 +471,6 @@ static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
}
continue;
}
if (byte == 0xFC || byte == 0xFD) {
/* Service packet */
if (byte == 0xFC) {
@@ -563,7 +503,6 @@ static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
goto err;
}
}
/* Read data */
size_t data_len = cg_len - cg_pos;
if (data_len > 0) {
@@ -594,7 +533,6 @@ static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
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 */
@@ -610,7 +548,6 @@ static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
goto err;
}
}
/* Обычная запись (не фрагмент) */
size_t L;
if (byte <= 0xEF) {
@@ -623,9 +560,7 @@ static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
/* Недопустимый байт */
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;
@@ -638,7 +573,6 @@ static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
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) {
@@ -662,9 +596,9 @@ static void unpacker_handler(ll_queue_t* q, ll_entry_t* unused, void* arg) {
}
cg_pos += L;
}
}
err:
queue_entry_free(entry);
}
queue_resume_callback(q);
}
err:
queue_entry_free(entry);
}
queue_resume_callback(q);
}
+8 -5
View File
@@ -3,13 +3,16 @@
#define PKT_NORMALIZER_H
#include "ll_queue.h"
#include "u_async.h"
#include "../u_async/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.*/
typedef struct pn_struct pn_struct;
typedef struct pkt_normalizer_pair pkt_normalizer_pair;
@@ -49,10 +52,10 @@ struct pn_struct {
void* service_callback_user_data;
};
pn_struct* pkt_normalizer_init(uasync_t* ua, int is_packer); // 1 for packer, 0 for unpacker
void pkt_normalizer_deinit(pn_struct* pn);
pkt_normalizer_pair* pkt_normalizer_pair_init(uasync_t* ua);
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(pn_struct* pn);
pkt_normalizer_pair* pkt_normalizer_pair_init(uasync_t* ua, int mtu);
void pkt_normalizer_pair_deinit(pkt_normalizer_pair* pair);
/* Error handling */
+18 -7
View File
@@ -4,7 +4,7 @@
*/
#include "routing.h"
#include "connection.h"
#include "etcp_connections.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -15,6 +15,22 @@
#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 = addr.s_addr;
*prefix_length = (uint8_t)prefix;
return 0;
}
static int compare_routes(const void *a, const void *b) {
const route_entry_t *route_a = (const route_entry_t *)a;
const route_entry_t *route_b = (const route_entry_t *)b;
@@ -245,7 +261,7 @@ bool routing_validate_route(routing_table_t *table, uint32_t network, uint8_t pr
range_count = table->local_subnet_count;
break;
default:
table->stats.miss_count++;
table->stats.routes_lookup_misses++;
return false;
}
@@ -389,11 +405,6 @@ const char *route_type_to_string(route_type_t type) {
}
}
void routing_get_stats(const routing_table_t *table, routing_stats_t *stats) {
if (!table || !stats) return;
*stats = table->stats;
}
char *ip_to_string(uint32_t ip, char *buffer) {
if (!buffer) return NULL;
+53 -136
View File
@@ -1,170 +1,87 @@
/**
* @file routing.h
* @brief Улучшенная система маршрутизации с поддержкой пропускной способности и типов маршрутов
* @details Поддерживает статические/динамические маршруты с метриками и валидацией
*/
#ifndef ROUTING_H
#define ROUTING_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "connection.h"
#include "control_socket.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_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_FLAG_ACTIVE = (1 << 0),
ROUTE_FLAG_VALIDATED = (1 << 1),
ROUTE_FLAG_ADVERTISED = (1 << 2),
ROUTE_FLAG_LEARNED = (1 << 3)
} route_flags_t;
// Расширенные метрики маршрута
typedef struct {
uint32_t bandwidth_kbps; // Доступная (минимальная по всем узлам) пропускная способность до узла по этому маршруту, кбит/с
uint16_t packet_loss_rate; // Процент потерь пакетов (0–10000 = 0.00–100.00%)
uint16_t latency_ms; // Задержка в миллисекундах (суммарная до узла, не вулючает задержку до next hop)
uint8_t hop_count; // Количество хопов до цели (не включая next hop)
uint64_t last_updated; // Время последнего обновления (в timebase 0.1ms)
uint32_t bandwidth_kbps;
uint16_t packet_loss_rate;
uint16_t latency_ms;
uint8_t hop_count;
uint64_t last_updated;
} route_metrics_t;
// Расширенная запись маршрута
typedef struct {
uint32_t network; // Адрес сети (в сетевом порядке байтов)
uint8_t prefix_length; // Длина префикса (0–32)
uint32_t next_hop_ip; // IP-адрес следующего хопа (узла который анонсировал маршрут)
conn_handle_t* next_hop; // Указатель на соединение со следующим хопом
route_type_t type; // Тип маршрута
uint8_t flags; // Флаги маршрута
route_metrics_t metrics; // Метрики маршрута
uint64_t created_time; // Время создания записи
uint64_t last_update; // Время последнего обновления
uint64_t last_used; // Время последнего использования
uint32_t network;
uint8_t prefix_length;
uint32_t next_hop_ip;
struct ETCP_CONNECTIONS* next_hop;
route_type_t type;
uint8_t flags;
route_metrics_t metrics;
uint64_t created_time;
uint64_t last_update;
uint64_t last_used;
} route_entry_t;
// Расширенная таблица маршрутизации (элементы сортированы по IP), может содержать дублирующие маршруты по альтернативным каналам.
// Таблица маршрутизации
typedef struct {
route_entry_t *entries; // Массив записей маршрутов
size_t count; // Количество активных записей
size_t capacity; // Выделенная ёмкость массива (расширение x2 при нехватке места)
// Диапазоны для валидации подсетей
uint32_t *dynamic_subnets; // Разрешённые диапазоны динамических маршрутов (анонс не принимается если вне этой таблицы)
size_t dynamic_subnet_count;
// Локальные подсети, которые будут анонсироваться
uint32_t *local_subnets;
size_t local_subnet_count;
routing_stats_t stats; // Statistics counters
route_entry_t *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;
} routing_table_t;
/**
* @brief Создаёт улучшенную таблицу маршрутизации
* @return указатель на новую таблицу или NULL при ошибке
*/
routing_table_t *routing_table_create(void);
/**
* @brief Уничтожает таблицу маршрутизации
*/
routing_table_t* routing_table_create(void);
void routing_table_destroy(routing_table_t *table);
/**
* @brief Получить статистику таблицы маршрутизации
* @param table таблица маршрутизации
* @param stats указатель на структуру для заполнения статистики
*/
void routing_get_stats(const routing_table_t *table, routing_stats_t *stats);
/**
* @brief Добавляет маршрут в таблицу
*
* @param table таблица маршрутизации
* @param entry структура маршрута для вставки
* @return true при успехе, false при ошибке
*/
bool routing_table_insert(routing_table_t *table, const route_entry_t *entry);
/**
* @brief Удаляет маршрут из таблицы
*
* @param table таблица маршрутизации
* @param network адрес сети
* @param prefix_length длина префикса
* @param source_node_id идентификатор узла-источника
* @return true — маршрут удалён, false — не найден
*/
bool routing_table_delete(routing_table_t *table, uint32_t network, uint8_t prefix_length, uint32_t source_node_id);
/**
* @brief Ищет лучший маршрут до указанного адреса
*
* @param table таблица маршрутизации
* @param dest_ip целевой IP-адрес
* @param best_route [out] сюда будет записан лучший маршрут
* @return true — маршрут найден, false — нет подходящего маршрута
*/
bool routing_table_lookup(routing_table_t *table, uint32_t dest_ip, route_entry_t *best_route);
/**
* @brief Проверяет маршрут на соответствие разрешённым подсетям
*
* @param table таблица с диапазонами валидации
* @param network адрес сети
* @param prefix_length длина префикса
* @param route_type ожидаемый тип маршрута
* @return true — маршрут разрешён, false — запрещён
*/
bool routing_validate_route(routing_table_t *table, uint32_t network, uint8_t prefix_length, route_type_t route_type);
/**
* @brief Добавляет диапазон подсети для динамической валидации
*/
bool routing_add_dynamic_subnet(routing_table_t *table, uint32_t network, uint8_t prefix_length);
/**
* @brief Добавляет локальную подсеть, которая будет анонсироваться соседям
*/
bool routing_add_local_subnet(routing_table_t *table, uint32_t network, uint8_t prefix_length);
/**
* @brief Возвращает все маршруты для указанной сети/префикса
*
* @param routes [out] массив указателей (выделяется внутри, нужно освободить)
*/
bool routing_get_all_routes(const routing_table_t *table, uint32_t network, uint8_t prefix_length,
route_entry_t **routes, size_t *count);
/**
* @brief Выводит содержимое таблицы маршрутизации (для отладки)
*/
bool routing_get_all_routes(const routing_table_t *table, uint32_t network, uint8_t prefix_length, route_entry_t **routes, size_t *count);
void routing_table_print(const routing_table_t *table);
const char* route_type_to_string(route_type_t type);
char* ip_to_string(uint32_t ip, char *buffer);
/**
* @brief Преобразует тип маршрута в строку
* @return текстовое представление типа
*/
const char *route_type_to_string(route_type_t type);
/**
* @brief Преобразует IP-адрес в строку (для отладки)
*
* @param ip адрес в сетевом порядке байтов
* @param buffer буфер минимум 16 байт
* @return указатель на заполненный буфер
*/
char *ip_to_string(uint32_t ip, char *buffer);
#endif // ROUTING_H
#endif // ROUTING_H
+4 -38
View File
@@ -1,6 +1,6 @@
/* sc_lib.c - Secure Channel library implementation using TinyCrypt */
#include "sc_lib.h"
#include "secure_channel.h"
#include <tinycrypt/ecc.h>
#include <tinycrypt/ecc_dh.h>
#include <tinycrypt/aes.h>
@@ -12,45 +12,11 @@
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#include "crc32.h"
#include <fcntl.h>
static const struct uECC_Curve_t *curve = NULL;
/* CRC32 implementation (IEEE 802.3 polynomial 0xEDB88320) */
static uint32_t crc32_table[256];
static int crc32_table_initialized = 0;
static void init_crc32_table(void) {
if (crc32_table_initialized) return;
uint32_t polynomial = 0xEDB88320;
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) ^ polynomial;
} else {
crc >>= 1;
}
}
crc32_table[i] = crc;
}
crc32_table_initialized = 1;
}
static uint32_t crc32(const uint8_t *data, size_t len) {
if (!crc32_table_initialized) {
init_crc32_table();
}
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < len; i++) {
uint8_t byte = data[i];
uint8_t table_index = (crc ^ byte) & 0xFF;
crc = (crc >> 8) ^ crc32_table[table_index];
}
return crc ^ 0xFFFFFFFF;
}
static int sc_rng(uint8_t *dest, unsigned size)
{
@@ -220,7 +186,7 @@ sc_status_t sc_encrypt(sc_context_t *ctx,
/* Добавляем CRC32 к данным */
memcpy(plaintext_with_crc, plaintext, plaintext_len);
uint32_t crc = crc32(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;
@@ -304,7 +270,7 @@ sc_status_t sc_decrypt(sc_context_t *ctx,
/* Проверяем CRC32 */
size_t data_len = total_plaintext_len - SC_CRC32_SIZE;
uint32_t expected_crc = crc32(plaintext_with_crc, data_len);
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) |
File renamed without changes.
-6
View File
@@ -1,6 +0,0 @@
// settings.c
#include "settings.h"
settings_t settings = {
.max_fragment_size = 1400
};
-11
View File
@@ -1,11 +0,0 @@
// settings.h
#ifndef SETTINGS_H
#define SETTINGS_H
typedef struct {
int max_fragment_size;
} settings_t;
extern settings_t settings;
#endif // SETTINGS_H
+13 -1
View File
@@ -235,4 +235,16 @@ int tun_get_config(const char *ifname, tun_config_t *config) {
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;
}
+4 -4
View File
@@ -2,12 +2,12 @@
#define _DEFAULT_SOURCE
#define _POSIX_C_SOURCE 200809L
#include "config_parser.h"
#include "connection.h"
#include "etcp_connections.h"
#include "tun_if.h"
#include "sc_lib.h"
#include "secure_channel.h"
#include "routing.h"
#include "control_socket.h"
#include "utun_state.h"
#include "u_async.h"
#include <stdio.h>
+52
View File
@@ -0,0 +1,52 @@
#include "utun_instance.h"
#include "routing.h"
#include "etcp_connections.h"
#include "config_parser.h"
#include "../u_async/u_async.h"
conn_handle_t* route_get_next_hop(routing_table_t* rt, uint32_t dest_ip) {
route_entry_t route = {0};
if (!rt || !routing_table_lookup(rt, dest_ip, &route)) return NULL;
return (conn_handle_t*)route.next_hop;
}
static int add_default_route(utun_instance_t *instance) {
if (!instance || !instance->routing_table || instance->connection_count == 0) return -1;
route_entry_t default_route = {0};
default_route.network = 0; // 0.0.0.0
default_route.prefix_length = 0;
default_route.next_hop_ip = 0;
default_route.next_hop = (struct ETCP_CONNECTIONS*)instance->connections[0]->conns;
default_route.type = ROUTE_TYPE_STATIC;
default_route.flags = ROUTE_FLAG_ACTIVE;
default_route.created_time = 0;
return routing_table_insert((routing_table_t*)instance->routing_table, &default_route) ? 0 : -1;
}
void conn_destroy(conn_handle_t* conn);
int conn_send(conn_handle_t* conn, const uint8_t* data, size_t len);
uint32_t get_dest_ip(const uint8_t *packet, size_t len);
int parse_subnet(const char* subnet_str, uint32_t* network, uint8_t* prefix_len);
conn_handle_t* route_find_connection(utun_instance_t* instance, uint32_t dest_ip) {
if (!instance || !instance->routing_table) return NULL;
route_entry_t route = {0};
if (!routing_table_lookup((routing_table_t*)instance->routing_table, dest_ip, &route)) {
return NULL;
}
struct ETCP_CONNECTIONS* conns = route.next_hop;
if (!conns) return NULL;
// Find matching connection handle
for (int i = 0; i < instance->connection_count; i++) {
if (instance->connections[i] && instance->connections[i]->conns == conns) {
return instance->connections[i];
}
}
return NULL;
}
+184
View File
@@ -0,0 +1,184 @@
// utun_instance.c - Root instance implementation
#include "utun_instance.h"
#include "config_parser.h"
#include "tun_if.h"
#include "routing.h"
#include "etcp_connections.h"
#include "../u_async/u_async.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
// Global instance for signal handlers
static utun_instance_t *g_instance = NULL;
// Create and initialize root instance
utun_instance_t* utun_instance_create(const char *config_file, const char *log_file) {
utun_instance_t *instance = calloc(1, sizeof(utun_instance_t));
if (!instance) return NULL;
// Initialize basic fields
instance->running = 0;
instance->log_fp = NULL;
// Load configuration
instance->config = parse_config(config_file);
if (!instance->config) {
fprintf(stderr, "Failed to load config from %s\n", config_file);
free(instance);
return NULL;
}
// Open log file
if (log_file) {
instance->log_fp = fopen(log_file, "a");
if (!instance->log_fp) {
fprintf(stderr, "Failed to open log file %s: %s\n", log_file, strerror(errno));
}
}
// Create uasync instance
instance->ua = uasync_create();
if (!instance->ua) {
fprintf(stderr, "Failed to create uasync instance\n");
utun_instance_destroy(instance);
return NULL;
}
// uasync has built-in wakeup mechanism, no need for manual pipe
// Set global instance
utun_instance_set(instance);
return instance;
}
// Destroy instance and cleanup resources
void utun_instance_destroy(utun_instance_t *instance) {
if (!instance) return;
// Stop running
instance->running = 0;
// Cleanup components
if (instance->ua) {
uasync_destroy(instance->ua);
}
if (instance->routing_table) {
routing_table_destroy(instance->routing_table);
}
// Cleanup connections
if (instance->connections) {
for (int i = 0; i < instance->connection_count; i++) {
if (instance->connections[i]) {
conn_destroy(instance->connections[i]);
}
}
free(instance->connections);
}
// 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);
}
// Initialize all components
int utun_instance_init(utun_instance_t *instance) {
if (!instance || !instance->config) return -1;
// Initialize TUN device
if (tun_create(&instance->tun) < 0) {
fprintf(stderr, "Failed to create TUN device\n");
return -1;
}
// Configure TUN device
if (tun_set_ip(instance->tun.ifname, instance->config->global.tun_ip) < 0) {
fprintf(stderr, "Failed to set TUN IP\n");
tun_close(&instance->tun);
return -1;
}
if (instance->config->global.mtu > 0) {
if (tun_set_mtu(instance->tun.ifname, instance->config->global.mtu) < 0) {
fprintf(stderr, "Failed to set TUN MTU\n");
tun_close(&instance->tun);
return -1;
}
}
if (tun_set_up(instance->tun.ifname) < 0) {
fprintf(stderr, "Failed to bring up TUN interface\n");
tun_close(&instance->tun);
return -1;
}
// Create routing table
instance->routing_table = routing_table_create();
if (!instance->routing_table) {
fprintf(stderr, "Failed to create routing table\n");
return -1;
}
// Subnets configuration will be added later from connection configs
// For now just return success
return 0;
}
// Start main event loop
int utun_instance_run(utun_instance_t *instance) {
if (!instance || !instance->ua) return -1;
instance->running = 1;
// Run main loop
while (instance->running) {
uasync_poll(instance->ua, 1000); // 100ms timeout
}
return 0;
}
// Stop instance
void utun_instance_stop(utun_instance_t *instance) {
if (!instance) return;
instance->running = 0;
// Wakeup main loop using built-in uasync wakeup
if (instance->ua) {
uasync_wakeup(instance->ua);
}
}
// Global instance access
utun_instance_t* utun_instance_get(void) {
return g_instance;
}
void utun_instance_set(utun_instance_t *instance) {
g_instance = instance;
}
+46
View File
@@ -0,0 +1,46 @@
/**
* @file utun_instance.h
* @brief Main application state structure
* @details Contains all module instances and configuration
*/
#ifndef UTUN_INSTANCE_H
#define UTUN_INSTANCE_H
#include <stdio.h>
#include "tun_if.h"
#include "etcp_sockets.h"
#include "config_parser.h"
#include "u_async/u_async.h"
// Root instance structure - single point of access to all components
typedef struct utun_instance {
// Configuration (moved from utun_state)
utun_config_t *config;
// First listening socket for server connections
struct ETCP_SOCKET* first_listen_socket;
// Main components (from utun_state)
tun_config_t tun;
conn_handle_t **connections; // Array of connection handles
int connection_count;
void *routing_table; // routing_table_t* - void* to avoid circular dependency
int running;
uasync_t *ua;
FILE *log_fp;
} utun_instance_t;
// Functions for root instance management
utun_instance_t* utun_instance_create(const char *config_file, const char *log_file);
void utun_instance_destroy(utun_instance_t *instance);
int utun_instance_init(utun_instance_t *instance);
int utun_instance_run(utun_instance_t *instance);
void utun_instance_stop(utun_instance_t *instance);
// Global instance for signal handlers
utun_instance_t* utun_instance_get(void);
void utun_instance_set(utun_instance_t *instance);
#endif /* UTUN_INSTANCE_H */
+23
View File
@@ -0,0 +1,23 @@
// Add default route via first connection
static int add_default_route(utun_instance_t *instance) {
if (!instance || !instance->routing_table || instance->connection_count == 0) return -1;
route_entry_t default_route = {0};
default_route.network = 0; // 0.0.0.0
default_route.prefix_length = 0;
default_route.next_hop_ip = 0; // Unknown
default_route.next_hop = (struct ETCP_CONNECTIONS*)instance->connections[0]->conns;
default_route.type = ROUTE_TYPE_STATIC;
default_route.flags = ROUTE_FLAG_ACTIVE;
default_route.created_time = 0; // Not used
default_route.last_update = 0;
default_route.last_used = 0;
if (!routing_table_insert(instance->routing_table, &default_route)) {
fprintf(stderr, "Failed to add default route\n");
return -1;
}
printf("Added default route via first connection\n");
return 0;
}
-30
View File
@@ -1,30 +0,0 @@
/**
* @file utun_state.h
* @brief Main application state structure
* @details Contains all module instances and configuration
*/
#ifndef UTUN_STATE_H
#define UTUN_STATE_H
#include <stdio.h>
#include "tun_if.h"
#include "connection.h"
#include "routing.h"
#include "control_socket.h"
#include "config_parser.h"
// Main application state
typedef struct utun_state {
tun_config_t tun;
conn_handle_t **connections;
int connection_count;
routing_table_t *routing_table;
control_socket_t *control_socket;
int running;
uasync_t *ua;
utun_config_t *config;
FILE *log_fp;
} utun_state_t;
#endif /* UTUN_STATE_H */