Browse Source

ETCP_CONNECTIONS: add encryption support with secure channel (AES-CCM), nonce, counters. Simplified architecture by removing socket wrappers and integrating uasync_add_socket. pubkey exchange moved to INIT packet only. Added statistics counters.

v2_dev
Evgeny 3 months ago
parent
commit
20e5401ec9
  1. 7
      changelog.txt
  2. 158
      lib/sha256.c
  3. 34
      lib/sha256.h
  4. 16
      src/config_parser.c
  5. 4
      src/config_parser.h
  6. 293
      src/config_updater.c
  7. 18
      src/config_updater.h
  8. 5
      src/etcp.h
  9. 105
      src/etcp_connections.c
  10. 36
      src/etcp_connections.h
  11. 12
      src/etcp_protocol.txt
  12. 11
      src/utun_instance.c
  13. 2
      src/utun_instance.h

7
changelog.txt

@ -213,3 +213,10 @@ Sun Jan 19 2026 14:19
Final: Build successful with refactored architecture
Sun Jan 19 2026 14:26
Refactoring complete - utun binary ready
2026-01-18 16:38:15
Добавлен config_updater.c/h - автоматическая генерация ключей и node_id при запуске
2026-01-18 17:17:40: secure_channel - переделана генерация nonce на SHA256(64bit urandom + 64bit counter + 32bit utime), счетчики расширены до 64bit
2026-01-18 17:22:28: sha256.c/h перемещены из src/ в lib/
2026-01-18 18:17:37: Упрощена структура ETCP_CONNECTIONS - встроено uasync_add_socket, удалены обертки socket-функций
2026-01-18 19:32:55: Встроено шифрование и дешифрование в etcp_link_send и etcp_input. Добавлены счетчики ошибок и статистика.
2026-01-18 19:37:40: Имплементация шифрования завершена. Добавлена поддержка secure channel с AES-CCM, nonce и счетчиками. pubkey в конце INIT-пакетов. Клиенты загружают peer_public_key из конфига.

158
lib/sha256.c

@ -0,0 +1,158 @@
/*********************************************************************
* Filename: sha256.c
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Implementation of the SHA-256 hashing algorithm.
SHA-256 is one of the three algorithms in the SHA2
specification. The others, SHA-384 and SHA-512, are not
offered in this implementation.
Algorithm specification can be found here:
* http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
This implementation uses little endian byte order.
*********************************************************************/
/*************************** HEADER FILES ***************************/
#include <stdlib.h>
#include <memory.h>
#include "sha256.h"
/****************************** MACROS ******************************/
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))
/**************************** VARIABLES *****************************/
static const WORD k[64] = {
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};
/*********************** FUNCTION DEFINITIONS ***********************/
void sha256_transform(SHA256_CTX *ctx, const BYTE data[])
{
WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64];
for (i = 0, j = 0; i < 16; ++i, j += 4)
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]);
for ( ; i < 64; ++i)
m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16];
a = ctx->state[0];
b = ctx->state[1];
c = ctx->state[2];
d = ctx->state[3];
e = ctx->state[4];
f = ctx->state[5];
g = ctx->state[6];
h = ctx->state[7];
for (i = 0; i < 64; ++i) {
t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
t2 = EP0(a) + MAJ(a,b,c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
ctx->state[0] += a;
ctx->state[1] += b;
ctx->state[2] += c;
ctx->state[3] += d;
ctx->state[4] += e;
ctx->state[5] += f;
ctx->state[6] += g;
ctx->state[7] += h;
}
void sha256_init(SHA256_CTX *ctx)
{
ctx->datalen = 0;
ctx->bitlen = 0;
ctx->state[0] = 0x6a09e667;
ctx->state[1] = 0xbb67ae85;
ctx->state[2] = 0x3c6ef372;
ctx->state[3] = 0xa54ff53a;
ctx->state[4] = 0x510e527f;
ctx->state[5] = 0x9b05688c;
ctx->state[6] = 0x1f83d9ab;
ctx->state[7] = 0x5be0cd19;
}
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len)
{
WORD i;
for (i = 0; i < len; ++i) {
ctx->data[ctx->datalen] = data[i];
ctx->datalen++;
if (ctx->datalen == 64) {
sha256_transform(ctx, ctx->data);
ctx->bitlen += 512;
ctx->datalen = 0;
}
}
}
void sha256_final(SHA256_CTX *ctx, BYTE hash[])
{
WORD i;
i = ctx->datalen;
// Pad whatever data is left in the buffer.
if (ctx->datalen < 56) {
ctx->data[i++] = 0x80;
while (i < 56)
ctx->data[i++] = 0x00;
}
else {
ctx->data[i++] = 0x80;
while (i < 64)
ctx->data[i++] = 0x00;
sha256_transform(ctx, ctx->data);
memset(ctx->data, 0, 56);
}
// Append to the padding the total message's length in bits and transform.
ctx->bitlen += ctx->datalen * 8;
ctx->data[63] = ctx->bitlen;
ctx->data[62] = ctx->bitlen >> 8;
ctx->data[61] = ctx->bitlen >> 16;
ctx->data[60] = ctx->bitlen >> 24;
ctx->data[59] = ctx->bitlen >> 32;
ctx->data[58] = ctx->bitlen >> 40;
ctx->data[57] = ctx->bitlen >> 48;
ctx->data[56] = ctx->bitlen >> 56;
sha256_transform(ctx, ctx->data);
// Since this implementation uses little endian byte ordering and SHA uses big endian,
// reverse all the bytes when copying the final state to the output hash.
for (i = 0; i < 4; ++i) {
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff;
hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff;
hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff;
hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff;
hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff;
hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff;
hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff;
}
}

