Browse Source

Reorganize project structure: move sources to src/, update Makefile, add header symlinks

v2_dev
jek 9 months ago
parent
commit
f2c10dcbf9
  1. 8
      .gitignore
  2. 128
      Makefile
  3. 7
      changelog.txt
  4. 117
      config_parser.h
  5. 1
      config_parser.h
  6. 129
      connection.h
  7. 1
      connection.h
  8. 208
      control_socket.h
  9. 1
      control_socket.h
  10. 234
      etcp.h
  11. 1
      etcp.h
  12. 141
      ll_queue.h
  13. 1
      ll_queue.h
  14. 2
      monitor.py
  15. 75
      pkt_normalizer.h
  16. 1
      pkt_normalizer.h
  17. 170
      routing.h
  18. 1
      routing.h
  19. 119
      sc_lib.h
  20. 1
      sc_lib.h
  21. 11
      settings.h
  22. 1
      settings.h
  23. 0
      src/config_parser.c
  24. 117
      src/config_parser.h
  25. 0
      src/connection.c
  26. 129
      src/connection.h
  27. 0
      src/control_socket.c
  28. 208
      src/control_socket.h
  29. 0
      src/etcp.c
  30. 234
      src/etcp.h
  31. 0
      src/ll_queue.c
  32. 141
      src/ll_queue.h
  33. 0
      src/pkt_normalizer.c
  34. 75
      src/pkt_normalizer.h
  35. 0
      src/routing.c
  36. 170
      src/routing.h
  37. 0
      src/sc_lib.c
  38. 119
      src/sc_lib.h
  39. 0
      src/settings.c
  40. 11
      src/settings.h
  41. 0
      src/timeout_heap.c
  42. 92
      src/timeout_heap.h
  43. 0
      src/tun_if.c
  44. 95
      src/tun_if.h
  45. 0
      src/u_async.c
  46. 47
      src/u_async.h
  47. 0
      src/utun.c
  48. 30
      src/utun_state.h
  49. 92
      timeout_heap.h
  50. 1
      timeout_heap.h
  51. 1
      todo.txt
  52. 95
      tun_if.h
  53. 1
      tun_if.h
  54. 304
      u_async.c.backup
  55. 47
      u_async.h
  56. 1
      u_async.h
  57. 30
      utun_state.h
  58. 1
      utun_state.h

8
.gitignore vendored

