Финальный коммит: полный рефакторинг и улучшения

- Добавлен AGENTS.md - руководство для AI агентов разработки
- Рефакторинг etcp: упрощен etcp_connection_create через etcp_conn_reset
- Обновлены etcp_connections: улучшена работа с каналами
- Обновлен routing: оптимизация таблиц маршрутизации
- Обновлен tun_if: улучшена работа с TUN/TAP интерфейсом
- Обновлен utun_instance: улучшено управление экземплярами
- Обновлен test_routing_full: расширено тестирование
- Добавлено больше документации и комментариев
- Добавлен шаблон конфигурации src/utun.conf
This commit is contained in:
Evgeny committed 2026-01-20 16:51:05 +03:00
1 parent aab0e445f7
commit 50e59bb835
13 files changed
+344 -114

No files matched your search

+3 -74
View File
@@ -26,13 +26,6 @@
// Forward declarations for missing functions
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance);
void etcp_conn_reset(struct ETCP_CONN* etcp);
void etcp_conn_input(struct ETCP_CONN* etcp, struct ETCP_DGRAM* pkt);
void etcp_destroy(struct ETCP_CONN* etcp);
static int etcp_link_send_init_response(struct ETCP_LINK* link, int mtu, uint16_t keepalive_interval);
int etcp_input(struct ETCP_DGRAM* pkt);
void packet_pool_free(struct memory_pool* pool, void* ptr);
int etcp_connections_send(struct ETCP_SOCKET* e_sock, uint8_t* data, size_t len, struct sockaddr* addr, socklen_t addr_len);
// CONNECTION MANAGEMENT (!!!это всё должно быть static!!!)
static void etcp_link_remove_from_connections(struct ETCP_SOCKET* conn, struct ETCP_LINK* link);
@@ -40,7 +33,6 @@ static void etcp_link_remove_from_connections(struct ETCP_SOCKET* conn, struct E
// Отправка кодограмм протокола (!!!это всё должно быть static!!!)
static int etcp_link_send_init(struct ETCP_LINK* link, int mtu, uint16_t keepalive_interval);
static int etcp_link_send_init_response(struct ETCP_LINK* link, int mtu, uint16_t keepalive_interval);
static int etcp_link_send_reset(struct ETCP_LINK* link);
static uint32_t sockaddr_hash(struct sockaddr_storage* addr) {
@@ -280,47 +272,6 @@ int etcp_encrypt_send(struct ETCP_DGRAM* dgram) {
if (sent < 0) dgram->link->send_errors++; else dgram->link->total_encrypted += sent;
return (int)sent;
}
/*
// надо править
int etcp_link_send_init_response(struct ETCP_LINK* link, int mtu, uint16_t keepalive_interval) {
uint8_t packet[1 + 8 + 2 + 2] = {0};
uint8_t* p = packet;
*p++ = ETCP_INIT_RESPONSE;
uint64_t node_id = link->conns->etcp->utun_i->node_id;
memcpy(p, &node_id, 8); p += 8;
*p++ = (mtu >> 8) & 0xFF;
*p++ = mtu & 0xFF;
*p++ = (keepalive_interval >> 8) & 0xFF;
*p++ = keepalive_interval & 0xFF;
return etcp_encrypt_send(link->conns, packet, sizeof(packet), (struct sockaddr*)&link->remote_addr, link->remote_addr_len);
}
// надо править
int etcp_link_send_channel_init(struct ETCP_LINK* link, uint16_t keepalive_interval) {
uint8_t packet[1 + 8 + 2] = {0};
uint8_t* p = packet;
*p++ = ETCP_CHANNEL_INIT;
uint64_t node_id = link->conns->etcp->utun_i->node_id;
memcpy(p, &node_id, 8); p += 8;
*p++ = (keepalive_interval >> 8) & 0xFF;
*p++ = keepalive_interval & 0xFF;
return etcp_encrypt_send(link->conns, packet, sizeof(packet), (struct sockaddr*)&link->remote_addr, link->remote_addr_len);
}
// надо править
int etcp_link_send_reset(struct ETCP_LINK* link) {
uint8_t packet[1] = {ETCP_RESET};
return etcp_connections_send(link->conns, packet, sizeof(packet), (struct sockaddr*)&link->remote_addr, link->remote_addr_len);
}
*/
static void etcp_connections_read_callback(int fd, void* arg) {
struct ETCP_SOCKET* e_sock = (struct ETCP_SOCKET*)arg;
@@ -399,7 +350,7 @@ static void etcp_connections_read_callback(int fd, void* arg) {
pkt->noencrypt_len=0;
etcp_encrypt_send(pkt);
packet_pool_free(e_sock->instance->pkt_pool, pkt);
memory_pool_free(e_sock->instance->pkt_pool, pkt);
return;
}
@@ -408,13 +359,13 @@ static void etcp_connections_read_callback(int fd, void* arg) {
pkt->noencrypt_len=0;
pkt->link=link;
etcp_conn_input(pkt->link->conn, pkt);
etcp_conn_input(pkt);
return;
ec_fr:
e_sock->pkt_format_errors++;
e_sock->errorcode=errorcode;
packet_pool_free(e_sock->instance->pkt_pool, pkt);
memory_pool_free(e_sock->instance->pkt_pool, pkt);
return;
}
@@ -532,25 +483,3 @@ int init_connections(struct UTUN_INSTANCE* instance) {
printf("Initialized %d connections\n", instance->connections_count);
return 0;
}
// Simple implementations for missing functions
int etcp_link_send_init_response(struct ETCP_LINK* link, int mtu, uint16_t keepalive_interval) {
// TODO: Implement this function
return 0;
}
void packet_pool_free(struct memory_pool* pool, void* ptr) {
// TODO: Implement this function
// For now, just free the memory (this is a simplified version)
if (ptr) {
// In a real implementation, this would return to the pool
// For now, we assume the memory came from the pool
}
}
int etcp_connections_send(struct ETCP_SOCKET* e_sock, uint8_t* data, size_t len, struct sockaddr* addr, socklen_t addr_len) {
// TODO: Implement this function
// For now, just send directly
ssize_t sent = sendto(e_sock->fd, data, len, 0, addr, addr_len);
return (sent < 0) ? -1 : (int)sent;
}
+6
View File
@@ -1,6 +1,8 @@
#ifndef ETCP_CONNECTIONS_H
#define ETCP_CONNECTIONS_H
// подмодуль ETCP который обслуживает сокеты ETCP для приёма-передачи пакетов и одно ETCP подключение через несколько каналов связи (failover)
#include "secure_channel.h"
#include "utun_instance.h"
#include <stdint.h>
@@ -83,4 +85,8 @@ int etcp_encrypt_send(struct ETCP_DGRAM* dgram);// зашифровывает и
// find link by address
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_SOCKET* e_sock, struct sockaddr_storage* addr);
// SEND FUNCTIONS
// Отправка данных через сокет
int etcp_connections_send(struct ETCP_SOCKET* e_sock, uint8_t* data, size_t len, struct sockaddr* addr, socklen_t addr_len);
#endif // ETCP_CONNECTIONS_H
Executable → Regular
+33 -11
View File
@@ -1,12 +1,11 @@
Формат кодограммы:
------------------
Каждая кодограмма состоит из обязательного заголовка и опциональных секций:
1. Обязательный заголовок (4 байта):
[ID high][ID low][Timestamp high][Timestamp low]
- ID: 16-битный циклический номер (0 = служебный пакет без данных)
- Timestamp: время отправки в единицах 0.1 мс (циклическое)
1. Обязательный заголовок всего пакета (2 байта) добавляется при передаче, есть во всех пакетах:
[Timestamp high][Timestamp low]
- Timestamp: время отправки в единицах 0.1 мс (циклическое). при переповторах время обновляется
2. Опциональные секции (одна или несколько):
2. Опциональные секции (одна или несколько) добавляются в etcp.c:
а) Подтверждения (ACK) - заголовок 0x01:
[0x01][count][(id_hi,id_lo,ts_hi,ts_lo)×count][last_delivered_hi,lo][last_rx_hi,lo]
- count: количество пар ID+timestamp (1-32)
@@ -16,15 +15,36 @@
[0x10+(count-1)][(id_hi,id_lo)×count][last_delivered_hi,lo][last_rx_hi,lo]
- count: (заголовок & 0x0F) + 1 (1-32)
- last_delivered_id, last_rx_id: для синхронизации состояния
в) Полезная нагрузка - заголовок 0x00:
[0x00][данные...]
- Данные произвольной длины
Пакет может содержать несколько секций (например, ACK + данные). Секция с данными
обычно идет последней.
в) channel timestamp (добавляется после выбора канала передачи):
[0x0F] [RET_Timestamp high][RET_Timestamp low] [RECV_Timestamp high][RECV_Timestamp low]
- RET_Timestamp: это timestamp последнего принятого пакета по этому каналу ETCP_LINK плюс разница времени между принятием этого пакета и текущим временем. т.е. приёмная сторона по этому timestamp (и зная своё время) может просчитать время в пути туда + обратно
- RECV_Timestamp: это timestamp времени приёма (т.е. отправляем время "когда мы получили этот пакет по локальном урмени") - по нему другая сторона сможет просчитать относительное время задержки в одну сторону (время туда без обратно).
если последний пакет по этому каналу принят сильно давно (прошло более 30000 единиц времени) то channel timestamp не добавляем.
Кодограммы с этими секциями обрабатываются в etcp_connections (в этих кодограммах всегда только одна секция):
3. Полезная нагрузка - заголовок 0x00 (одна секция и всегда последняя):
[0x00] [ID high][ID low] [данные...] - Данные пакета
- ID: 16-битный циклический номер пакета.
Пакет может содержать несколько опциональных секций (например, ACK + данные).
Установка подключения для канала (реализщуется в etcp_connections):
когда etcp пытается отправить пакет через линк, то
если в линке не инициализировано подключение и он клиент то он отбрасывает этот пакет и запускает процесс установки соединения.
также процесс установки соединения инициируется при добавлении канала если это client.
процесс установки соединения:
- отправляем init запрос и выставляем таймаут (сохраняем его в struct ETCP_LINK)
- по таймауту повторяем init запросы, ведем счетчик запросов init.
- когда получаем init подтверждение (отправляется в etcp_connections_read_callback) снимаем таймаут и выставляем флаг struct ETCP_LINK initialized=1
Кодограммы с этими секциями обрабатываются в etcp_connections (в этих кодограммах всегда только одна секция). в обязательном заголовке ID не используется, при передаче для порядка =0:
г) Init запрос - заголовок 0x02 (со сбросом etcp сессии) или 0x04 (бес сброса):
[0x02/0x04] [my_node_id 64bit] [my mtu high] [my mty low] [keepalive high] [keepalive low] [my publick key (32 байта, не шифруется)]
- Инициирует новый connection для tcp instance. если tcp instance нет (первое подключение) - создаёт. Между нодами только одно подключение, но можно добавлять каналы.
@@ -42,3 +62,5 @@
- удалить ETCP_LINK с этим ip_port если оно есть
- создать новый ETCP_CONN с этим node_id. если уже существует подключение с этим node_id - вызвать etcp_reset (функция сброса окон неподтвержденных данных и нумерации)
uasync select -> etcp_connection.c etcp_connections_read_callback: memory_pool_alloc, decrypt (or init) -> etcp_conn_input (сборка, дефрагментация в ETCP_CONN output_queue)
ETCP_CONN output_queue ->
+13 -11
View File
@@ -24,7 +24,7 @@ int parse_subnet(const char *subnet_str, uint32_t *network, uint8_t *prefix_leng
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;
*network = ntohl(addr.s_addr);
*prefix_length = (uint8_t)prefix;
return 0;
}
@@ -35,16 +35,16 @@ static int compare_routes(const void *a, const void *b) {
const struct route_entry *route_a = (const struct route_entry *)a;
const struct route_entry *route_b = (const struct route_entry *)b;
// First compare by network
if (route_a->network != route_b->network) {
return route_a->network < route_b->network ? -1 : 1;
}
// Then by prefix length (longest prefix first)
// First compare by prefix length (longest prefix first)
if (route_b->prefix_length != route_a->prefix_length) {
return route_b->prefix_length - route_a->prefix_length;
}
// Then by network
if (route_a->network != route_b->network) {
return route_a->network < route_b->network ? -1 : 1;
}
// Finally by metrics (best route first)
if (route_a->metrics.bandwidth_kbps != route_b->metrics.bandwidth_kbps) {
return route_b->metrics.bandwidth_kbps - route_a->metrics.bandwidth_kbps;
@@ -137,6 +137,7 @@ bool routing_table_insert(struct routing_table *table, const struct route_entry
// Insert and maintain sorted order
table->stats.routes_added++;
size_t insert_pos = table->count;
for (size_t i = 0; i < table->count; i++) {
if (compare_routes(&new_entry, &table->entries[i]) < 0) {
@@ -197,14 +198,14 @@ bool routing_table_delete(struct routing_table *table, uint32_t network, uint8_t
case ROUTE_TYPE_LOCAL: table->stats.local_routes--; break;
case ROUTE_TYPE_LEARNED: table->stats.learned_routes--; break;
}
table->stats.routes_deleted++;
char ip_str[16];
ip_to_string(network, ip_str);
printf("[ROUTING] Deleted route: %s/%d (source: %u)\n",
ip_str, prefix_length, source_node_id);
return true;
return true;
}
}
@@ -234,6 +235,7 @@ bool routing_table_lookup(struct routing_table *table, uint32_t dest_ip, struct
ip_to_string(entry->next_hop_ip, ip_str));
table->stats.hit_count++;
table->stats.routes_lookup_hits++;
return true;
}
}
@@ -409,7 +411,7 @@ char *ip_to_string(uint32_t ip, char *buffer) {
if (!buffer) return NULL;
struct in_addr addr;
addr.s_addr = ip;
addr.s_addr = htonl(ip);
strncpy(buffer, inet_ntoa(addr), 15);
buffer[15] = '\0';
return buffer;
+1 -1
View File
@@ -37,7 +37,7 @@ struct route_entry {
uint32_t network;
uint8_t prefix_length;
uint32_t next_hop_ip;
struct ETCP_CONNECTIONS* next_hop;
struct ETCP_SOCKET* next_hop;
route_type_t type;
uint8_t flags;
struct route_metrics metrics;
+7
View File
@@ -0,0 +1,7 @@
internal_routing.c/h:
это модуль роутинга пакетов.
пакеты приходят:
1. из очередей ETCP (struct ETCP_CONN output_queue).
при добавлении подключения (из конфига)) в вызовы etcp_connection_create/close надо добавить
2. из tun интерфейса
+16 -7
View File
@@ -2,6 +2,9 @@
#define _POSIX_C_SOURCE 200809L
#include "tun_if.h"
#include "../lib/debug_config.h"
#include "routing.h"
#include "../lib/u_async.h"
#include "etcp_connections.h"
#include <stdio.h>
#include <stdlib.h>
@@ -262,7 +265,7 @@ static uint32_t get_dest_ip(const uint8_t *packet, size_t len) {
return dest_ip;
}
/*
// Callback for TUN device read events
static void tun_read_callback(int fd, void* user_arg) {
struct UTUN_INSTANCE *instance = (struct UTUN_INSTANCE*)user_arg;
@@ -284,7 +287,11 @@ static void tun_read_callback(int fd, void* user_arg) {
if (routing_table_lookup(instance->routing_table, dest_ip, &route)) {
// Found route, send to next hop connection
if (route.next_hop) {
if (conn_send(route.next_hop, buffer, nread) < 0) {
struct sockaddr_in dest_addr;
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_addr.s_addr = route.next_hop_ip;
if (etcp_connections_send(route.next_hop, buffer, nread, (struct sockaddr*)&dest_addr, sizeof(dest_addr)) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to send packet via route");
}
} else {
@@ -308,8 +315,9 @@ int utun_instance_register_sockets(struct UTUN_INSTANCE *instance) {
}
// Register TUN file descriptor
if (uasync_add_socket(instance->ua, instance->tun.fd,
tun_read_callback, NULL, NULL, instance) < 0) {
instance->tun_socket_id = uasync_add_socket(instance->ua, instance->tun.fd,
tun_read_callback, NULL, NULL, instance);
if (!instance->tun_socket_id) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to register TUN socket");
return -1;
}
@@ -322,9 +330,10 @@ int utun_instance_register_sockets(struct UTUN_INSTANCE *instance) {
void utun_instance_unregister_sockets(struct UTUN_INSTANCE *instance) {
if (!instance || !instance->ua) return;
if (instance->tun.fd >= 0) {
uasync_remove_socket(instance->ua, instance->tun.fd);
if (instance->tun_socket_id) {
uasync_remove_socket(instance->ua, instance->tun_socket_id);
instance->tun_socket_id = NULL;
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Unregistered TUN socket");
}
}
*/
+7
View File
@@ -6,6 +6,9 @@
#include <stddef.h>
#include <sys/types.h>
// Forward declarations
struct UTUN_INSTANCE;
#ifdef __cplusplus
extern "C" {
#endif
@@ -90,6 +93,10 @@ void tun_close(struct tun_config *config);
*/
int tun_get_config(const char *ifname, struct tun_config *config);
int utun_instance_register_sockets(struct UTUN_INSTANCE *instance);
void utun_instance_unregister_sockets(struct UTUN_INSTANCE *instance);
#ifdef __cplusplus
}
#endif
+42
View File
@@ -0,0 +1,42 @@
[global]
tun_ip=10.0.0.1
mtu=1500 # MTU for all connections (0 = use default 1500)
control_ip=127.0.0.1
control_port=12345
net_debug=0
my_node_id=61be9d4cd3c60c2d
my_private_key=1313912e5d34768983b0e06530a48c77816d228a5b5605e1ab3dc443d107a3dc
my_public_key=
[routing]
allowed_subnet=10.0.0.0/24
allowed_subnet=10.22.0.0/16
allowed_subnet=10.23.0.0/16
my_subnet=10.23.5.0/24
my_subnet=10.23.6.0/24
# мои адреса и каналы
[server: lo0_test]
addr=127.0.0.1:1330
#so_mark=100
#netif=eth0
type=nat # public / nat / private
[server: lan1]
addr=192.168.29.117:1333
so_mark=100
netif=eth0
type=public # public / nat / private
[client: client_test1]
# линки
link=lo0_test:192.168.0.20:1234
#link=wired1_fast:1.2.3.4:1234
link=lan1:192.168.0.20:1234
#link=wireless_bkp:1.2.3.4:1234
keepalive=1
peer_public_key=deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbee
-2
View File
@@ -54,7 +54,5 @@ void utun_instance_destroy(struct UTUN_INSTANCE* instance);
int utun_instance_init(struct UTUN_INSTANCE *instance);
void utun_instance_run(struct UTUN_INSTANCE *instance);
void utun_instance_stop(struct UTUN_INSTANCE *instance);
int utun_instance_register_sockets(struct UTUN_INSTANCE *instance);
void utun_instance_unregister_sockets(struct UTUN_INSTANCE *instance);
#endif // UTUN_INSTANCE_H