34
lib/sha256.h

@ -0,0 +1,34 @@
/*********************************************************************
* Filename: sha256.h
* Author: Brad Conte (brad AT bradconte.com)
* Copyright:
* Disclaimer: This code is presented "as is" without any guarantees.
* Details: Defines the API for the corresponding SHA1 implementation.
*********************************************************************/
#ifndef SHA256_H
#define SHA256_H
/*************************** HEADER FILES ***************************/
#include <stddef.h>
/****************************** MACROS ******************************/
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
/**************************** DATA TYPES ****************************/
typedef unsigned char BYTE; // 8-bit byte
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
typedef struct {
BYTE data[64];
WORD datalen;
unsigned long long bitlen;
WORD state[8];
} SHA256_CTX;
/*********************** FUNCTION DECLARATIONS **********************/
void sha256_init(SHA256_CTX *ctx);
void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len);
void sha256_final(SHA256_CTX *ctx, BYTE hash[]);
#endif // SHA256_H

16
src/config_parser.c

@ -12,6 +12,14 @@
#define MAX_LINE_LEN 1024
#define INITIAL_ARRAY_CAPACITY 8
void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex_str, size_t hex_len) {
if (!bytes || !hex_str || hex_len < len * 2 + 1) return;
for (size_t i = 0; i < len; i++) {
snprintf(hex_str + i * 2, hex_len - i * 2, "%02x", bytes[i]);
}
hex_str[len * 2] = '\0';
}
typedef enum {
SECTION_UNKNOWN,
SECTION_GLOBAL,
@ -106,6 +114,10 @@ static int add_route(struct utun_config *cfg, const char *server, const char *cl
static int parse_global(const char *key, const char *value, struct global_config *global) {
if (strcmp(key, "my_private_key") == 0) return assign_string(global->my_private_key_hex, MAX_KEY_LEN, value);
if (strcmp(key, "my_public_key") == 0) return assign_string(global->my_public_key_hex, MAX_KEY_LEN, value);
if (strcmp(key, "my_node_id") == 0) {
global->my_node_id = strtoull(value, NULL, 16);
return 0;
}
if (strcmp(key, "tun_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);
@ -143,8 +155,7 @@ static int parse_client(const char *key, const char *value, struct client_config
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);
if (strcmp(key, "peer_public_key") == 0) return assign_string(cli->peer_public_key_hex, MAX_KEY_LEN, value);
return 0;
}
@ -333,6 +344,7 @@ void print_config(const struct utun_config *cfg) {
printf("Global:\n");
printf(" Private key: %s\n", g->my_private_key_hex);
printf(" Public key: %s\n", g->my_public_key_hex);
printf(" Node ID: %llx\n", (unsigned long long)g->my_node_id);
printf(" TUN IP: %s\n", g->tun_ip);
printf(" MTU: %d\n", g->mtu);
printf(" Control: %s:%u\n", g->control_ip, g->control_port);

4
src/config_parser.h

@ -32,6 +32,7 @@ struct client_config {
char name[MAX_CONN_NAME_LEN];
char from[MAX_CONN_NAME_LEN];
char to_addr[MAX_ADDR_LEN];
char peer_public_key_hex[MAX_KEY_LEN];
};
struct route_pair {
@ -50,6 +51,7 @@ struct connection_config_v2 {
struct global_config {
char my_private_key_hex[MAX_KEY_LEN];
char my_public_key_hex[MAX_KEY_LEN];
uint64_t my_node_id;
char tun_ip[MAX_ADDR_LEN];
int mtu;
char control_ip[MAX_ADDR_LEN];
@ -77,6 +79,8 @@ void free_config(struct utun_config *config);
void print_config(const struct utun_config *config);
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key);
void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex_str, size_t hex_len);
#ifdef __cplusplus
}
#endif

293
src/config_updater.c

@ -0,0 +1,293 @@
// config_updater.c - Configuration file updater implementation
#define _POSIX_C_SOURCE 200809L
#include "config_updater.h"
#include "config_parser.h"
#include "secure_channel.h"
#include "debug_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#define HEXKEY_LEN 64 // 32 bytes * 2 hex chars
#define HEXNODEID_LEN 16 // 8 bytes * 2 hex chars
#define MAX_LINE_LEN 1024
static int is_valid_hex_key(const char *key) {
if (!key || strlen(key) != HEXKEY_LEN) return 0;
for (int i = 0; i < HEXKEY_LEN; i++) {
if (!isxdigit((unsigned char)key[i])) return 0;
}
return 1;
}
static int is_valid_node_id(uint64_t node_id) {
return node_id != 0;
}
static int read_file_to_mem(const char *filename, char **buffer, size_t *size) {
FILE *fp = fopen(filename, "r");
if (!fp) return -1;
fseek(fp, 0, SEEK_END);
long file_size = ftell(fp);
if (file_size < 0) {
fclose(fp);
return -1;
}
fseek(fp, 0, SEEK_SET);
*buffer = malloc(file_size + 1);
if (!*buffer) {
fclose(fp);
return -1;
}
size_t read_size = fread(*buffer, 1, file_size, fp);
if (read_size != (size_t)file_size) {
free(*buffer);
fclose(fp);
return -1;
}
(*buffer)[file_size] = '\0';
*size = file_size;
fclose(fp);
return 0;
}
static int write_mem_to_file(const char *filename, const char *buffer, size_t size) {
FILE *fp = fopen(filename, "w");
if (!fp) return -1;
size_t written = fwrite(buffer, 1, size, fp);
if (written != size) {
fclose(fp);
return -1;
}
fclose(fp);
return 0;
}
static char* find_section_global(char *buf, size_t buf_len) {
char *p = buf;
while (p < buf + buf_len) {
if (p[0] == '[' && strncmp(p + 1, "global", 6) == 0) {
char *end = strchr(p, ']');
if (end) return end + 1;
}
p = strchr(p, '\n');
if (!p) break;
p++; // skip '\n'
}
return NULL;
}
static char* find_option(char *buf, size_t buf_len, const char *option) {
char *p = buf;
while (p < buf + buf_len) {
if (strncmp(p, option, strlen(option)) == 0) {
char *after_key = p + strlen(option);
if (after_key[0] == '=') return p;
}
p = strchr(p, '\n');
if (!p) break;
p++;
}
return NULL;
}
static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_capacity, const char *option, const char *value) {
if (!buf || !*buf || !buf_len || !buf_capacity || !option || !value) return -1;
// Check if option already exists
char *opt_pos = find_option(*buf, *buf_len, option);
if (opt_pos) {
// Find option line end
char *line_end = strchr(opt_pos, '\n');
if (!line_end) line_end = *buf + *buf_len;
// Prepare new line
char new_line[MAX_LINE_LEN];
int new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value);
if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1;
// Calculate length difference
size_t old_line_len = line_end - opt_pos + 1;
long len_diff = new_len - old_line_len;
// Ensure buffer capacity
if (*buf_len + len_diff + 1 > *buf_capacity) {
*buf_capacity = *buf_len + len_diff + 1024;
char *new_buf = realloc(*buf, *buf_capacity);
if (!new_buf) return -1;
*buf = new_buf;
// Recalculate positions after realloc
opt_pos = find_option(*buf, *buf_len, option);
if (!opt_pos) return -1;
line_end = strchr(opt_pos, '\n');
if (!line_end) line_end = *buf + *buf_len;
}
// Move content and insert new line
memmove(opt_pos + new_len, line_end, *buf_len - (line_end - *buf) + 1);
memcpy(opt_pos, new_line, new_len);
*buf_len += len_diff;
} else {
// Insert after [global] section
char *section_end = find_section_global(*buf, *buf_len);
if (!section_end) return -1;
// Find end of global section (next [ or end of buffer)
char *insert_pos = strchr(section_end, '[');
if (!insert_pos) insert_pos = *buf + *buf_len;
else {
// Move to beginning of next line
char *prev_nl = insert_pos;
while (prev_nl > *buf && *prev_nl != '\n') prev_nl--;
if (*prev_nl == '\n') insert_pos = prev_nl + 1;
}
// Prepare new line
char new_line[MAX_LINE_LEN];
int new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value);
if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1;
// Ensure buffer capacity
if (*buf_len + new_len + 1 > *buf_capacity) {
*buf_capacity = *buf_len + new_len + 1024;
char *new_buf = realloc(*buf, *buf_capacity);
if (!new_buf) return -1;
*buf = new_buf;
// Recalculate insert position after realloc
section_end = find_section_global(*buf, *buf_len);
if (!section_end) return -1;
insert_pos = strchr(section_end, '[');
if (!insert_pos) insert_pos = *buf + *buf_len;
else {
char *prev_nl = insert_pos;
while (prev_nl > *buf && *prev_nl != '\n') prev_nl--;
if (*prev_nl == '\n') insert_pos = prev_nl + 1;
}
}
// Move content and insert new line
memmove(insert_pos + new_len, insert_pos, *buf_len - (insert_pos - *buf) + 1);
memcpy(insert_pos, new_line, new_len);
*buf_len += new_len;
}
return 0;
}
int config_ensure_keys_and_node_id(const char *filename) {
struct utun_config *config = parse_config(filename);
if (!config) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to parse config: %s", filename);
return -1;
}
struct global_config *global = &config->global;
// Check if we need to generate anything
int need_priv_key = !is_valid_hex_key(global->my_private_key_hex);
int need_pub_key = !is_valid_hex_key(global->my_public_key_hex);
int need_node_id = !is_valid_node_id(global->my_node_id);
if (!need_priv_key && !need_pub_key && !need_node_id) {
free_config(config);
return 0;
}
// Generate keys if needed
char new_priv_key[HEXKEY_LEN + 1] = {0};
char new_pub_key[HEXKEY_LEN + 1] = {0};
uint64_t new_node_id = 0;
if (need_priv_key || need_pub_key) {
struct secure_channel ctx;
if (sc_generate_keypair(&ctx) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to generate keypair");
free_config(config);
return -1;
}
bytes_to_hex(ctx.private_key, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
bytes_to_hex(ctx.public_key, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key));
}
if (need_node_id) {
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to open /dev/urandom");
free_config(config);
return -1;
}
if (read(fd, &new_node_id, sizeof(new_node_id)) != sizeof(new_node_id)) {
close(fd);
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to read random bytes for node_id");
free_config(config);
return -1;
}
close(fd);
new_node_id &= 0x7FFFFFFFFFFFFFFF;
}
free_config(config);
// Read entire config file to memory
char *file_buf = NULL;
size_t file_size = 0;
if (read_file_to_mem(filename, &file_buf, &file_size) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to read config file: %s", filename);
return -1;
}
// Update config file
size_t buf_capacity = file_size + 1024;
char *work_buf = malloc(buf_capacity);
if (!work_buf) {
free(file_buf);
return -1;
}
memcpy(work_buf, file_buf, file_size);
size_t work_len = file_size;
int ret = 0;
if (need_priv_key) {
char node_id_hex[HEXNODEID_LEN + 1];
snprintf(node_id_hex, sizeof(node_id_hex), "%llx", (unsigned long long)new_node_id);
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_node_id", node_id_hex) < 0) {
ret = -1;
}
}
if (need_priv_key && ret == 0) {
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_private_key", new_priv_key) < 0) {
ret = -1;
}
}
if (need_pub_key && ret == 0) {
if (insert_or_replace_option(&work_buf, &work_len, &buf_capacity, "my_public_key", new_pub_key) < 0) {
ret = -1;
}
}
if (ret == 0) {
if (write_mem_to_file(filename, work_buf, work_len) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to write updated config file: %s", filename);
ret = -1;
}
}
free(file_buf);
free(work_buf);
return ret;
}