@ -21,4 +21,10 @@ tinycrypt/tests/*.o
# OS files
.DS_Store
Thumbs.db
Thumbs.db
# Header symlinks (headers are in src/)
/*.h
# Build directories
/obj/

128
Makefile

@ -1,8 +1,15 @@
CC := gcc
CFLAGS := -Os -std=c99 -Wall -Wextra -D_ISOC99_SOURCE -DENABLE_TESTS -DETCP_DEBUG -DETCP_DEBUG_EXT
INCLUDES := -Itinycrypt/lib/include/ -Itinycrypt/lib/source/ -Itinycrypt/tests/include/ -I.
SRC_DIR := src
TEST_DIR := tests
INCLUDES := -I$(SRC_DIR) -Itinycrypt/lib/include/ -Itinycrypt/lib/source/ -Itinycrypt/tests/include/ -I.
# Директории для объектных файлов
OBJ_DIR := obj
SRC_OBJ_DIR := $(OBJ_DIR)/src
TEST_OBJ_DIR := $(OBJ_DIR)/tests
# Исходники tinycrypt
TINYCRYPT_SRCS := \
tinycrypt/lib/source/ecc.c \
tinycrypt/lib/source/ecc_dh.c \
@ -13,65 +20,124 @@ TINYCRYPT_SRCS := \
tinycrypt/lib/source/ctr_mode.c \
tinycrypt/lib/source/ccm_mode.c
# Объектные файлы tinycrypt (остаются в своих директориях)
TINYCRYPT_OBJS := $(TINYCRYPT_SRCS:.c=.o)
SC_LIB_OBJS := sc_lib.o
UASYNC_OBJS := u_async.o timeout_heap.o
PN_OBJS := pkt_normalizer.o settings.o
LL_QUEUE_OBJS := ll_queue.o
ETCP_OBJS := etcp.o
CONFIG_PARSER_OBJS := config_parser.o
TUN_IF_OBJS := tun_if.o
all: utun $(TEST_DIR)/test_sc_lib $(TEST_DIR)/test_udp_secure $(TEST_DIR)/test_pkt_normalizer $(TEST_DIR)/test_etcp $(TEST_DIR)/test_etcp_stress $(TEST_DIR)/test_etcp_simple $(TEST_DIR)/test_connection $(TEST_DIR)/test_connection_stress $(TEST_DIR)/test_new_features $(TEST_DIR)/test_utun_integration $(TEST_DIR)/test_utun_fork
# Основные объектные файлы (будут в obj/src/)
SC_LIB_OBJS := $(SRC_OBJ_DIR)/sc_lib.o
UASYNC_OBJS := $(SRC_OBJ_DIR)/u_async.o $(SRC_OBJ_DIR)/timeout_heap.o
PN_OBJS := $(SRC_OBJ_DIR)/pkt_normalizer.o $(SRC_OBJ_DIR)/settings.o
LL_QUEUE_OBJS := $(SRC_OBJ_DIR)/ll_queue.o
ETCP_OBJS := $(SRC_OBJ_DIR)/etcp.o
CONFIG_PARSER_OBJS := $(SRC_OBJ_DIR)/config_parser.o
TUN_IF_OBJS := $(SRC_OBJ_DIR)/tun_if.o
CONNECTION_OBJS := $(SRC_OBJ_DIR)/connection.o
ROUTING_OBJS := $(SRC_OBJ_DIR)/routing.o
CONTROL_SOCKET_OBJS := $(SRC_OBJ_DIR)/control_socket.o
UTUN_OBJS := $(SRC_OBJ_DIR)/utun.o
# Тестовые объектные файлы (будут в obj/tests/)
TEST_PKT_NORMALIZER_OBJS := $(TEST_OBJ_DIR)/test_pkt_normalizer.o
TEST_ETCP_OBJS := $(TEST_OBJ_DIR)/test_etcp.o
TEST_ETCP_STRESS_OBJS := $(TEST_OBJ_DIR)/test_etcp_stress.o
TEST_ETCP_SIMPLE_OBJS := $(TEST_OBJ_DIR)/test_etcp_simple.o
TEST_CONNECTION_OBJS := $(TEST_OBJ_DIR)/test_connection.o
TEST_CONNECTION_STRESS_OBJS := $(TEST_OBJ_DIR)/test_connection_stress.o
TEST_NEW_FEATURES_OBJS := $(TEST_OBJ_DIR)/test_new_features.o
TEST_SC_LIB_OBJS := $(TEST_OBJ_DIR)/test_sc_lib.o
TEST_UDP_SECURE_OBJS := $(TEST_OBJ_DIR)/test_udp_secure.o
TEST_UTUN_INTEGRATION_OBJS := $(TEST_OBJ_DIR)/test_utun_integration.o
TEST_UTUN_FORK_OBJS := $(TEST_OBJ_DIR)/test_utun_fork.o
SIMPLE_UASYNC_OBJS := $(TEST_OBJ_DIR)/simple_uasync.o
# Основная цель
all: utun \
$(TEST_DIR)/test_sc_lib \
$(TEST_DIR)/test_udp_secure \
$(TEST_DIR)/test_pkt_normalizer \
$(TEST_DIR)/test_etcp \
$(TEST_DIR)/test_etcp_stress \
$(TEST_DIR)/test_etcp_simple \
$(TEST_DIR)/test_connection \
$(TEST_DIR)/test_connection_stress \
$(TEST_DIR)/test_new_features \
$(TEST_DIR)/test_utun_integration \
$(TEST_DIR)/test_utun_fork
# Создание директорий
$(OBJ_DIR):
mkdir -p $(OBJ_DIR)
$(SRC_OBJ_DIR): $(OBJ_DIR)
mkdir -p $(SRC_OBJ_DIR)
$(TEST_OBJ_DIR): $(OBJ_DIR)
mkdir -p $(TEST_OBJ_DIR)
# Правило компиляции для исходников из src/
$(SRC_OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(SRC_OBJ_DIR)
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(TEST_DIR)/test_pkt_normalizer: $(TEST_DIR)/test_pkt_normalizer.o $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
# Правило компиляции для тестовых .c файлов
$(TEST_OBJ_DIR)/%.o: $(TEST_DIR)/%.c | $(TEST_OBJ_DIR)
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(TEST_DIR)/test_etcp: $(TEST_DIR)/test_etcp.o $(ETCP_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
# Правило компиляции для tinycrypt (оставляем в оригинальных директориях)
tinycrypt/lib/source/%.o: tinycrypt/lib/source/%.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(TEST_DIR)/test_etcp_stress: $(TEST_DIR)/test_etcp_stress.o $(ETCP_OBJS) $(LL_QUEUE_OBJS) $(TEST_DIR)/simple_uasync.o
# Правила линковки
utun: $(UTUN_OBJS) $(CONFIG_PARSER_OBJS) $(TUN_IF_OBJS) $(CONNECTION_OBJS) $(ROUTING_OBJS) $(CONTROL_SOCKET_OBJS) $(ETCP_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
$(TEST_DIR)/test_etcp_simple: $(TEST_DIR)/test_etcp_simple.o $(ETCP_OBJS) $(LL_QUEUE_OBJS) $(TEST_DIR)/simple_uasync.o
$(TEST_DIR)/test_pkt_normalizer: $(TEST_PKT_NORMALIZER_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
$(TEST_DIR)/test_connection: $(TEST_DIR)/test_connection.o connection.o $(ETCP_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(TEST_DIR)/test_etcp: $(TEST_ETCP_OBJS) $(ETCP_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
$(TEST_DIR)/test_connection_stress: $(TEST_DIR)/test_connection_stress.o connection.o $(ETCP_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(TEST_DIR)/test_etcp_stress: $(TEST_ETCP_STRESS_OBJS) $(ETCP_OBJS) $(LL_QUEUE_OBJS) $(SIMPLE_UASYNC_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
$(TEST_DIR)/test_new_features: $(TEST_DIR)/test_new_features.o $(ETCP_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS) connection.o $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(TEST_DIR)/test_etcp_simple: $(TEST_ETCP_SIMPLE_OBJS) $(ETCP_OBJS) $(LL_QUEUE_OBJS) $(SIMPLE_UASYNC_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
utun: utun.o $(CONFIG_PARSER_OBJS) $(TUN_IF_OBJS) connection.o routing.o control_socket.o $(ETCP_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(TEST_DIR)/test_connection: $(TEST_CONNECTION_OBJS) $(CONNECTION_OBJS) $(ETCP_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
$(TEST_DIR)/test_sc_lib: $(TEST_DIR)/test_sc_lib.o $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(TEST_DIR)/test_connection_stress: $(TEST_CONNECTION_STRESS_OBJS) $(CONNECTION_OBJS) $(ETCP_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
$(TEST_DIR)/test_udp_secure: $(TEST_DIR)/test_udp_secure.o $(SC_LIB_OBJS) $(UASYNC_OBJS) $(TINYCRYPT_OBJS)
$(TEST_DIR)/test_new_features: $(TEST_NEW_FEATURES_OBJS) $(ETCP_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS) $(CONNECTION_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
$(TEST_DIR)/test_utun_integration: $(TEST_DIR)/test_utun_integration.o $(CONFIG_PARSER_OBJS) $(TUN_IF_OBJS) connection.o $(ETCP_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(TEST_DIR)/test_sc_lib: $(TEST_SC_LIB_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
$(TEST_DIR)/test_utun_fork: $(TEST_DIR)/test_utun_fork.o $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(TEST_DIR)/test_udp_secure: $(TEST_UDP_SECURE_OBJS) $(SC_LIB_OBJS) $(UASYNC_OBJS) $(TINYCRYPT_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
$(TEST_DIR)/test_pkt_normalizer: $(TEST_DIR)/test_pkt_normalizer.o $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS)
$(TEST_DIR)/test_utun_integration: $(TEST_UTUN_INTEGRATION_OBJS) $(CONFIG_PARSER_OBJS) $(TUN_IF_OBJS) $(CONNECTION_OBJS) $(ETCP_OBJS) $(PN_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
$(TEST_DIR)/test_etcp: $(TEST_DIR)/test_etcp.o $(ETCP_OBJS) $(LL_QUEUE_OBJS) $(UASYNC_OBJS)
$(TEST_DIR)/test_utun_fork: $(TEST_UTUN_FORK_OBJS) $(SC_LIB_OBJS) $(TINYCRYPT_OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
# Очистка
clean:
rm -f utun $(TEST_DIR)/test_sc_lib $(TEST_DIR)/test_udp_secure $(TEST_DIR)/test_pkt_normalizer $(TEST_DIR)/test_etcp $(TEST_DIR)/test_etcp_stress $(TEST_DIR)/test_etcp_simple $(TEST_DIR)/test_connection $(TEST_DIR)/test_connection_stress $(TEST_DIR)/test_new_features $(TEST_DIR)/test_utun_integration $(TEST_DIR)/test_utun_fork \
*.o tinycrypt/lib/source/*.o $(TEST_DIR)/*.o
rm -rf $(OBJ_DIR)
rm -f utun \
$(TEST_DIR)/test_sc_lib \
$(TEST_DIR)/test_udp_secure \
$(TEST_DIR)/test_pkt_normalizer \
$(TEST_DIR)/test_etcp \
$(TEST_DIR)/test_etcp_stress \
$(TEST_DIR)/test_etcp_simple \
$(TEST_DIR)/test_connection \
$(TEST_DIR)/test_connection_stress \
$(TEST_DIR)/test_new_features \
$(TEST_DIR)/test_utun_integration \
$(TEST_DIR)/test_utun_fork
rm -f *.o $(TEST_DIR)/*.o tinycrypt/lib/source/*.o
.PHONY: all clean

7
changelog.txt

@ -46,3 +46,10 @@ Thu Jan 15 2026 21:30: Добавление детектора утечек па
- Добавлена проверка утечек в uasync_destroy с аварийным завершением при обнаружении неосвобожденных ресурсов после очистки
- Исправлен подсчет освобождений: теперь все таймеры учитываются правильно
- Все тесты проходят, утечки не обнаруживаются после очистки
Thu Jan 15 2026 17:09: Реорганизация структуры проекта и обновление Makefile
- Исходные файлы перемещены в каталог src/
- Обновлен Makefile для сборки объектных файлов в obj/src и obj/tests
- Добавлены символические ссылки на заголовочные файлы в корне для совместимости с тестами
- Обновлены пути включения заголовков (-Isrc)
- Все цели сборки работают корректно

117
config_parser.h

@ -1,117 +0,0 @@
// config_parser.h - Configuration parser for utun application
#ifndef CONFIG_PARSER_H
#define CONFIG_PARSER_H
#include <stdint.h>
#include <stddef.h>
#include "connection.h"
#ifdef __cplusplus
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_ADDR_LEN 64
#define MAX_NETIF_LEN 16
#define MAX_OPTION_VALUE_LEN 256
#define MAX_ALLOWED_SUBNETS 32
// Connection mode (config parser internal)
typedef enum {
CONFIG_MODE_UNKNOWN = 0,
CONFIG_MODE_CLIENT,
CONFIG_MODE_SERVER
} config_conn_mode_t;
// Subnet entry for allowed routing
typedef struct {
char subnet[MAX_ADDR_LEN]; // Format: "192.168.0.0/24"
} subnet_entry_t;
// Connection configuration
typedef struct {
char name[MAX_CONN_NAME_LEN]; // Connection name from [connection: name]
config_conn_mode_t mode; // server or client
char local_addr[MAX_ADDR_LEN]; // For client: from_addr, for server: addr
char remote_addr[MAX_ADDR_LEN]; // For client: to_addr, for server: N/A
char peer_public_key_hex[MAX_KEY_LEN]; // Peer's public key in HEX
int so_mark; // Socket mark (0 = not set)
char netif[MAX_NETIF_LEN]; // Network interface to bind to
char tun_ifname[MAX_NETIF_LEN]; // TUN interface name (e.g., tun12)
char tun_ip[MAX_ADDR_LEN]; // TUN interface IP address (e.g., 10.0.0.1/24)
} connection_config_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
} global_config_t;
// Complete configuration
typedef struct {
global_config_t global;
connection_config_t *connections;
int connection_count;
int connection_capacity;
subnet_entry_t allowed_subnets[MAX_ALLOWED_SUBNETS];
int allowed_subnet_count;
} utun_config_t;
/**
* @brief Parse configuration file
* @param filename Path to configuration file
* @return Pointer to parsed configuration, NULL on error
*
* Parses INI-style configuration file with format:
* [global]
* option=value
* my_private_key=HEX
* my_public_key=HEX
*
* [connection: name]
* mode=server|client
* addr=ip:port (for server)
* from_addr=ip:port (for client)
* to_addr=ip:port (for client)
* peer_public_key=HEX
* so_mark=number
* netif=interface_name
*/
utun_config_t* parse_config(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);
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_PARSER_H */

1
config_parser.h

@ -0,0 +1 @@
src/config_parser.h

129
connection.h

@ -1,129 +0,0 @@
// connection.h - Минималистичный API для защищенных UDP подключений
#ifndef CONNECTION_H
#define CONNECTION_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Непрозрачный дескриптор подключения */
typedef struct conn_handle conn_handle_t;
/* Forward declaration для uasync */
typedef struct uasync_s uasync_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; /* Джиттер в миллисекундах */
} conn_stats_t;
int conn_get_stats(conn_handle_t* conn, conn_stats_t* stats);
#ifdef __cplusplus
}
#endif
#endif /* CONNECTION_H */

1
connection.h

@ -0,0 +1 @@
src/connection.h

