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:
1 parent
4e544156fd
commit
2c58c008ce
52 files changed
+2488
-6138
No files matched your search
+276
-541
@@ -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, ¤t_server) != 0) {
|
||||
fprintf(stderr, "Failed to add server: %s\n", current_server.name);
|
||||
}
|
||||
}
|
||||
memset(¤t_server, 0, sizeof(current_server));
|
||||
in_server_section = 0;
|
||||
}
|
||||
if (in_client_section) {
|
||||
if (strlen(current_client.name) > 0) {
|
||||
if (add_client(config, ¤t_client) != 0) {
|
||||
fprintf(stderr, "Failed to add client: %s\n", current_client.name);
|
||||
}
|
||||
}
|
||||
memset(¤t_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, ¤t_conn_v2) != 0) {
|
||||
fprintf(stderr, "Failed to add connection v2\n");
|
||||
}
|
||||
}
|
||||
memset(¤t_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(¤t_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, ¤t_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, ¤t_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, ¤t_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;
|
||||
}
|
||||
@@ -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, ¤t_conn) != 0) {
|
||||
fprintf(stderr, "Failed to add connection: %s\n", current_conn.name);
|
||||
}
|
||||
}
|
||||
memset(¤t_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, ¤t_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
@@ -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
File diff suppressed because it is too large.
Load diff
@@ -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 */
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
+35
-226
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
Executable
+49
@@ -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
@@ -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
|
||||
- очереди и прочее необходимое для работы
|
||||
@@ -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
|
||||
@@ -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
@@ -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 нельзя).
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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.
@@ -1,6 +0,0 @@
|
||||
// settings.c
|
||||
#include "settings.h"
|
||||
|
||||
settings_t settings = {
|
||||
.max_fragment_size = 1400
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
Reference in new issue
Block a user