18
src/config_updater.h

@ -0,0 +1,18 @@
// config_updater.h - Configuration file updater
#ifndef CONFIG_UPDATER_H
#define CONFIG_UPDATER_H
#ifdef __cplusplus
extern "C" {
#endif
// Ensures config file has valid my_private_key, my_public_key, and my_node_id
// If any are missing or invalid, generates and updates them
// Returns 0 on success, -1 on error
int config_ensure_keys_and_node_id(const char *filename);
#ifdef __cplusplus
}
#endif
#endif // CONFIG_UPDATER_H

5
src/etcp.h

@ -35,6 +35,11 @@ struct ETCP_CONN {
// Криптография и состояние
struct secure_channel* crypto_ctx;
// Информация о пире (для клиентов)
uint64_t peer_node_id; // Node ID пира
uint8_t has_peer_key; // Флаг наличия публичного ключа пира
uint8_t peer_public_key[SC_PUBKEY_SIZE]; // Публичный ключ пира (бинарный)
struct ll_queue* input_queue;// отправитель -> input_queue -> etcp -> etcp_channel -> отправка (etcp_sockets)
struct ll_queue* output_queue;// etcp_sockets -> etcp_channel -> etcp -> output_queue -> получатель

105
src/etcp_connections.c

@ -1,15 +1,35 @@
#include "etcp_connections.h"
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "debug_config.h"
#include "routing.h"
#include "utun_instance.h"
#include "config_parser.h"
#include "crc32.h"
#include "etcp.h"
#include "packet_pool.h"
#include "u_async.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
// Конвертация hex строки в бинарный формат
static int hex_to_binary(const char *hex_str, uint8_t *binary, size_t binary_len) {
if (!hex_str || !binary || strlen(hex_str) != binary_len * 2) return -1;
for (size_t i = 0; i < binary_len; i++) {
unsigned int byte;
if (sscanf(hex_str + i * 2, "%2x", &byte) != 1) return -1;
binary[i] = (uint8_t)byte;
}
return 0;
}
// Forward declaration
int etcp_socket_send(struct ETCP_SOCKET* sock, const uint8_t* data, size_t len, const struct sockaddr* to_addr);
// Бинарный поиск линка по ip_port_hash
static int find_link_index(struct ETCP_CONNECTIONS* conns, uint32_t hash) {
if (!conns || conns->num_channels == 0) return -1;
@ -80,13 +100,56 @@ static void remove_link(struct ETCP_CONNECTIONS* conns, uint32_t hash) {
conns->num_channels--;
}
struct ETCP_CONNECTIONS* etcp_connections_init(struct ETCP_SOCKET* socket) {
if (!socket) return NULL;
struct ETCP_CONNECTIONS* etcp_connections_init(struct ETCP_CONN* etcp, const char* bind_addr, uint16_t port) {
if (!etcp) return NULL;
struct ETCP_CONNECTIONS* conns = calloc(1, sizeof(struct ETCP_CONNECTIONS));
if (!conns) return NULL;
if (!conns) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to allocate connections");
return NULL;
}
conns->etcp = etcp;
conns->conns = conns;
conns->socket.etcp = etcp;
conns->socket.socket_id = (uint32_t)(uintptr_t)&conns->socket;
conns->socket.fd = socket(AF_INET, SOCK_DGRAM, 0);
if (conns->socket.fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to create socket: %s", strerror(errno));
free(conns);
return NULL;
}
int flags = fcntl(conns->socket.fd, F_GETFL, 0);
fcntl(conns->socket.fd, F_SETFL, flags | O_NONBLOCK);
struct sockaddr_in* addr = (struct sockaddr_in*)&conns->socket.local_addr;
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
conns->socket.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(conns->socket.fd, (struct sockaddr*)addr, conns->socket.local_addr_len) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to bind socket to %s:%u: %s",
bind_addr ? bind_addr : "ANY", port, strerror(errno));
close(conns->socket.fd);
free(conns);
return NULL;
}
if (getsockname(conns->socket.fd, (struct sockaddr*)addr, &conns->socket.local_addr_len) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "Failed to get socket name: %s", strerror(errno));
close(conns->socket.fd);
free(conns);
return NULL;
}
memcpy(&conns->socket, socket, sizeof(struct ETCP_SOCKET));
return conns;
}
@ -97,11 +160,16 @@ void etcp_connections_destroy(struct ETCP_CONNECTIONS* conns) {
etcp_link_close(conns->links[i]);
}
if (conns->socket.fd >= 0) {
close(conns->socket.fd);
}
free(conns->links);
free(conns);
}
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* socket,
struct ETCP_CONNECTIONS* conns,
const struct sockaddr* remote_addr, socklen_t addr_len) {
if (!socket || !remote_addr || addr_len == 0) return NULL;
@ -110,6 +178,7 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* sock
link->socket = (struct ETCP_SOCKET*)socket;
link->etcp = etcp;
link->conns = conns;
memcpy(&link->remote_addr, remote_addr, addr_len);
link->remote_addr_len = addr_len;
link->last_activity = time(NULL);
@ -150,16 +219,16 @@ int etcp_input(struct packet_buffer* pkt, struct ETCP_SOCKET* socket, struct ETC
etcp_link_close(link);
}
link = etcp_link_new(NULL, socket, src_addr, addr_len);
link = etcp_link_new(NULL, socket, conns, src_addr, addr_len);
if (!link) return -1;
uint8_t* p = &data[1];
link->peer_node_id = 0;
memcpy(&link->peer_node_id, p, 8);
link->etcp->peer_node_id = 0;
memcpy(&link->etcp->peer_node_id, p, 8);
p += 8;
memcpy(link->peer_public_key, p, SC_PUBKEY_SIZE);
link->has_peer_key = 1;
memcpy(link->etcp->peer_public_key, p, SC_PUBKEY_SIZE);
link->etcp->has_peer_key = 1;
p += SC_PUBKEY_SIZE;
uint16_t peer_mtu = ntohs(*(uint16_t*)p);
@ -176,7 +245,7 @@ int etcp_input(struct packet_buffer* pkt, struct ETCP_SOCKET* socket, struct ETC
struct ETCP_LINK* link = etcp_link_find_by_addr(conns, src_addr, addr_len);
if (!link) {
struct ETCP_LINK* temp_link = etcp_link_new(NULL, socket, src_addr, addr_len);
struct ETCP_LINK* temp_link = etcp_link_new(NULL, socket, conns, src_addr, addr_len);
if (temp_link) {
etcp_link_send_reset(temp_link);
etcp_link_close(temp_link);
@ -189,8 +258,8 @@ int etcp_input(struct packet_buffer* pkt, struct ETCP_SOCKET* socket, struct ETC
case ETCP_INIT_RESPONSE:
if (pkt->metadata.data_len < 1 + 8 + 2 + 2) return -1;
link->peer_node_id = 0;
memcpy(&link->peer_node_id, &data[1], 8);
link->etcp->peer_node_id = 0;
memcpy(&link->etcp->peer_node_id, &data[1], 8);
link->mtu = ntohs(*(uint16_t*)&data[1 + 8]);
link->keepalive_interval = ntohs(*(uint16_t*)&data[1 + 8 + 2]);
@ -200,8 +269,8 @@ int etcp_input(struct packet_buffer* pkt, struct ETCP_SOCKET* socket, struct ETC
case ETCP_CHANNEL_INIT:
if (pkt->metadata.data_len < 1 + 8 + 2) return -1;
link->peer_node_id = 0;
memcpy(&link->peer_node_id, &data[1], 8);
link->etcp->peer_node_id = 0;
memcpy(&link->etcp->peer_node_id, &data[1], 8);
link->keepalive_interval = ntohs(*(uint16_t*)&data[1 + 8]);
return etcp_link_send_channel_response(link);
@ -209,8 +278,8 @@ int etcp_input(struct packet_buffer* pkt, struct ETCP_SOCKET* socket, struct ETC
case ETCP_CHANNEL_RESPONSE:
if (pkt->metadata.data_len < 1 + 8) return -1;
link->peer_node_id = 0;
memcpy(&link->peer_node_id, &data[1], 8);
link->etcp->peer_node_id = 0;
memcpy(&link->etcp->peer_node_id, &data[1], 8);
link->initialized = 1;
break;
@ -561,7 +630,7 @@ int init_connections(struct UTUN_INSTANCE* instance) {
}
// Create connections manager
struct ETCP_CONNECTIONS* conns = etcp_connections_init(sock);
struct ETCP_CONNECTIONS* conns = etcp_connections_init(etcp, bind_ip_str, bind_port);
if (!conns) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create connections for %s", server->name);
etcp_socket_destroy(sock);

36
src/etcp_connections.h

@ -32,6 +32,13 @@ struct ETCP_CONNECTIONS {
// Дополнительные поля для управления соединениями
struct ETCP_CONN* etcp; // Родительский ETCP instance
struct ETCP_CONNECTIONS* conns;// Указатель на себя (для совместимости)
// Статистика
size_t encrypt_errors;
size_t decrypt_errors;
size_t send_errors;
size_t total_encrypted;
size_t total_decrypted;
};
// ETCP Link - одно динамическое соединение (один путь)
@ -41,16 +48,12 @@ struct ETCP_LINK {
struct ETCP_SOCKET* socket; // Сокет через который идет трафик
struct ETCP_CONN* etcp; // Родительский ETCP instance
struct ETCP_CONNECTIONS* conns; // Родительский connections (для статистики)
// Путь соединения
struct sockaddr_storage remote_addr; // Удалённый адрес
socklen_t remote_addr_len;
// Информация о пире
uint64_t peer_node_id; // Node ID пира
uint8_t peer_public_key[SC_PUBKEY_SIZE]; // Публичный ключ пира
uint8_t has_peer_key; // Флаг наличия публичного ключа пира
// Параметры соединения
uint16_t mtu; // MTU соединения
uint16_t keepalive_interval; // Keepalive интервал
@ -59,19 +62,12 @@ struct ETCP_LINK {
uint64_t last_activity; // Время последней активности
};
// SOCKET FUNCTIONS (moved from etcp_sockets)
struct ETCP_SOCKET* etcp_socket_create(struct ETCP_CONN* etcp, const char* bind_addr, uint16_t port);
void etcp_socket_destroy(struct ETCP_SOCKET* sock);
int etcp_socket_send(struct ETCP_SOCKET* sock, const uint8_t* data, size_t len, const struct sockaddr* to_addr);
void etcp_socket_set_recv_callback(struct ETCP_SOCKET* sock, void (*callback)(struct ETCP_SOCKET*, struct packet_buffer*, void*), void* user_data);
int etcp_socket_get_fd(const struct ETCP_SOCKET* sock);
void etcp_socket_get_local_addr(const struct ETCP_SOCKET* sock, struct sockaddr_storage* addr, socklen_t* addr_len);
int etcp_socket_set_option(struct ETCP_SOCKET* sock, int level, int optname, const void* optval, socklen_t optlen);
// CONNECTIONS FUNCTIONS
struct ETCP_CONNECTIONS* etcp_connections_init(struct ETCP_SOCKET* socket);
struct ETCP_CONNECTIONS* etcp_connections_init(struct ETCP_CONN* etcp, const char* bind_addr, uint16_t port);
void etcp_connections_destroy(struct ETCP_CONNECTIONS* conns);
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* socket, const struct sockaddr* remote_addr, socklen_t addr_len);
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* socket,
struct ETCP_CONNECTIONS* conns,
const struct sockaddr* remote_addr, socklen_t addr_len);
void etcp_link_close(struct ETCP_LINK* link);
int etcp_input(struct packet_buffer* pkt, struct ETCP_SOCKET* socket, struct ETCP_CONNECTIONS* conns);
int etcp_link_send(struct ETCP_CONN* etcp, struct ETCP_LINK* link, const uint8_t* data, size_t len);
@ -94,4 +90,12 @@ int etcp_link_send_reset(struct ETCP_LINK* link);
// Utility functions
size_t etcp_connections_get_channel_count(const struct ETCP_CONNECTIONS* conns);
// Получение статистики шифрования
void etcp_connections_get_crypto_stats(const struct ETCP_CONNECTIONS* conns,
size_t* encrypt_errors,
size_t* decrypt_errors,
size_t* send_errors,
size_t* total_encrypted,
size_t* total_decrypted);
#endif // ETCP_CONNECTIONS_H

12
src/etcp_protocol.txt

@ -1,4 +1,4 @@
Формат кодограммы:
Формат кодограммы:
------------------
Каждая кодограмма состоит из обязательного заголовка и опциональных секций:
1. Обязательный заголовок (4 байта):
@ -25,19 +25,21 @@
Кодограммы с этими секциями обрабатываются в etcp_connections (в этих кодограммах всегда только одна секция):
г) Init запрос - заголовок 0x02: (у инициирующей стороны всегда есть publick key удаленного пира)
[0x02] [my_node_id 64bit] [my publick key, binary] [my mtu high] [my mty low] [keepalive high] [keepalive low]
г) Init запрос - заголовок 0x02:
[0x02] [my_node_id 64bit] [my mtu high] [my mty low] [keepalive high] [keepalive low] [my publick key (32 байта, не шифруется)]
- Инициирует первый connection и + etcp instance
- Публичный ключ отправляется в конце пакета без шифрования, чтобы получатель мог установить его и расшифровать остальную часть пакета
д) Init подтверждение - заголовок 0x03:
[0x03] [my_node_id 64bit] [my mtu high] [my mty low] [keepalive high] [keepalive low]
- Подтверждение инициализации
г) Channel init запрос - добавляем дополнительный канал для подключения
г) Channel init запрос:
[0x04] [my_node_id 64bit] [keepalive high] [keepalive low]
- Инициирует новый connection в существующем etcp instance
д) Channel init подтверждение - заголовок 0x05:
[0x05] [my_node_id 64bit]
[0x05] [my_node_id 64bit] [my publick key (32 байта, не шифруется)]
- Подтверждение иниуиализации дополнительного канала
е) Reset запрос:

11
src/utun_instance.c

@ -1,6 +1,7 @@
// utun_instance.c - Root instance implementation
#include "utun_instance.h"
#include "config_parser.h"
#include "config_updater.h"
#include "tun_if.h"
#include "routing.h"
#include "etcp_connections.h"
@ -32,6 +33,13 @@ struct UTUN_INSTANCE* utun_instance_create(const char *config_file, const char *
instance->running = 0;
instance->log_fp = NULL;
// Ensure keys and node_id exist in config
if (config_ensure_keys_and_node_id(config_file) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to ensure keys and node_id in config: %s", config_file);
free(instance);
return NULL;
}
// Load configuration
instance->config = parse_config(config_file);
if (!instance->config) {
@ -58,6 +66,9 @@ struct UTUN_INSTANCE* utun_instance_create(const char *config_file, const char *
// uasync has built-in wakeup mechanism, no need for manual pipe
// Set node_id from config
instance->node_id = instance->config->global.my_node_id;
// Set global instance
utun_instance_set(instance);

2
src/utun_instance.h

@ -27,7 +27,7 @@ struct UTUN_INSTANCE {
void *tun_socket_id; // Socket ID from uasync_add_socket
// Identification
uint32_t node_id;
uint64_t node_id;
// Main async context
struct uasync_s *ua;

Loading…
Cancel
Save