208
control_socket.h

@ -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 */

1
control_socket.h

@ -0,0 +1 @@
src/control_socket.h

234
etcp.h

@ -1,234 +0,0 @@
// etcp.h - Расширенный протокол управления передачей (Extended Transmission Control Protocol)
#ifndef ETCP_H
#define ETCP_H
#include <stdint.h>
#include <stddef.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
#ifdef __cplusplus
extern "C" {
#endif
// Предварительные объявления
typedef struct epkt epkt_t;
// Тип обратного вызова для отправки пакетов через UDP
typedef void (*etcp_tx_callback_t)(epkt_t* epkt, uint8_t* pkt, uint16_t len, void* arg);
// Основная структура ETCP
struct epkt {
// Очереди
ll_queue_t* tx_queue; // Очередь данных для отправки
ll_queue_t* output_queue; // Выходная очередь (собранные данные)
// Список полученных пакетов (отсортированный связанный список)
struct rx_packet* rx_list;
// Отправленные пакеты (для повторной передачи)
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
};
// Функции API
/**
* @brief Инициализировать новый экземпляр ETCP
* @param ua Экземпляр uasync для таймеров (обязательный параметр)
* @return Указатель на новый экземпляр или NULL в случае ошибки
*/
epkt_t* etcp_init(uasync_t* ua);
/**
* @brief Освободить экземпляр ETCP и все связанные ресурсы
* @param epkt Экземпляр для освобождения
*/
void etcp_free(epkt_t* epkt);
/**
* @brief Установить обратный вызов для отправки пакетов через UDP
* @param epkt Экземпляр ETCP
* @param cb Функция обратного вызова
* @param arg Пользовательский аргумент, передаваемый в обратный вызов
*/
void etcp_set_callback(epkt_t* epkt, etcp_tx_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);
#ifdef __cplusplus
}
#endif
#endif // ETCP_H

1
etcp.h

@ -0,0 +1 @@
src/etcp.h

141
ll_queue.h

@ -1,141 +0,0 @@
#ifndef LL_QUEUE_H
#define LL_QUEUE_H
#include <stddef.h> // для size_t
// Предварительные объявления
typedef struct ll_queue ll_queue_t;
typedef struct uasync_s uasync_t;
typedef struct ll_entry ll_entry_t;
// Тип коллбэка: вызывается при добавлении элемента в пустую очередь или для продолжения обработки
// Параметры: указатель на очередь, указатель на элемент (первый в очереди), пользовательский аргумент
typedef void (*queue_callback_t)(ll_queue_t* q, ll_entry_t* entry, void* arg);
// Структура элемента - переменный размер, данные расположены сразу после структуры
struct ll_entry {
struct ll_entry* next; // Указатель на следующий элемент в очереди
size_t size; // Размер данных элемента (байт)
};
// Структура условия ожидания (waiter)
struct queue_waiter {
int max_packets; // Максимальное количество пакетов
size_t max_bytes; // Максимальное количество байт
void (*callback)(ll_queue_t* q, void* arg); // Коллбэк для вызова
void* callback_arg; // Аргумент коллбэка
struct queue_waiter* next; // Следующий ожидающий в списке
};
typedef struct queue_waiter queue_waiter_t;
typedef void (*queue_threshold_callback_t)(ll_queue_t* q, void* arg);
// Структура очереди
struct ll_queue {
ll_entry_t* head; // Первый элемент (извлекается отсюда)
ll_entry_t* tail; // Последний элемент (добавляется сюда)
int count; // Текущее количество элементов
size_t total_bytes; // Общий размер данных всех элементов (байт)
int size_limit; // Максимальное количество (-1 = без ограничения)
queue_callback_t callback; // Функция коллбэка
void* callback_arg; // Пользовательский аргумент для коллбэка
int callback_suspended; // 1 если коллбэки приостановлены (во время обработки)
void* resume_timeout_id; // ID таймаута uasync для отложенного возобновления
uasync_t* ua; // Экземпляр uasync для таймеров
queue_waiter_t* waiters; // Список ожидающих коллбэков
};
// ==================== Управление очередью ====================
// Создать новую пустую очередь
// ua - экземпляр uasync для таймеров (обязательный параметр)
// Возвращает: указатель на очередь или NULL при ошибке выделения памяти
ll_queue_t* queue_new(uasync_t* ua);
// Освободить очередь и все её элементы
// Также отменяет отложенное возобновление если оно запланировано
void queue_free(ll_queue_t* q);
// ==================== Конфигурация очереди ====================
// Установить функцию и аргумент коллбэка для очереди
// Коллбэк вызывается при добавлении элемента в пустую очередь (разрешенные коллбэки)
// обработчик должен обработать этот пакет и когда будет готов к приёму следующего - вызывает resume_callback. обработка строго по одному пакету.
void queue_set_callback(ll_queue_t* q, queue_callback_t cbk_fn, void* arg);
// Возобновить коллбэки после обработки элемента переданного в коллбэке (тянуть дополнительные элементы из очереди не предусмотернные api нельзя).
// эта функция должна вызываться всегда после того как cbk_fn обработала пакет (можно с ожиданием через async), иначе очередь застрянет.
// Если в очереди остались элементы, запланирует вызов коллбэка через uasync_set_timeout(0)
// Это предотвращает накопление рекурсии в стеке вызовов
void queue_resume_callback(ll_queue_t* q);
// Установить максимальное количество элементов в очереди
// При превышении лимита новый элемент автоматически освобождается
void queue_set_size_limit(ll_queue_t* q, int lim);
// ==================== Управление элементами ====================
// Создать новый элемент с областью данных указанного размера
// Память выделяется одним блоком: [ll_entry_t][область данных data_size байт]
// Возвращает: указатель на элемент или NULL при ошибке выделения памяти
ll_entry_t* queue_entry_new(size_t data_size);
// Освободить элемент (не влияет на связи в очереди)
void queue_entry_free(ll_entry_t* entry);
// ==================== Операции с очередью ====================
// Добавить элемент в конец очереди (FIFO)
// Если очередь была пустой и коллбэки разрешены - вызывает коллбэк
// Возвращает: 0 при успехе, -1 если превышен лимит размера (элемент освобожден)
int queue_entry_put(ll_queue_t* q, ll_entry_t* entry);
// Добавить элемент в начало очереди (LIFO, высокий приоритет)
// Если очередь была пустой и коллбэки разрешены - вызывает коллбэк
// Возвращает: 0 при успехе, -1 если превышен лимит размера (элемент освобожден)
int queue_entry_put_first(ll_queue_t* q, ll_entry_t* entry);
// Извлечь элемент из начала очереди
// При извлечении приостанавливает коллбэки (callback_suspended = 1) чтобы предотвратить рекурсию
// Возвращает: указатель на элемент или NULL если очередь пуста
ll_entry_t* queue_entry_get(ll_queue_t* q);
// Получить текущее количество элементов в очереди
int queue_entry_count(ll_queue_t* q);
// ==================== Вспомогательные функции ====================
// Получить указатель на область данных элемента
// Данные расположены сразу после структуры ll_entry_t
static inline void* ll_entry_data(ll_entry_t* entry) {
return (void*)(entry + 1);
}
// Получить размер данных элемента
static inline size_t ll_entry_size(ll_entry_t* entry) {
return entry->size;
}
// ==================== Асинхронное ожидание ====================
// Зарегистрировать коллбэк, который будет вызван когда очередь будет иметь
// не более max_packets пакетов и не более max_bytes байт.
// Если условие уже выполнено, коллбэк вызывается немедленно.
// Можно зарегистрировать несколько ожиданий на одной очереди.
// Возвращает указатель на waiter для возможной отмены через queue_cancel_wait
queue_waiter_t* queue_wait_threshold(ll_queue_t* q, int max_packets, size_t max_bytes,
queue_threshold_callback_t callback, void* arg);
// Отменить ожидание (удалить waiter из списка)
void queue_cancel_wait(ll_queue_t* q, queue_waiter_t* waiter);
// Получить общий размер данных в очереди (байт)
static inline size_t queue_total_bytes(ll_queue_t* q) {
if (!q) return 0;
return q->total_bytes;
}
#endif // LL_QUEUE_H

1
ll_queue.h

@ -0,0 +1 @@
src/ll_queue.h

2
monitor.py

@ -25,7 +25,7 @@ CONTROL_ERR_NONE = 0
CONTROL_ERR_INVALID_CMD = 1
CONTROL_ERR_INTERNAL = 2
def send_command(host='127.0.0.1', port=5555, command=CONTROL_CMD_GET_STATS, sequence=1):
def send_command(host='192.168.29.117', port=5555, command=CONTROL_CMD_GET_STATS, sequence=1):
"""Send a control command and receive response"""
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(2.0)

75
pkt_normalizer.h

