45 changed files with 1593 additions and 1806 deletions
@ -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,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,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,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,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,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,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,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,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
|
||||
@ -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,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,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,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,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,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,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,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,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,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,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,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,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 */ |
||||
@ -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,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,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--; |
||||
} |
||||
} |
||||
} |
||||
@ -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,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 */ |
||||
Loading…
Reference in new issue