@ -1,75 +0,0 @@
// pkt_normalizer.h
#ifndef PKT_NORMALIZER_H
#define PKT_NORMALIZER_H
#include "ll_queue.h"
#include "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
typedef struct pn_struct pn_struct;
typedef struct pkt_normalizer_pair pkt_normalizer_pair;
/* Service packet callback type */
typedef void (*pkt_normalizer_service_callback_t)(void* user_data, uint8_t type, const uint8_t* data, size_t len);
struct pn_struct {
ll_queue_t* input;
ll_queue_t* output;
uasync_t* ua;
int is_packer;
union {
struct {
uint8_t* buf;
size_t len;
size_t cap;
int error_count;
} packer;
struct {
uint8_t* buf; /* буфер для сборки фрагментов */
size_t len; /* текущая накопленная длина */
size_t total_len; /* ожидаемая общая длина из первого фрагмента */
size_t cap; /* ёмкость буфера */
int error_count; /* счетчик ошибок сборки */
int in_fragment; /* флаг: идет сборка фрагментов (1) или нет (0) */
/* Service packet reassembly */
uint8_t* service_buf; /* буфер для сборки сервисных пакетов */
size_t service_len; /* текущая накопленная длина сервисного пакета */
size_t service_cap; /* ёмкость буфера сервисного пакета */
uint8_t service_type; /* тип сервисного пакета */
int in_service; /* флаг: идет сборка сервисного пакета (1) или нет (0) */
} unpacker;
} u;
/* Service packet callback */
pkt_normalizer_service_callback_t service_callback;
void* service_callback_user_data;
};
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);
void pkt_normalizer_pair_deinit(pkt_normalizer_pair* pair);
/* Error handling */
int pkt_normalizer_get_error_count(const pn_struct* pn);
void pkt_normalizer_reset_error_count(pn_struct* pn);
/* Flush internal buffer (packer only) */
void pkt_normalizer_flush(pn_struct* pn);
int pkt_normalizer_send_service(pn_struct* pn, uint8_t type, const void* data, size_t len);
void pkt_normalizer_set_service_callback(pn_struct* pn, pkt_normalizer_service_callback_t callback, void* user_data);
void pkt_normalizer_reset_service_state(pn_struct* pn);
void pkt_normalizer_reset_state(pn_struct* pn);
struct pkt_normalizer_pair {
pn_struct* packer;
pn_struct* unpacker;
};
#endif // PKT_NORMALIZER_H

1
pkt_normalizer.h

@ -0,0 +1 @@
src/pkt_normalizer.h

170
routing.h

@ -1,170 +0,0 @@
/**
* @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"
// Типы маршрутов
typedef enum {
ROUTE_TYPE_STATIC = 0, // Статические маршруты, загружаемые при старте
ROUTE_TYPE_DYNAMIC = 1, // Динамические маршруты, полученные от соседей
ROUTE_TYPE_LOCAL = 2, // Локальные подсети
ROUTE_TYPE_LEARNED = 3 // Маршруты, полученные от других узлов
} route_type_t;
// Флаги маршрута
typedef enum {
ROUTE_FLAG_ACTIVE = (1 << 0), // Маршрут активен (лучший путь)
ROUTE_FLAG_VALIDATED = (1 << 1), // Маршрут прошёл валидацию (по разрешенным подсетям в конфиге)
ROUTE_FLAG_ADVERTISED = (1 << 2), // Маршрут анонсируется другим узлам
ROUTE_FLAG_LEARNED = (1 << 3) // Маршрут получен от соседа
} route_flags_t;
// Расширенные метрики маршрута
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)
} 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; // Время последнего использования
} 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
} routing_table_t;
/**
* @brief Создаёт улучшенную таблицу маршрутизации
* @return указатель на новую таблицу или NULL при ошибке
*/
routing_table_t *routing_table_create(void);
/**
* @brief Уничтожает таблицу маршрутизации
*/
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 Выводит содержимое таблицы маршрутизации (для отладки)
*/
void routing_table_print(const routing_table_t *table);
/**
* @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

1
routing.h

@ -0,0 +1 @@
src/routing.h

119
sc_lib.h

@ -1,119 +0,0 @@
#ifndef SECURE_CHANNEL_H
#define SECURE_CHANNEL_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ===== Константы ===== */
#define SC_PUBKEY_SIZE 64 /* secp256r1: X || Y */
#define SC_PRIVKEY_SIZE 32
#define SC_SHARED_SECRET_SIZE 32
#define SC_SESSION_KEY_SIZE 16 /* AES-128 key size */
#define SC_NONCE_SIZE 13
#define SC_TAG_SIZE 16
/* ===== Коды ошибок ===== */
typedef enum {
SC_OK = 0,
SC_ERR_INVALID_ARG,
SC_ERR_CRYPTO,
SC_ERR_NOT_INITIALIZED,
SC_ERR_AUTH_FAILED
} sc_status_t;
/* ===== Контекст защищённого канала ===== */
typedef struct
{
/* Локальные ключи */
uint8_t private_key[SC_PRIVKEY_SIZE];
uint8_t public_key[SC_PUBKEY_SIZE];
/* Ключ пира */
uint8_t peer_public_key[SC_PUBKEY_SIZE];
uint8_t peer_key_set;
/* Производные ключи */
uint8_t session_key[SC_SESSION_KEY_SIZE];
/* Nonce / counters */
uint32_t tx_counter;
uint32_t rx_counter;
/* Флаги состояния */
uint8_t initialized;
uint8_t session_ready;
} sc_context_t;
/* ===== API ===== */
/**
* @brief Сгенерировать пару ECDH ключей
*
* @param ctx Контекст
* @return SC_OK или ошибка
*/
sc_status_t sc_generate_keypair(sc_context_t *ctx);
/**
* @brief Инициализация контекста с уже существующими ключами
*
* @param ctx Контекст
* @param public_key Указатель на публичный ключ (64 байта)
* @param private_key Указатель на приватный ключ (32 байта)
*/
sc_status_t sc_init_local_keys(sc_context_t *ctx,
const uint8_t *public_key,
const uint8_t *private_key);
/**
* @brief Установить публичный ключ пира и вычислить session key
*
* @param ctx Контекст
* @param peer_public_key Публичный ключ пира (64 байта)
*/
sc_status_t sc_set_peer_public_key(sc_context_t *ctx,
const uint8_t *peer_public_key);
/**
* @brief Зашифровать сообщение
*
* @param ctx Контекст
* @param plaintext Входные данные
* @param plaintext_len Длина
* @param ciphertext Выход (может совпадать с plaintext)
* @param tag MAC-тег (16 байт)
*/
sc_status_t sc_encrypt(sc_context_t *ctx,
const uint8_t *plaintext,
size_t plaintext_len,
uint8_t *ciphertext,
uint8_t *tag);
/**
* @brief Расшифровать и проверить сообщение
*
* @param ctx Контекст
* @param ciphertext Зашифрованные данные
* @param ciphertext_len Длина
* @param tag MAC-тег
* @param plaintext Выход
*/
sc_status_t sc_decrypt(sc_context_t *ctx,
const uint8_t *ciphertext,
size_t ciphertext_len,
const uint8_t *tag,
uint8_t *plaintext);
#ifdef __cplusplus
}
#endif
#endif /* SECURE_CHANNEL_H */

1
sc_lib.h

@ -0,0 +1 @@
src/sc_lib.h

11
settings.h

@ -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

1
settings.h

@ -0,0 +1 @@
src/settings.h

0
config_parser.c → src/config_parser.c

117
src/config_parser.h

@ -0,0 +1,117 @@
// config_parser.h - Configuration parser for utun application
#ifndef CONFIG_PARSER_H
#define CONFIG_PARSER_H
#include <stdint.h>
#include <stddef.h>
#include "connection.h"
#ifdef __cplusplus
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_ADDR_LEN 64
#define MAX_NETIF_LEN 16
#define MAX_OPTION_VALUE_LEN 256
#define MAX_ALLOWED_SUBNETS 32
// Connection mode (config parser internal)
typedef enum {
CONFIG_MODE_UNKNOWN = 0,
CONFIG_MODE_CLIENT,
CONFIG_MODE_SERVER
} config_conn_mode_t;
// Subnet entry for allowed routing
typedef struct {
char subnet[MAX_ADDR_LEN]; // Format: "192.168.0.0/24"
} subnet_entry_t;
// Connection configuration
typedef struct {
char name[MAX_CONN_NAME_LEN]; // Connection name from [connection: name]
config_conn_mode_t mode; // server or client
char local_addr[MAX_ADDR_LEN]; // For client: from_addr, for server: addr
char remote_addr[MAX_ADDR_LEN]; // For client: to_addr, for server: N/A
char peer_public_key_hex[MAX_KEY_LEN]; // Peer's public key in HEX
int so_mark; // Socket mark (0 = not set)
char netif[MAX_NETIF_LEN]; // Network interface to bind to
char tun_ifname[MAX_NETIF_LEN]; // TUN interface name (e.g., tun12)
char tun_ip[MAX_ADDR_LEN]; // TUN interface IP address (e.g., 10.0.0.1/24)
} connection_config_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
} global_config_t;
// Complete configuration
typedef struct {
global_config_t global;
connection_config_t *connections;
int connection_count;
int connection_capacity;
subnet_entry_t allowed_subnets[MAX_ALLOWED_SUBNETS];
int allowed_subnet_count;
} utun_config_t;
/**
* @brief Parse configuration file
* @param filename Path to configuration file
* @return Pointer to parsed configuration, NULL on error
*
* Parses INI-style configuration file with format:
* [global]
* option=value
* my_private_key=HEX
* my_public_key=HEX
*
* [connection: name]
* mode=server|client
* addr=ip:port (for server)
* from_addr=ip:port (for client)
* to_addr=ip:port (for client)
* peer_public_key=HEX
* so_mark=number
* netif=interface_name
*/
utun_config_t* parse_config(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);
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_PARSER_H */

0
connection.c → src/connection.c

129
src/connection.h

@ -0,0 +1,129 @@
// connection.h - Минималистичный API для защищенных UDP подключений
#ifndef CONNECTION_H
#define CONNECTION_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Непрозрачный дескриптор подключения */
typedef struct conn_handle conn_handle_t;
/* Forward declaration для uasync */
typedef struct uasync_s uasync_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; /* Джиттер в миллисекундах */
} conn_stats_t;
int conn_get_stats(conn_handle_t* conn, conn_stats_t* stats);
#ifdef __cplusplus
}
#endif
#endif /* CONNECTION_H */

0
control_socket.c → src/control_socket.c

208
src/control_socket.h

@ -0,0 +1,208 @@
/**
* @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 */

0
etcp.c → src/etcp.c

234
src/etcp.h

@ -0,0 +1,234 @@
// etcp.h - Расширенный протокол управления передачей (Extended Transmission Control Protocol)
#ifndef ETCP_H
#define ETCP_H
#include <stdint.h>
#include <stddef.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
#ifdef __cplusplus
extern "C" {
#endif
// Предварительные объявления
typedef struct epkt epkt_t;
// Тип обратного вызова для отправки пакетов через UDP
typedef void (*etcp_tx_callback_t)(epkt_t* epkt, uint8_t* pkt, uint16_t len, void* arg);
// Основная структура ETCP
struct epkt {
// Очереди
ll_queue_t* tx_queue; // Очередь данных для отправки
ll_queue_t* output_queue; // Выходная очередь (собранные данные)
// Список полученных пакетов (отсортированный связанный список)
struct rx_packet* rx_list;
// Отправленные пакеты (для повторной передачи)
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
};
// Функции API
/**
* @brief Инициализировать новый экземпляр ETCP
* @param ua Экземпляр uasync для таймеров (обязательный параметр)
* @return Указатель на новый экземпляр или NULL в случае ошибки
*/
epkt_t* etcp_init(uasync_t* ua);
/**
* @brief Освободить экземпляр ETCP и все связанные ресурсы
* @param epkt Экземпляр для освобождения
*/
void etcp_free(epkt_t* epkt);
/**
* @brief Установить обратный вызов для отправки пакетов через UDP
* @param epkt Экземпляр ETCP
* @param cb Функция обратного вызова
* @param arg Пользовательский аргумент, передаваемый в обратный вызов
*/
void etcp_set_callback(epkt_t* epkt, etcp_tx_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);
#ifdef __cplusplus
}
#endif
#endif // ETCP_H

0
ll_queue.c → src/ll_queue.c

141
src/ll_queue.h

@ -0,0 +1,141 @@
#ifndef LL_QUEUE_H
#define LL_QUEUE_H
#include <stddef.h> // для size_t
// Предварительные объявления
typedef struct ll_queue ll_queue_t;
typedef struct uasync_s uasync_t;
typedef struct ll_entry ll_entry_t;
// Тип коллбэка: вызывается при добавлении элемента в пустую очередь или для продолжения обработки
// Параметры: указатель на очередь, указатель на элемент (первый в очереди), пользовательский аргумент
typedef void (*queue_callback_t)(ll_queue_t* q, ll_entry_t* entry, void* arg);
// Структура элемента - переменный размер, данные расположены сразу после структуры
struct ll_entry {
struct ll_entry* next; // Указатель на следующий элемент в очереди
size_t size; // Размер данных элемента (байт)
};
// Структура условия ожидания (waiter)
struct queue_waiter {
int max_packets; // Максимальное количество пакетов
size_t max_bytes; // Максимальное количество байт
void (*callback)(ll_queue_t* q, void* arg); // Коллбэк для вызова
void* callback_arg; // Аргумент коллбэка
struct queue_waiter* next; // Следующий ожидающий в списке
};
typedef struct queue_waiter queue_waiter_t;
typedef void (*queue_threshold_callback_t)(ll_queue_t* q, void* arg);
// Структура очереди
struct ll_queue {
ll_entry_t* head; // Первый элемент (извлекается отсюда)
ll_entry_t* tail; // Последний элемент (добавляется сюда)
int count; // Текущее количество элементов
size_t total_bytes; // Общий размер данных всех элементов (байт)
int size_limit; // Максимальное количество (-1 = без ограничения)
queue_callback_t callback; // Функция коллбэка
void* callback_arg; // Пользовательский аргумент для коллбэка
int callback_suspended; // 1 если коллбэки приостановлены (во время обработки)
void* resume_timeout_id; // ID таймаута uasync для отложенного возобновления
uasync_t* ua; // Экземпляр uasync для таймеров
queue_waiter_t* waiters; // Список ожидающих коллбэков
};
// ==================== Управление очередью ====================
// Создать новую пустую очередь
// ua - экземпляр uasync для таймеров (обязательный параметр)
// Возвращает: указатель на очередь или NULL при ошибке выделения памяти
ll_queue_t* queue_new(uasync_t* ua);
// Освободить очередь и все её элементы
// Также отменяет отложенное возобновление если оно запланировано
void queue_free(ll_queue_t* q);
// ==================== Конфигурация очереди ====================
// Установить функцию и аргумент коллбэка для очереди
// Коллбэк вызывается при добавлении элемента в пустую очередь (разрешенные коллбэки)
// обработчик должен обработать этот пакет и когда будет готов к приёму следующего - вызывает resume_callback. обработка строго по одному пакету.
void queue_set_callback(ll_queue_t* q, queue_callback_t cbk_fn, void* arg);
// Возобновить коллбэки после обработки элемента переданного в коллбэке (тянуть дополнительные элементы из очереди не предусмотернные api нельзя).
// эта функция должна вызываться всегда после того как cbk_fn обработала пакет (можно с ожиданием через async), иначе очередь застрянет.
// Если в очереди остались элементы, запланирует вызов коллбэка через uasync_set_timeout(0)
// Это предотвращает накопление рекурсии в стеке вызовов
void queue_resume_callback(ll_queue_t* q);
// Установить максимальное количество элементов в очереди
// При превышении лимита новый элемент автоматически освобождается
void queue_set_size_limit(ll_queue_t* q, int lim);
// ==================== Управление элементами ====================
// Создать новый элемент с областью данных указанного размера
// Память выделяется одним блоком: [ll_entry_t][область данных data_size байт]
// Возвращает: указатель на элемент или NULL при ошибке выделения памяти
ll_entry_t* queue_entry_new(size_t data_size);
// Освободить элемент (не влияет на связи в очереди)
void queue_entry_free(ll_entry_t* entry);
// ==================== Операции с очередью ====================
// Добавить элемент в конец очереди (FIFO)
// Если очередь была пустой и коллбэки разрешены - вызывает коллбэк
// Возвращает: 0 при успехе, -1 если превышен лимит размера (элемент освобожден)
int queue_entry_put(ll_queue_t* q, ll_entry_t* entry);
// Добавить элемент в начало очереди (LIFO, высокий приоритет)
// Если очередь была пустой и коллбэки разрешены - вызывает коллбэк
// Возвращает: 0 при успехе, -1 если превышен лимит размера (элемент освобожден)
int queue_entry_put_first(ll_queue_t* q, ll_entry_t* entry);
// Извлечь элемент из начала очереди
// При извлечении приостанавливает коллбэки (callback_suspended = 1) чтобы предотвратить рекурсию
// Возвращает: указатель на элемент или NULL если очередь пуста
ll_entry_t* queue_entry_get(ll_queue_t* q);
// Получить текущее количество элементов в очереди
int queue_entry_count(ll_queue_t* q);
// ==================== Вспомогательные функции ====================
// Получить указатель на область данных элемента
// Данные расположены сразу после структуры ll_entry_t
static inline void* ll_entry_data(ll_entry_t* entry) {
return (void*)(entry + 1);
}
// Получить размер данных элемента
static inline size_t ll_entry_size(ll_entry_t* entry) {
return entry->size;
}
// ==================== Асинхронное ожидание ====================
// Зарегистрировать коллбэк, который будет вызван когда очередь будет иметь
// не более max_packets пакетов и не более max_bytes байт.
// Если условие уже выполнено, коллбэк вызывается немедленно.
// Можно зарегистрировать несколько ожиданий на одной очереди.
// Возвращает указатель на waiter для возможной отмены через queue_cancel_wait
queue_waiter_t* queue_wait_threshold(ll_queue_t* q, int max_packets, size_t max_bytes,
queue_threshold_callback_t callback, void* arg);
// Отменить ожидание (удалить waiter из списка)
void queue_cancel_wait(ll_queue_t* q, queue_waiter_t* waiter);
// Получить общий размер данных в очереди (байт)
static inline size_t queue_total_bytes(ll_queue_t* q) {
if (!q) return 0;
return q->total_bytes;
}
#endif // LL_QUEUE_H

0
pkt_normalizer.c → src/pkt_normalizer.c

75
src/pkt_normalizer.h

@ -0,0 +1,75 @@
// pkt_normalizer.h
#ifndef PKT_NORMALIZER_H
#define PKT_NORMALIZER_H
#include "ll_queue.h"
#include "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
typedef struct pn_struct pn_struct;
typedef struct pkt_normalizer_pair pkt_normalizer_pair;
/* Service packet callback type */
typedef void (*pkt_normalizer_service_callback_t)(void* user_data, uint8_t type, const uint8_t* data, size_t len);
struct pn_struct {
ll_queue_t* input;
ll_queue_t* output;
uasync_t* ua;
int is_packer;
union {
struct {
uint8_t* buf;
size_t len;
size_t cap;
int error_count;
} packer;
struct {
uint8_t* buf; /* буфер для сборки фрагментов */
size_t len; /* текущая накопленная длина */
size_t total_len; /* ожидаемая общая длина из первого фрагмента */
size_t cap; /* ёмкость буфера */
int error_count; /* счетчик ошибок сборки */
int in_fragment; /* флаг: идет сборка фрагментов (1) или нет (0) */
/* Service packet reassembly */
uint8_t* service_buf; /* буфер для сборки сервисных пакетов */
size_t service_len; /* текущая накопленная длина сервисного пакета */
size_t service_cap; /* ёмкость буфера сервисного пакета */
uint8_t service_type; /* тип сервисного пакета */
int in_service; /* флаг: идет сборка сервисного пакета (1) или нет (0) */
} unpacker;
} u;
/* Service packet callback */
pkt_normalizer_service_callback_t service_callback;
void* service_callback_user_data;
};
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);
void pkt_normalizer_pair_deinit(pkt_normalizer_pair* pair);
/* Error handling */
int pkt_normalizer_get_error_count(const pn_struct* pn);
void pkt_normalizer_reset_error_count(pn_struct* pn);
/* Flush internal buffer (packer only) */
void pkt_normalizer_flush(pn_struct* pn);
int pkt_normalizer_send_service(pn_struct* pn, uint8_t type, const void* data, size_t len);
void pkt_normalizer_set_service_callback(pn_struct* pn, pkt_normalizer_service_callback_t callback, void* user_data);
void pkt_normalizer_reset_service_state(pn_struct* pn);
void pkt_normalizer_reset_state(pn_struct* pn);
struct pkt_normalizer_pair {
pn_struct* packer;
pn_struct* unpacker;
};
#endif // PKT_NORMALIZER_H

0
routing.c → src/routing.c

170
src/routing.h

@ -0,0 +1,170 @@
/**
* @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"
// Типы маршрутов
typedef enum {
ROUTE_TYPE_STATIC = 0, // Статические маршруты, загружаемые при старте
ROUTE_TYPE_DYNAMIC = 1, // Динамические маршруты, полученные от соседей
ROUTE_TYPE_LOCAL = 2, // Локальные подсети
ROUTE_TYPE_LEARNED = 3 // Маршруты, полученные от других узлов
} route_type_t;
// Флаги маршрута
typedef enum {
ROUTE_FLAG_ACTIVE = (1 << 0), // Маршрут активен (лучший путь)
ROUTE_FLAG_VALIDATED = (1 << 1), // Маршрут прошёл валидацию (по разрешенным подсетям в конфиге)
ROUTE_FLAG_ADVERTISED = (1 << 2), // Маршрут анонсируется другим узлам
ROUTE_FLAG_LEARNED = (1 << 3) // Маршрут получен от соседа
} route_flags_t;
// Расширенные метрики маршрута
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)
} 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; // Время последнего использования
} 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
} routing_table_t;
/**
* @brief Создаёт улучшенную таблицу маршрутизации
* @return указатель на новую таблицу или NULL при ошибке
*/
routing_table_t *routing_table_create(void);
/**
* @brief Уничтожает таблицу маршрутизации
*/
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 Выводит содержимое таблицы маршрутизации (для отладки)
*/
void routing_table_print(const routing_table_t *table);
/**
* @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

0
sc_lib.c → src/sc_lib.c

119
src/sc_lib.h

@ -0,0 +1,119 @@
#ifndef SECURE_CHANNEL_H
#define SECURE_CHANNEL_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ===== Константы ===== */
#define SC_PUBKEY_SIZE 64 /* secp256r1: X || Y */
#define SC_PRIVKEY_SIZE 32
#define SC_SHARED_SECRET_SIZE 32
#define SC_SESSION_KEY_SIZE 16 /* AES-128 key size */
#define SC_NONCE_SIZE 13
#define SC_TAG_SIZE 16
/* ===== Коды ошибок ===== */
typedef enum {
SC_OK = 0,
SC_ERR_INVALID_ARG,
SC_ERR_CRYPTO,
SC_ERR_NOT_INITIALIZED,
SC_ERR_AUTH_FAILED
} sc_status_t;
/* ===== Контекст защищённого канала ===== */
typedef struct
{
/* Локальные ключи */
uint8_t private_key[SC_PRIVKEY_SIZE];
uint8_t public_key[SC_PUBKEY_SIZE];
/* Ключ пира */
uint8_t peer_public_key[SC_PUBKEY_SIZE];
uint8_t peer_key_set;
/* Производные ключи */
uint8_t session_key[SC_SESSION_KEY_SIZE];
/* Nonce / counters */
uint32_t tx_counter;
uint32_t rx_counter;
/* Флаги состояния */
uint8_t initialized;
uint8_t session_ready;
} sc_context_t;
/* ===== API ===== */
/**
* @brief Сгенерировать пару ECDH ключей
*
* @param ctx Контекст
* @return SC_OK или ошибка
*/
sc_status_t sc_generate_keypair(sc_context_t *ctx);
/**
* @brief Инициализация контекста с уже существующими ключами
*
* @param ctx Контекст
* @param public_key Указатель на публичный ключ (64 байта)
* @param private_key Указатель на приватный ключ (32 байта)
*/
sc_status_t sc_init_local_keys(sc_context_t *ctx,
const uint8_t *public_key,
const uint8_t *private_key);
/**
* @brief Установить публичный ключ пира и вычислить session key
*
* @param ctx Контекст
* @param peer_public_key Публичный ключ пира (64 байта)
*/
sc_status_t sc_set_peer_public_key(sc_context_t *ctx,
const uint8_t *peer_public_key);
/**
* @brief Зашифровать сообщение
*
* @param ctx Контекст
* @param plaintext Входные данные
* @param plaintext_len Длина
* @param ciphertext Выход (может совпадать с plaintext)
* @param tag MAC-тег (16 байт)
*/
sc_status_t sc_encrypt(sc_context_t *ctx,
const uint8_t *plaintext,
size_t plaintext_len,
uint8_t *ciphertext,
uint8_t *tag);
/**
* @brief Расшифровать и проверить сообщение
*
* @param ctx Контекст
* @param ciphertext Зашифрованные данные
* @param ciphertext_len Длина
* @param tag MAC-тег
* @param plaintext Выход
*/
sc_status_t sc_decrypt(sc_context_t *ctx,
const uint8_t *ciphertext,
size_t ciphertext_len,
const uint8_t *tag,
uint8_t *plaintext);
#ifdef __cplusplus
}
#endif
#endif /* SECURE_CHANNEL_H */

0
settings.c → src/settings.c

11
src/settings.h

@ -0,0 +1,11 @@
// settings.h
#ifndef SETTINGS_H
#define SETTINGS_H
typedef struct {
int max_fragment_size;
} settings_t;
extern settings_t settings;
#endif // SETTINGS_H

0
timeout_heap.c → src/timeout_heap.c

92
src/timeout_heap.h

@ -0,0 +1,92 @@
// timeout_heap.h
#ifndef TIMEOUT_HEAP_H
#define TIMEOUT_HEAP_H
#include <stdint.h> // For uint64_t
#include <stddef.h> // For size_t
typedef uint64_t TimeoutTime; // e.g., milliseconds since epoch or from now
typedef struct {
TimeoutTime expiration; // Sort key (smaller = earlier)
void *data; // User data (e.g., callback or ID)
int deleted; // 0 = active, 1 = deleted
} TimeoutEntry;
typedef struct TimeoutHeap TimeoutHeap;
struct TimeoutHeap {
TimeoutEntry *heap; // Dynamic array
size_t size; // Current number of elements
size_t capacity; // Allocated size
size_t freed_count; // Number of freed timer nodes
void* user_data; // User data for free callback
void (*free_callback)(void* user_data, void* data); // Callback to free data
};
/**
* Create a new timeout heap with initial capacity.
* @param initial_capacity Starting capacity (will grow as needed).
* @return Pointer to the heap, or NULL on failure.
*/
TimeoutHeap *timeout_heap_create(size_t initial_capacity);
/**
* Destroy the timeout heap and free resources.
* @param h The heap to destroy.
*/
void timeout_heap_destroy(TimeoutHeap *h);
/**
* Set a callback function to free data when deleted nodes are removed.
* @param h The heap.
* @param user_data User data passed to callback.
* @param callback Callback function (if NULL, data is freed with free()).
*/
void timeout_heap_set_free_callback(TimeoutHeap *h, void* user_data, void (*callback)(void* user_data, void* data));
/**
* Insert a new timeout into the heap.
* @param h The heap.
* @param expiration The expiration time.
* @param data User data associated with the timeout.
* @return 0 on success, -1 on allocation failure.
*/
int timeout_heap_push(TimeoutHeap *h, TimeoutTime expiration, void *data);
/**
* Peek at the earliest non-deleted timeout without removing it.
* @param h The heap.
* @param out Where to store the entry.
* @return 0 on success, -1 if empty.
*/
int timeout_heap_peek(TimeoutHeap *h, TimeoutEntry *out);
/**
* Pop the earliest non-deleted timeout from the heap.
* @param h The heap.
* @param out Where to store the entry.
* @return 0 on success, -1 if empty.
*/
int timeout_heap_pop(TimeoutHeap *h, TimeoutEntry *out);
/**
* Cancel a timeout by matching expiration and data.
* Scans the heap linearly, so O(n) time.
* Assumes combinations are unique; cancels the first match.
* @param h The heap.
* @param expiration The expiration time to match.
* @param data The data to match.
* @return 0 if found and canceled, -1 if not found.
*/
int timeout_heap_cancel(TimeoutHeap *h, TimeoutTime expiration, void *data);
/**
* Get the number of freed timer nodes.
* @param h The heap.
* @return Count of freed timer nodes.
*/
size_t timeout_heap_get_freed_count(TimeoutHeap *h);
#endif // TIMEOUT_HEAP_H

0
tun_if.c → src/tun_if.c

95
src/tun_if.h

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

0
u_async.c → src/u_async.c

47
src/u_async.h

@ -0,0 +1,47 @@
// uasync.h
// модуль асинхронных операций. добавляем сокеты и таймауты и mainloop их обслуживает.
#ifndef UASYNC_H
#define UASYNC_H
#include <sys/time.h>
#include <sys/select.h>
#include <stddef.h>
typedef void (*timeout_callback_t)(void* user_arg);// передаёт user_arg из uasync_set_timeout
typedef void (*socket_callback_t)(int fd, void* user_arg);// передаёт user_arg из uasync_add_socket
// user_arg полезен если нужно передать управляющую структуру. Ее можно выделить в памяти и в ней хранить всё что надо. т.е. при set_timeout передаём и получаем ее в callback-е
// Error type
typedef int err_t;
#define ERR_OK 0
#define ERR_FAIL -1
// Opaque uasync instance handle
typedef struct uasync_s uasync_t;
// Instance API - основной API для работы с uasync
uasync_t* uasync_create(void);
void uasync_destroy(uasync_t* ua);
void uasync_init_instance(uasync_t* ua);
// Timeouts, timebase = 0.1 mS
void* uasync_set_timeout(uasync_t* ua, int timeout_tb, void* user_arg, timeout_callback_t callback);
err_t uasync_cancel_timeout(uasync_t* ua, void* t_id);
// Sockets
void* uasync_add_socket(uasync_t* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_arg);
err_t uasync_remove_socket(uasync_t* ua, void* s_id);
// Single iteration of event loop with timeout (timebase units)
void uasync_poll(uasync_t* ua, int timeout_tb);
// Mainloop (бесконечный цикл, __noreturn)
void uasync_mainloop(uasync_t* ua);
// Debug statistics
void uasync_get_stats(uasync_t* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free);
#endif // UASYNC_H

0
utun.c → src/utun.c

30
src/utun_state.h

@ -0,0 +1,30 @@
/**
* @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 */

92
timeout_heap.h

@ -1,92 +0,0 @@
// timeout_heap.h
#ifndef TIMEOUT_HEAP_H
#define TIMEOUT_HEAP_H
#include <stdint.h> // For uint64_t
#include <stddef.h> // For size_t
typedef uint64_t TimeoutTime; // e.g., milliseconds since epoch or from now
typedef struct {
TimeoutTime expiration; // Sort key (smaller = earlier)
void *data; // User data (e.g., callback or ID)
int deleted; // 0 = active, 1 = deleted
} TimeoutEntry;
typedef struct TimeoutHeap TimeoutHeap;
struct TimeoutHeap {
TimeoutEntry *heap; // Dynamic array
size_t size; // Current number of elements
size_t capacity; // Allocated size
size_t freed_count; // Number of freed timer nodes
void* user_data; // User data for free callback
void (*free_callback)(void* user_data, void* data); // Callback to free data
};
/**
* Create a new timeout heap with initial capacity.
* @param initial_capacity Starting capacity (will grow as needed).
* @return Pointer to the heap, or NULL on failure.
*/
TimeoutHeap *timeout_heap_create(size_t initial_capacity);
/**
* Destroy the timeout heap and free resources.
* @param h The heap to destroy.
*/
void timeout_heap_destroy(TimeoutHeap *h);
/**
* Set a callback function to free data when deleted nodes are removed.
* @param h The heap.
* @param user_data User data passed to callback.
* @param callback Callback function (if NULL, data is freed with free()).
*/
void timeout_heap_set_free_callback(TimeoutHeap *h, void* user_data, void (*callback)(void* user_data, void* data));
/**
* Insert a new timeout into the heap.
* @param h The heap.
* @param expiration The expiration time.
* @param data User data associated with the timeout.
* @return 0 on success, -1 on allocation failure.
*/
int timeout_heap_push(TimeoutHeap *h, TimeoutTime expiration, void *data);
/**
* Peek at the earliest non-deleted timeout without removing it.
* @param h The heap.
* @param out Where to store the entry.
* @return 0 on success, -1 if empty.
*/
int timeout_heap_peek(TimeoutHeap *h, TimeoutEntry *out);
/**
* Pop the earliest non-deleted timeout from the heap.
* @param h The heap.
* @param out Where to store the entry.
* @return 0 on success, -1 if empty.
*/
int timeout_heap_pop(TimeoutHeap *h, TimeoutEntry *out);
/**
* Cancel a timeout by matching expiration and data.
* Scans the heap linearly, so O(n) time.
* Assumes combinations are unique; cancels the first match.
* @param h The heap.
* @param expiration The expiration time to match.
* @param data The data to match.
* @return 0 if found and canceled, -1 if not found.
*/
int timeout_heap_cancel(TimeoutHeap *h, TimeoutTime expiration, void *data);
/**
* Get the number of freed timer nodes.
* @param h The heap.
* @return Count of freed timer nodes.
*/
size_t timeout_heap_get_freed_count(TimeoutHeap *h);
#endif // TIMEOUT_HEAP_H

1
timeout_heap.h

@ -0,0 +1 @@
src/timeout_heap.h

1
todo.txt

@ -1 +0,0 @@


95
tun_if.h

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

1
tun_if.h

@ -0,0 +1 @@
src/tun_if.h

304
u_async.c.backup

@ -1,304 +0,0 @@
// uasync.c
#include "u_async.h"
#include "timeout_heap.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#ifndef FD_SETSIZE
#define FD_SETSIZE 1024 // Assume standard size; adjust if needed for your platform
#endif
// Timeout node
struct timeout_node {
void* arg;
timeout_callback_t callback;
uint64_t expiration_ms; // absolute expiration time in milliseconds
};
// Socket node
struct socket_node {
int fd;
socket_callback_t read_cbk;
socket_callback_t write_cbk;
socket_callback_t except_cbk;
void* user_data;
struct socket_node* next;
};
// Global state
static TimeoutHeap* timeout_heap = NULL; // Heap for timeout management
static struct socket_node* socket_head = NULL;
static int max_fd = -1;
// New: Persistent master fd_sets, updated only on add/remove
static fd_set master_readfds;
static fd_set master_writefds;
static fd_set master_exceptfds;
// New: FD-to-node map for faster post-select lookup (addresses point 2)
static struct socket_node* fd_to_node[FD_SETSIZE];
// Helper to get current time
static void get_current_time(struct timeval* tv) {
gettimeofday(tv, NULL);
}
// Helper to add timeval: tv += dt (timebase units)
static void timeval_add_tb(struct timeval* tv, int dt) {
tv->tv_usec += (dt % 10000) * 100;
tv->tv_sec += dt / 10000 + tv->tv_usec / 1000000;
tv->tv_usec %= 1000000;
}
// Convert timeval to milliseconds (uint64_t)
static uint64_t timeval_to_ms(const struct timeval* tv) {
return (uint64_t)tv->tv_sec * 1000ULL + (uint64_t)tv->tv_usec / 1000ULL;
}
// Process expired timeouts
static void process_timeouts() {
if (!timeout_heap) return;
struct timeval now_tv;
get_current_time(&now_tv);
uint64_t now_ms = timeval_to_ms(&now_tv);
while (1) {
TimeoutEntry entry;
if (timeout_heap_peek(timeout_heap, &entry) != 0) break;
if (entry.expiration > now_ms) break;
// Pop the expired timeout
timeout_heap_pop(timeout_heap, &entry);
struct timeout_node* node = (struct timeout_node*)entry.data;
if (node && node->callback) {
node->callback(node->arg);
}
free(node);
}
}
// Compute time to next timeout
static void get_next_timeout(struct timeval* tv) {
if (!timeout_heap) {
tv->tv_sec = 0;
tv->tv_usec = 0;
return;
}
TimeoutEntry entry;
if (timeout_heap_peek(timeout_heap, &entry) != 0) {
tv->tv_sec = 0;
tv->tv_usec = 0;
return;
}
struct timeval now_tv;
get_current_time(&now_tv);
uint64_t now_ms = timeval_to_ms(&now_tv);
if (entry.expiration <= now_ms) {
tv->tv_sec = 0;
tv->tv_usec = 0;
return;
}
uint64_t delta_ms = entry.expiration - now_ms;
if (delta_ms > 86400000) { // Cap at 1 day to avoid overflow
delta_ms = 86400000;
}
tv->tv_sec = delta_ms / 1000;
tv->tv_usec = (delta_ms % 1000) * 1000;
}
void uasync_init(void) {
FD_ZERO(&master_readfds);
FD_ZERO(&master_writefds);
FD_ZERO(&master_exceptfds);
memset(fd_to_node, 0, sizeof(fd_to_node)); // Init map to NULL
if (!timeout_heap) {
timeout_heap = timeout_heap_create(16); // initial capacity 16
}
}
void* uasync_set_timeout(int timeout_tb, void* arg, timeout_callback_t callback) {
if (timeout_tb < 0 || !callback) return NULL;
if (!timeout_heap) return NULL;
struct timeout_node* node = malloc(sizeof(struct timeout_node));
if (!node) return NULL;
node->arg = arg;
node->callback = callback;
// Calculate expiration time in milliseconds
struct timeval now;
get_current_time(&now);
timeval_add_tb(&now, timeout_tb);
node->expiration_ms = timeval_to_ms(&now);
// Insert into heap
if (timeout_heap_push(timeout_heap, node->expiration_ms, node) != 0) {
free(node);
return NULL;
}
return node;
}
err_t uasync_cancel_timeout(void* t_id) {
if (!t_id || !timeout_heap) return ERR_FAIL;
struct timeout_node* node = (struct timeout_node*)t_id;
// Try to cancel from heap
if (timeout_heap_cancel(timeout_heap, node->expiration_ms, node) == 0) {
free(node);
return ERR_OK;
}
// If not found in heap (maybe already expired and removed), still free
free(node);
return ERR_FAIL;
}
void* uasync_add_socket(int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) {
if (fd < 0 || fd >= FD_SETSIZE) return NULL; // Add bounds check for map
struct socket_node* node = malloc(sizeof(struct socket_node));
if (!node) return NULL;
node->fd = fd;
node->read_cbk = read_cbk;
node->write_cbk = write_cbk;
node->except_cbk = except_cbk;
node->user_data = user_data;
node->next = socket_head;
socket_head = node;
// Update masters (point 1)
if (read_cbk) FD_SET(fd, &master_readfds);
if (write_cbk) FD_SET(fd, &master_writefds);
if (except_cbk) FD_SET(fd, &master_exceptfds);
// Update map (point 2)
fd_to_node[fd] = node;
if (fd > max_fd) max_fd = fd;
return node;
}
err_t uasync_remove_socket(void* s_id) {
if (!s_id) return ERR_FAIL;
struct socket_node* node = (struct socket_node*)s_id;
struct socket_node* cur = socket_head;
struct socket_node* prev = NULL;
while (cur) {
if (cur == node) {
if (prev) {
prev->next = cur->next;
} else {
socket_head = cur->next;
}
// Update masters (point 1)
if (node->read_cbk) FD_CLR(node->fd, &master_readfds);
if (node->write_cbk) FD_CLR(node->fd, &master_writefds);
if (node->except_cbk) FD_CLR(node->fd, &master_exceptfds);
// Update map (point 2)
fd_to_node[node->fd] = NULL;
free(cur);
// Update max_fd (simple rescan; optimize if needed by checking if removed == max_fd)
max_fd = -1;
cur = socket_head;
while (cur) {
if (cur->fd > max_fd) max_fd = cur->fd;
cur = cur->next;
}
return ERR_OK;
}
prev = cur;
cur = cur->next;
}
return ERR_FAIL;
}
void uasync_mainloop(void) {
while (1) {
uasync_poll(-1); /* infinite timeout */
}
}
void uasync_poll(int timeout_tb) {
/* Process expired timeouts */
process_timeouts();
/* Prepare select with copies of masters */
fd_set readfds = master_readfds;
fd_set writefds = master_writefds;
fd_set exceptfds = master_exceptfds;
struct timeval tv;
get_next_timeout(&tv);
/* If timeout_tb >= 0, compute timeout as min(timeout_tb, existing timer) */
if (timeout_tb >= 0) {
struct timeval user_tv;
user_tv.tv_sec = timeout_tb / 10000;
user_tv.tv_usec = (timeout_tb % 10000) * 100;
/* If no internal timer or user timeout is smaller */
if (tv.tv_sec == 0 && tv.tv_usec == 0 && (!timeout_heap || timeout_heap->size == 0)) {
tv = user_tv;
} else if (user_tv.tv_sec < tv.tv_sec ||
(user_tv.tv_sec == tv.tv_sec && user_tv.tv_usec < tv.tv_usec)) {
tv = user_tv;
}
}
struct timeval* ptv = (tv.tv_sec == 0 && tv.tv_usec == 0 && (!timeout_heap || timeout_heap->size == 0)) ? NULL : &tv;
int nfds = select(max_fd + 1, &readfds, &writefds, &exceptfds, ptv);
if (nfds < 0) {
if (errno == EINTR) return;
perror("select");
return;
}
/* Process timeouts that may have expired during select */
process_timeouts();
/* Process sockets with faster dispatch */
for (int fd = 0; nfds > 0 && fd <= max_fd; fd++) {
struct socket_node* node = fd_to_node[fd];
if (!node) continue;
if (node->except_cbk && FD_ISSET(fd, &exceptfds)) {
node->except_cbk(fd, node->user_data);
nfds--;
}
if (node->read_cbk && FD_ISSET(fd, &readfds)) {
node->read_cbk(fd, node->user_data);
nfds--;
}
if (node->write_cbk && FD_ISSET(fd, &writefds)) {
node->write_cbk(fd, node->user_data);
nfds--;
}
}
}

47
u_async.h

@ -1,47 +0,0 @@
// uasync.h
// модуль асинхронных операций. добавляем сокеты и таймауты и mainloop их обслуживает.
#ifndef UASYNC_H
#define UASYNC_H
#include <sys/time.h>
#include <sys/select.h>
#include <stddef.h>
typedef void (*timeout_callback_t)(void* user_arg);// передаёт user_arg из uasync_set_timeout
typedef void (*socket_callback_t)(int fd, void* user_arg);// передаёт user_arg из uasync_add_socket
// user_arg полезен если нужно передать управляющую структуру. Ее можно выделить в памяти и в ней хранить всё что надо. т.е. при set_timeout передаём и получаем ее в callback-е
// Error type
typedef int err_t;
#define ERR_OK 0
#define ERR_FAIL -1
// Opaque uasync instance handle
typedef struct uasync_s uasync_t;
// Instance API - основной API для работы с uasync
uasync_t* uasync_create(void);
void uasync_destroy(uasync_t* ua);
void uasync_init_instance(uasync_t* ua);
// Timeouts, timebase = 0.1 mS
void* uasync_set_timeout(uasync_t* ua, int timeout_tb, void* user_arg, timeout_callback_t callback);
err_t uasync_cancel_timeout(uasync_t* ua, void* t_id);
// Sockets
void* uasync_add_socket(uasync_t* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_arg);
err_t uasync_remove_socket(uasync_t* ua, void* s_id);
// Single iteration of event loop with timeout (timebase units)
void uasync_poll(uasync_t* ua, int timeout_tb);
// Mainloop (бесконечный цикл, __noreturn)
void uasync_mainloop(uasync_t* ua);
// Debug statistics
void uasync_get_stats(uasync_t* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free);
#endif // UASYNC_H

1
u_async.h

@ -0,0 +1 @@
src/u_async.h

30
utun_state.h

@ -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 */

1
utun_state.h

@ -0,0 +1 @@
src/utun_state.h
Loading…
Cancel
Save