Browse Source
- Deleted 11 obsolete debug/isolation test files - Restored test_pkt_normalizer.c from git repository - Added test_ecc_encrypt and test_routing to Makefile (2 new functional tests) - Removed settings.o from PN_OBJS (settings.c deleted) - Added crc32.o to SC_LIB_OBJS and ETCP_OBJS to fix linker errors - Removed unused routing_get_stats() function (undefined routing_stats_t type) - Updated changelog with detailed cleanup summary Current test count: 16 tests in Makefile (14 original + 2 new)Active test files in tests/: 25 files (was 35)test_ecc_encrypt: PASStest_routing: Compilation OKv2_dev
52 changed files with 2440 additions and 6090 deletions
@ -0,0 +1,37 @@
|
||||
# AGENTS.md - uTun Development Guide |
||||
|
||||
This document provides essential information for agentic coding assistants working on the uTun VPN tunnel project. |
||||
Совместимость со станым при доработках сохранять не надо. Вместо этого надо доработать остальной код чтобы работало. Важно своевременно чистить код от старых хвостов и проверять что ничего не сломалось. Т.е. код плохо нагромождать - надо стремиться к краткости, логичности и убирать то что стало неактуальным и ненужным. |
||||
|
||||
Если не работает - добавляй отладочную информацию чтобы быстрее найти проблемное место и не рушить работающий код. |
||||
если отладочная информация будет флудить сделай ее отключаемой или подумай как ограничить ее вывод по возможности сохраняя информативность. |
||||
Добавляй в код структуры для статистики (например суммируй число ошибок, вызовов и других потенциально нужных для анализа при ошибках метрик). |
||||
добавляй в тест подсчет времени сколько каждый этап длился для лучшего понимания оптимизации |
||||
перед запуском тестов не забывай их пересобирать |
||||
|
||||
веди changelog.txt: дата время: что поменялось |
||||
задача протокола - обеспечивать минимальную задержку при большом трафике. т.е. держать необходимый минимум данных в очередях. |
||||
дизайн - однопоточный асинхронный. |
||||
все ожидающие операции - через сокеты(дескрипторы) и таймауты -> u_async |
||||
все очереди через ll_queue |
||||
главный модуль обслуживания подключения - connection |
||||
unit-tests: для каждого теста в начале файла теста описывай что и как он тестирует - кратко но указыая на нюансы если они есть. |
||||
|
||||
Перед реализацией нового модуля: |
||||
1. Проанализируй архитектуру проекта и существующие модули, их ответственность и точки взаимодействия. |
||||
2. Определи роль нового модуля в общей архитектуре и его границы ответственности. |
||||
3. Спроектируй API модуля (входные данные, выходные данные, побочные эффекты). |
||||
4. Мысленно смоделируй работу модуля в нескольких типовых сценариях, включая: |
||||
корректный поток выполнения |
||||
граничные случаи |
||||
возможные ошибки |
||||
5. Сверь получившуюся логику с архитектурой проекта и существующими модулями. |
||||
6. Если обнаружены противоречия, нарушения ответственности или избыточные зависимости — доработай API и логику модуля. |
||||
Только после этого приступай к реализации. |
||||
|
||||
## Code Style Guidelines |
||||
|
||||
комменты в коде на русском |
||||
Приоритет - простое (логически понятное, минималистичное api, но функциональное). Всегда надо думать как сделать проще и красивее. не надо нагромождать лишнего - сперва подумай действительно ли оно нужно. |
||||
|
||||
рассуждай на английском, остальное лучше на русском (отчёты, дальшейшие шаги и ответы на запросы всегда на русском) |
||||
@ -0,0 +1,25 @@
|
||||
# Список файлов для дальнейшего рефакторинга |
||||
|
||||
## Высокий приоритет |
||||
|
||||
1. **src/utun.c** (627 строк) |
||||
- Заменить `utun_state_t` на `utun_instance_t` |
||||
- Добавить get_dest_ip() и parse_subnet() |
||||
- Убрать connection_v1 код (init_connection_v2 и т.д.) |
||||
|
||||
2. **src/etcp.c** (отключен) |
||||
- Обновить для работы с новой структурой ETCP_CONN |
||||
- Убрать ссылки на etcp_channel_t и sockets массивы |
||||
|
||||
## Средний приоритет |
||||
|
||||
3. **src/routing.c** |
||||
- Обновить использовать struct ETCP_CONNECTIONS напрямую |
||||
- Убрать зависимости от conn_handle_t если есть |
||||
|
||||
4. **Тесты в tests/** - обновить для нового API |
||||
|
||||
## Низкий приоритет |
||||
|
||||
5. Полное удаление etcp_sockets.c (уже сделано) |
||||
6. Обновление Makefile (уже сделано) |
||||
@ -0,0 +1,19 @@
|
||||
Sat Jan 17 2026 21:10: Fixed build errors and implemented utun_instance structure |
||||
|
||||
CHANGES: |
||||
1. Fixed missing struct sockaddr include in connection.h |
||||
2. Removed control_socket module (functionality to be moved to etcp_socket) |
||||
3. Removed udp_socket module (functionality merged into etcp_socket) |
||||
4. Created utun_instance.h and utun_instance.c with root instance structure |
||||
5. Refactored utun.c to use utun_instance instead of utun_state |
||||
6. Fixed ETCP API mismatches (temporarily disabled ETCP integration) |
||||
7. Fixed routing_stats_t structure members |
||||
8. Cleaned up pkt_normalizer.c formatting issues |
||||
9. Updated Makefile to remove deprecated modules |
||||
10. Successfully built utun binary |
||||
|
||||
NEXT STEPS: |
||||
- Fix ETCP API integration |
||||
- Re-enable ETCP tests |
||||
- Implement proper error handling in utun_instance |
||||
- Add comprehensive tests for utun_instance |
||||
@ -1,297 +0,0 @@
|
||||
ETCP - Extended Transmission Control Protocol |
||||
============================================= |
||||
|
||||
Протокол для надежной передачи данных поверх UDP с восстановлением порядка, |
||||
повторной передачей потерянных пакетов и управлением потоком. Работает в связке |
||||
с pkt_normalizer для фрагментации/сборки и connection для криптографии. |
||||
|
||||
Ключевые принципы: |
||||
- Минимальные накладные расходы (4 байта заголовка + опциональные метаданные) |
||||
- Циклические 16-битные счетчики (ID, timestamp) для компактности |
||||
- Адаптивное управление окном на основе RTT и bandwidth |
||||
- Механизм forward progress для гарантии доставки даже при потерях |
||||
- Reset handshake для синхронизации состояния соединения |
||||
|
||||
Структура данных epkt: |
||||
----------------------- |
||||
|
||||
struct epkt { |
||||
// Очереди |
||||
ll_queue_t* tx_queue; // Данные для отправки (от app → pkt_normalizer) |
||||
ll_queue_t* output_queue; // Собранные данные (к app через pkt_normalizer) |
||||
|
||||
// Списки пакетов |
||||
struct rx_packet* rx_list; // Полученные пакеты (отсортированный по ID) |
||||
struct sent_packet* sent_list; // Отправленные пакеты (ждут ACK) |
||||
|
||||
// Идентификаторы и тайминги |
||||
uint16_t next_tx_id; // Следующий ID для передачи (1..65535, циклический) |
||||
uint16_t last_sent_id; // Последний отправленный ID (для ретрансмиссии) |
||||
uint16_t last_rx_id; // Последний полученный ID |
||||
uint16_t last_delivered_id; // Последний ID, переданный в output_queue |
||||
uint16_t last_acked_id; // Последний подтвержденный ID |
||||
uint16_t last_rx_ack_id; // Последний полученный ACK ID от пира |
||||
|
||||
// Метрики и управление потоком |
||||
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_allowed; // Разрешенные к отправке байты (накопление) |
||||
uint32_t unacked_bytes; // Байты в пути (отправлены, но не подтверждены) |
||||
uint32_t window_size; // Текущий размер окна (RTT × bandwidth × 2) |
||||
uint8_t window_blocked; // Флаг блокировки передачи |
||||
|
||||
// Буферы служебной информации |
||||
uint16_t pending_ack_ids[32]; // ID пакетов для подтверждения |
||||
uint16_t pending_ack_timestamps[32]; // Timestamps для RTT расчета |
||||
uint8_t pending_ack_count; // Количество ожидающих ACK |
||||
uint16_t pending_retransmit_ids[32]; // ID пакетов для ретрансмиссии |
||||
uint8_t pending_retransmit_count; // Количество запросов |
||||
|
||||
// Отслеживание прогресса (forward progress) |
||||
uint16_t oldest_missing_id; // Самый старый отсутствующий ID |
||||
uint16_t missing_since_time; // Время первого обнаружения пропуска |
||||
|
||||
// Reset состояние |
||||
uint8_t reset_pending; // Отправлен reset, ждем ACK |
||||
uint8_t reset_ack_received; // Получен reset ACK |
||||
uint8_t initialized; // 1 = соединение инициализировано после первого handshake |
||||
void* reset_timer; // Таймер повторной отправки reset |
||||
uint16_t reset_retry_count; // Счетчик попыток reset |
||||
etcp_reset_callback_t reset_callback; // Callback для уведомления о reset |
||||
void* reset_callback_arg; // Аргумент callback |
||||
|
||||
// Таймеры (uasync-based) |
||||
void* next_tx_timer; // Таймер следующей передачи |
||||
void* retransmit_timer; // Таймер проверки ретрансмиссий |
||||
uasync_t* ua; // Инстанс uasync |
||||
|
||||
// Callback отправки |
||||
etcp_tx_callback_t tx_callback; |
||||
void* tx_callback_arg; |
||||
|
||||
// Статистика |
||||
uint32_t retransmissions_count; // Счетчик ретрансмиссий |
||||
uint32_t ack_packets_count; // Счетчик ACK пакетов |
||||
uint32_t control_packets_count; // Счетчик управляющих пакетов |
||||
uint32_t total_packets_sent; // Всего отправлено пакетов |
||||
uint32_t unique_packets_sent; // Уникальных отправленных пакетов |
||||
uint32_t bytes_sent_total; // Всего отправлено байт |
||||
uint32_t bytes_received_total; // Всего получено байт |
||||
}; |
||||
|
||||
Формат кодограммы: |
||||
------------------ |
||||
|
||||
Каждая кодограмма состоит из обязательного заголовка и опциональных секций: |
||||
|
||||
1. Обязательный заголовок (4 байта): |
||||
[ID high][ID low][Timestamp high][Timestamp low] |
||||
- ID: 16-битный циклический номер (0 = служебный пакет без данных) |
||||
- Timestamp: время отправки в единицах 0.1 мс (циклическое) |
||||
|
||||
2. Опциональные секции (одна или несколько): |
||||
|
||||
а) Подтверждения (ACK) - заголовок 0x01: |
||||
[0x01][count][(id_hi,id_lo,ts_hi,ts_lo)×count][last_delivered_hi,lo][last_rx_hi,lo] |
||||
- count: количество пар ID+timestamp (1-32) |
||||
- last_delivered_id: последний ID, доставленный получателю |
||||
- last_rx_id: последний полученный ID (для синхронизации прогресса) |
||||
|
||||
б) Запросы ретрансмиссии - заголовок 0x10-0x2F: |
||||
[0x10+(count-1)][(id_hi,id_lo)×count][last_delivered_hi,lo][last_rx_hi,lo] |
||||
- count: (заголовок & 0x0F) + 1 (1-32) |
||||
- last_delivered_id, last_rx_id: для синхронизации состояния |
||||
|
||||
в) Полезная нагрузка - заголовок 0x00: |
||||
[0x00][данные...] |
||||
- Данные произвольной длины |
||||
|
||||
г) Reset запрос - заголовок 0x02: |
||||
[0x02] |
||||
- Инициирует reset handshake |
||||
|
||||
д) Reset подтверждение - заголовок 0x03: |
||||
[0x03] |
||||
- Подтверждение получения reset |
||||
|
||||
Пакет может содержать несколько секций (например, ACK + данные). Секция с данными |
||||
обычно идет последней. |
||||
|
||||
Очереди и потоки данных: |
||||
------------------------ |
||||
|
||||
1. Отправка (app → сеть): |
||||
app_input_queue → pkt_normalizer(packer) → tx_queue → tx_process() → UDP |
||||
|
||||
2. Прием (сеть → app): |
||||
UDP → etcp_rx_input() → rx_list → output_queue → pkt_normalizer(unpacker) → app_output_queue |
||||
|
||||
3. Управление потоком: |
||||
- tx_process() учитывает bandwidth (bytes_allowed) и window_size |
||||
- Данные блокируются при reset_pending && !reset_ack_received |
||||
- Служебные пакеты (ACK, retransmit requests) обрабатываются всегда |
||||
|
||||
Алгоритмы: |
||||
---------- |
||||
|
||||
1. Передача (tx_process): |
||||
- Проверка bandwidth: bytes_allowed накапливается со временем |
||||
- Проверка окна: unacked_bytes ≤ window_size |
||||
- Блокировка data packets при reset handshake |
||||
- Формирование пакета: заголовок + ACK + retransmit requests + данные |
||||
- Сохранение в sent_list для возможной ретрансмиссии |
||||
- Обновление unacked_bytes |
||||
|
||||
2. Подтверждение и RTT: |
||||
- При получении пакета: ID+timestamp добавляются в pending_ack_ids |
||||
- При отправке: pending_ack_ids включаются в пакет |
||||
- Получатель вычисляет RTT = current_time - received_timestamp |
||||
- Усреднение: rtt_avg_10 (скользящее), rtt_avg_100 (история) |
||||
|
||||
3. Ретрансмиссия (retransmit_check): |
||||
- Проверка sent_list каждые retrans_timer_period = max(RTT/2, 2ms) |
||||
- Порог ретрансмиссии: 1.5×RTT |
||||
- Новейший неподтвержденный пакет (last_sent_id) ретрансмитится через 2×RTT |
||||
- Пропускается при reset_pending && !reset_ack_received |
||||
|
||||
4. Сборка на приеме: |
||||
- Вставка в отсортированный rx_list (по ID) |
||||
- Обнаружение пропусков → запросы ретрансмиссии |
||||
- Перемещение непрерывной последовательности в output_queue |
||||
- Обновление last_delivered_id |
||||
|
||||
5. Forward progress: |
||||
- Отслеживание oldest_missing_id и missing_since_time |
||||
- Если пакет отсутствует >3×RTT (минимум 6 мс), last_delivered_id продвигается |
||||
- Предотвращает бесконечное ожидание потерянных пакетов |
||||
|
||||
Reset механизм: |
||||
--------------- |
||||
|
||||
Цель: синхронизация состояния ETCP и pkt_normalizer при сбоях. |
||||
|
||||
1. Инициализация соединения: |
||||
- При создании соединения: initialized=0 |
||||
- Первый обмен reset (0x02) → reset_ack (0x03) → initialized=1 |
||||
- Последующие resets вызывают локальный сброс состояния |
||||
|
||||
2. Локальный reset (etcp_reset_connection): |
||||
- Установка reset_pending=1, reset_ack_received=0 |
||||
- Отправка reset пакета (0x02) |
||||
- Таймер повторной отправки каждые 100 мс (до 10 попыток) |
||||
- Блокировка data packets, разрешение service packets |
||||
|
||||
3. Удаленный reset (получение 0x02): |
||||
- Если initialized=0 (первый handshake): только reset_ack (0x03) |
||||
- Если initialized=1: reset_ack + вызов etcp_reset() |
||||
- etcp_reset() вызывает reset_callback (очистка pkt_normalizer) |
||||
|
||||
4. Callback интеграция с connection: |
||||
- etcp_set_reset_callback(epkt, conn_etcp_reset_callback, conn) |
||||
- Callback очищает состояние packer/unpacker нормализатора |
||||
- Данные в очередях приложения сохраняются для повторной отправки |
||||
|
||||
5. Завершение handshake: |
||||
- Получение reset_ack (0x03) → reset_ack_received=1, reset_pending=0 |
||||
- initialized=1 (если еще не установлен) |
||||
- Отмена reset_timer |
||||
- Возобновление передачи data packets |
||||
|
||||
Взаимодействие с pkt_normalizer: |
||||
--------------------------------- |
||||
|
||||
1. Фрагментация исходящих данных: |
||||
- pkt_normalizer(packer) разбивает данные на фрагменты ≤ 65535 байт |
||||
- Добавляет заголовки фрагментации (0xFC/0xFD/0xFE/0xFF) |
||||
- Передает в tx_queue через queue bridge |
||||
|
||||
2. Сборка входящих данных: |
||||
- output_queue → pkt_normalizer(unpacker) собирает фрагменты |
||||
- Удаляет заголовки фрагментации |
||||
- Передает в app_output_queue |
||||
|
||||
3. Reset очистка: |
||||
- pkt_normalizer_reset_state() сбрасывает буферы сборки |
||||
- Сохраняет сервисное состояние (service packets) |
||||
|
||||
Статистика и отладка: |
||||
--------------------- |
||||
|
||||
Счетчики в структуре epkt позволяют отслеживать: |
||||
- Эффективность ретрансмиссий (retransmissions_count / unique_packets_sent) |
||||
- Накладные расходы (control_packets_count / total_packets_sent) |
||||
- Прогресс доставки (last_delivered_id vs last_rx_id) |
||||
|
||||
Отладочные макросы (при -DETCP_DEBUG -DETCP_DEBUG_EXT): |
||||
- ETCP_LOG() - основные события |
||||
- ETCP_DEBUG_LOG() - детальная отладка |
||||
|
||||
API функции: |
||||
------------ |
||||
|
||||
// Основные |
||||
epkt_t* etcp_init(uasync_t* ua); |
||||
void etcp_free(epkt_t* epkt); |
||||
void etcp_set_callback(epkt_t* epkt, etcp_tx_callback_t cb, void* arg); |
||||
void etcp_set_reset_callback(epkt_t* epkt, etcp_reset_callback_t cb, void* arg); |
||||
int etcp_rx_input(epkt_t* epkt, uint8_t* pkt, uint16_t len); |
||||
int etcp_tx_put(epkt_t* epkt, uint8_t* data, uint16_t len); |
||||
ll_queue_t* etcp_get_output_queue(epkt_t* epkt); |
||||
|
||||
// Конфигурация |
||||
void etcp_set_bandwidth(epkt_t* epkt, uint16_t bandwidth); |
||||
void etcp_reset(epkt_t* epkt); // Локальный сброс состояния |
||||
void etcp_reset_connection(epkt_t* epkt); // Инициация reset handshake |
||||
|
||||
// Мониторинг |
||||
uint16_t etcp_get_rtt(epkt_t* epkt); |
||||
uint16_t etcp_get_jitter(epkt_t* epkt); |
||||
int etcp_tx_queue_size(epkt_t* epkt); |
||||
void etcp_get_stats(epkt_t* epkt, ...); // Полная статистика |
||||
|
||||
Особенности реализации: |
||||
----------------------- |
||||
|
||||
1. Циклические счетчики: |
||||
- Функция id_compare(a,b) учитывает переполнение 65535→0 |
||||
- Timestamp использует ту же логику для расчета RTT |
||||
|
||||
2. Единицы времени: |
||||
- Базовый интервал: 0.1 мс (100 микросекунд) |
||||
- Все таймеры, RTT, jitter в этих единицах |
||||
- get_current_timestamp() возвращает монотонное время |
||||
|
||||
3. Ограничения буферов: |
||||
- pending_ack_ids, pending_retransmit_ids: до 32 элементов |
||||
- rtt_history: 100 измерений для усреднения |
||||
- Максимальный размер данных в пакете: 65535 байт |
||||
|
||||
4. Интеграция с uasync: |
||||
- Каждый epkt имеет ссылку на uasync_t* ua |
||||
- Таймеры создаются через uasync_set_timeout() |
||||
- Асинхронная обработка без блокировок |
||||
|
||||
Пример потока данных: |
||||
--------------------- |
||||
|
||||
1. App отправляет данные → conn_send() → app_input_queue |
||||
2. Queue callback → pkt_normalizer(packer) → фрагментация |
||||
3. Packer output → tx_queue → tx_process() |
||||
4. tx_process() формирует пакет → tx_callback → UDP send |
||||
5. Пакет в сети → UDP recv → etcp_rx_input() |
||||
6. Обработка ACK/данных → output_queue → pkt_normalizer(unpacker) |
||||
7. Сборка фрагментов → app_output_queue → app callback |
||||
|
||||
Reset сценарий: |
||||
--------------- |
||||
|
||||
1. Обнаружена проблема → etcp_reset_connection() |
||||
2. reset_pending=1, блокировка data packets |
||||
3. Отправка 0x02, таймер повторной отправки |
||||
4. Получатель: получен 0x02 → отправка 0x03 |
||||
5. Если initialized=1 → etcp_reset() → reset_callback → очистка pkt_normalizer |
||||
6. Отправитель: получен 0x03 → reset_ack_received=1, reset_pending=0 |
||||
7. Возобновление передачи, повторная отправка данных из app_input_queue |
||||
@ -1,29 +0,0 @@
|
||||
# Example configuration with control socket enabled |
||||
[global] |
||||
my_private_key=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef |
||||
my_public_key=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef |
||||
control_ip=127.0.0.1 # Listen on localhost only |
||||
control_port=5555 # UDP port for monitoring |
||||
|
||||
# TUN interface configuration |
||||
[tun] |
||||
ifname=tun12 |
||||
ip_addr=10.0.0.1/24 |
||||
mtu=1400 |
||||
|
||||
# Routing configuration |
||||
[routing] |
||||
allowed_subnet=10.0.0.0/8 |
||||
allowed_subnet=172.16.0.0/12 |
||||
allowed_subnet=192.168.0.0/16 |
||||
|
||||
# Connection configuration (example) |
||||
[[connection]] |
||||
name=node1 |
||||
remote_ip=192.168.1.100 |
||||
remote_port=12345 |
||||
local_ip=0.0.0.0 |
||||
local_port=0 |
||||
mode=client |
||||
|
||||
# Add more connections as needed |
||||
@ -1,496 +0,0 @@
|
||||
// config_parser.c - Configuration parser for utun application |
||||
#define _POSIX_C_SOURCE 200809L |
||||
#include "config_parser.h" |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <strings.h> |
||||
#include <ctype.h> |
||||
#include <errno.h> |
||||
|
||||
#define MAX_LINE_LEN 1024 |
||||
#define INITIAL_CONNECTION_CAPACITY 4 |
||||
#define INITIAL_SERVER_CAPACITY 4 |
||||
#define INITIAL_CLIENT_CAPACITY 4 |
||||
#define INITIAL_CONNECTION_V2_CAPACITY 4 |
||||
|
||||
// Helper function to trim whitespace |
||||
static char* trim(char *str) { |
||||
if (!str) return NULL; |
||||
|
||||
// Trim leading spaces |
||||
while (isspace((unsigned char)*str)) str++; |
||||
|
||||
// Trim trailing spaces |
||||
char *end = str + strlen(str) - 1; |
||||
while (end > str && isspace((unsigned char)*end)) end--; |
||||
*(end + 1) = '\0'; |
||||
|
||||
return str; |
||||
} |
||||
|
||||
// Parse a key-value pair |
||||
static int parse_key_value(const char *line, char *key, size_t key_len, |
||||
char *value, size_t value_len) { |
||||
char *equal = strchr(line, '='); |
||||
if (!equal) return -1; |
||||
|
||||
// Extract key |
||||
size_t key_size = equal - line; |
||||
if (key_size >= key_len) return -1; |
||||
strncpy(key, line, key_size); |
||||
key[key_size] = '\0'; |
||||
trim(key); |
||||
|
||||
// Extract value |
||||
const char *val_start = equal + 1; |
||||
size_t val_len = strlen(val_start); |
||||
if (val_len >= value_len) return -1; |
||||
strcpy(value, val_start); |
||||
trim(value); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// Parse connection mode string |
||||
static config_conn_mode_t parse_mode(const char *mode_str) { |
||||
if (!mode_str) return CONFIG_MODE_UNKNOWN; |
||||
|
||||
if (strcasecmp(mode_str, "client") == 0) { |
||||
return CONFIG_MODE_CLIENT; |
||||
} else if (strcasecmp(mode_str, "server") == 0) { |
||||
return CONFIG_MODE_SERVER; |
||||
} |
||||
|
||||
return CONFIG_MODE_UNKNOWN; |
||||
} |
||||
|
||||
// Add a connection to the config |
||||
static int add_connection(utun_config_t *config, const connection_config_t *conn) { |
||||
if (config->connection_count >= config->connection_capacity) { |
||||
// Resize array |
||||
int new_capacity = config->connection_capacity * 2; |
||||
connection_config_t *new_connections = realloc(config->connections, |
||||
new_capacity * sizeof(connection_config_t)); |
||||
if (!new_connections) return -1; |
||||
|
||||
config->connections = new_connections; |
||||
config->connection_capacity = new_capacity; |
||||
} |
||||
|
||||
// Copy connection data |
||||
memcpy(&config->connections[config->connection_count], conn, sizeof(connection_config_t)); |
||||
config->connection_count++; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// Parse configuration file |
||||
utun_config_t* parse_config(const char *filename) { |
||||
FILE *fp = fopen(filename, "r"); |
||||
if (!fp) { |
||||
fprintf(stderr, "Failed to open config file: %s\n", filename); |
||||
return NULL; |
||||
} |
||||
|
||||
// Allocate config structure |
||||
utun_config_t *config = calloc(1, sizeof(utun_config_t)); |
||||
if (!config) { |
||||
fclose(fp); |
||||
return NULL; |
||||
} |
||||
|
||||
// Initialize connection array |
||||
config->connection_capacity = INITIAL_CONNECTION_CAPACITY; |
||||
config->connections = malloc(config->connection_capacity * sizeof(connection_config_t)); |
||||
if (!config->connections) { |
||||
free(config); |
||||
fclose(fp); |
||||
return NULL; |
||||
} |
||||
|
||||
char line[MAX_LINE_LEN]; |
||||
char current_section[128] = ""; |
||||
connection_config_t current_conn = {0}; |
||||
int in_connection_section = 0; |
||||
|
||||
while (fgets(line, sizeof(line), fp)) { |
||||
// Remove newline |
||||
line[strcspn(line, "\n")] = '\0'; |
||||
|
||||
char *trimmed = trim(line); |
||||
|
||||
// Skip empty lines and comments |
||||
if (strlen(trimmed) == 0 || trimmed[0] == ';' || trimmed[0] == '#') { |
||||
continue; |
||||
} |
||||
|
||||
// Check for section header |
||||
if (trimmed[0] == '[' && trimmed[strlen(trimmed) - 1] == ']') { |
||||
// End previous connection section if any |
||||
if (in_connection_section) { |
||||
if (strlen(current_conn.name) > 0) { |
||||
if (add_connection(config, ¤t_conn) != 0) { |
||||
fprintf(stderr, "Failed to add connection: %s\n", current_conn.name); |
||||
} |
||||
} |
||||
memset(¤t_conn, 0, sizeof(current_conn)); |
||||
in_connection_section = 0; |
||||
} |
||||
|
||||
// Extract section name |
||||
strncpy(current_section, trimmed + 1, sizeof(current_section) - 1); |
||||
current_section[sizeof(current_section) - 1] = '\0'; |
||||
current_section[strcspn(current_section, "]")] = '\0'; |
||||
trim(current_section); |
||||
|
||||
// Check if it's a connection section |
||||
if (strncmp(current_section, "connection:", 11) == 0) { |
||||
in_connection_section = 1; |
||||
// Extract connection name |
||||
char *name = trim(current_section + 11); |
||||
if (strlen(name) > 0) { |
||||
strncpy(current_conn.name, name, sizeof(current_conn.name) - 1); |
||||
} |
||||
} |
||||
|
||||
continue; |
||||
} |
||||
|
||||
// Parse key-value pair |
||||
char key[256], value[256]; |
||||
if (parse_key_value(trimmed, key, sizeof(key), value, sizeof(value)) != 0) { |
||||
fprintf(stderr, "Invalid key-value line: %s\n", trimmed); |
||||
continue; |
||||
} |
||||
|
||||
// Process based on current section |
||||
if (strcasecmp(current_section, "global") == 0) { |
||||
if (strcasecmp(key, "my_private_key") == 0) { |
||||
strncpy(config->global.my_private_key_hex, value, sizeof(config->global.my_private_key_hex) - 1); |
||||
} else if (strcasecmp(key, "my_public_key") == 0) { |
||||
strncpy(config->global.my_public_key_hex, value, sizeof(config->global.my_public_key_hex) - 1); |
||||
} else if (strcasecmp(key, "option") == 0) { |
||||
strncpy(config->global.option_value, value, sizeof(config->global.option_value) - 1); |
||||
} else if (strcasecmp(key, "control_ip") == 0) { |
||||
strncpy(config->global.control_ip, value, sizeof(config->global.control_ip) - 1); |
||||
} else if (strcasecmp(key, "control_port") == 0) { |
||||
config->global.control_port = atoi(value); |
||||
} else if (strcasecmp(key, "net_debug") == 0) { |
||||
config->global.net_debug = atoi(value); |
||||
} |
||||
} else if (strcasecmp(current_section, "routing") == 0) { |
||||
if (strcasecmp(key, "allowed_subnet") == 0) { |
||||
if (config->allowed_subnet_count < MAX_ALLOWED_SUBNETS) { |
||||
strncpy(config->allowed_subnets[config->allowed_subnet_count].subnet, value, sizeof(config->allowed_subnets[0].subnet) - 1); |
||||
config->allowed_subnet_count++; |
||||
} else { |
||||
fprintf(stderr, "Too many allowed subnets, maximum is %d\n", MAX_ALLOWED_SUBNETS); |
||||
} |
||||
} |
||||
} else if (in_connection_section) { |
||||
if (strcasecmp(key, "mode") == 0) { |
||||
current_conn.mode = parse_mode(value); |
||||
} else if (strcasecmp(key, "addr") == 0) { |
||||
// For server mode |
||||
if (current_conn.mode == CONFIG_MODE_SERVER) { |
||||
strncpy(current_conn.local_addr, value, sizeof(current_conn.local_addr) - 1); |
||||
} |
||||
} else if (strcasecmp(key, "from_addr") == 0) { |
||||
// For client mode |
||||
if (current_conn.mode == CONFIG_MODE_CLIENT) { |
||||
strncpy(current_conn.local_addr, value, sizeof(current_conn.local_addr) - 1); |
||||
} |
||||
} else if (strcasecmp(key, "to_addr") == 0) { |
||||
// For client mode |
||||
if (current_conn.mode == CONFIG_MODE_CLIENT) { |
||||
strncpy(current_conn.remote_addr, value, sizeof(current_conn.remote_addr) - 1); |
||||
} |
||||
} else if (strcasecmp(key, "peer_public_key") == 0) { |
||||
strncpy(current_conn.peer_public_key_hex, value, sizeof(current_conn.peer_public_key_hex) - 1); |
||||
} else if (strcasecmp(key, "so_mark") == 0) { |
||||
current_conn.so_mark = atoi(value); |
||||
} else if (strcasecmp(key, "netif") == 0) { |
||||
strncpy(current_conn.netif, value, sizeof(current_conn.netif) - 1); |
||||
} else if (strcasecmp(key, "tun") == 0) { |
||||
strncpy(current_conn.tun_ifname, value, sizeof(current_conn.tun_ifname) - 1); |
||||
} else if (strcasecmp(key, "tun_ip") == 0) { |
||||
strncpy(current_conn.tun_ip, value, sizeof(current_conn.tun_ip) - 1); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Add last connection if any |
||||
if (in_connection_section && strlen(current_conn.name) > 0) { |
||||
if (add_connection(config, ¤t_conn) != 0) { |
||||
fprintf(stderr, "Failed to add connection: %s\n", current_conn.name); |
||||
} |
||||
} |
||||
|
||||
fclose(fp); |
||||
return config; |
||||
} |
||||
|
||||
// Free configuration structure |
||||
void free_config(utun_config_t *config) { |
||||
if (!config) return; |
||||
|
||||
if (config->connections) { |
||||
free(config->connections); |
||||
} |
||||
free(config); |
||||
} |
||||
|
||||
// Print configuration for debugging |
||||
void print_config(const utun_config_t *config) { |
||||
if (!config) { |
||||
printf("Configuration is NULL\n"); |
||||
return; |
||||
} |
||||
|
||||
printf("Global configuration:\n"); |
||||
printf(" my_private_key: %s\n", config->global.my_private_key_hex); |
||||
printf(" my_public_key: %s\n", config->global.my_public_key_hex); |
||||
printf(" option: %s\n", config->global.option_value); |
||||
printf(" control_ip: %s\n", config->global.control_ip); |
||||
printf(" control_port: %u\n", config->global.control_port); |
||||
printf(" net_debug: %d\n", config->global.net_debug); |
||||
printf("\n"); |
||||
printf("Allowed subnets (%d):\n", config->allowed_subnet_count); |
||||
for (int i = 0; i < config->allowed_subnet_count; i++) { |
||||
printf(" %s\n", config->allowed_subnets[i].subnet); |
||||
} |
||||
printf("\n"); |
||||
|
||||
printf("Connections (%d):\n", config->connection_count); |
||||
for (int i = 0; i < config->connection_count; i++) { |
||||
const connection_config_t *conn = &config->connections[i]; |
||||
printf(" [%d] %s:\n", i, conn->name); |
||||
printf(" mode: %s\n", |
||||
conn->mode == CONFIG_MODE_CLIENT ? "client" : |
||||
conn->mode == CONFIG_MODE_SERVER ? "server" : "unknown"); |
||||
printf(" local_addr: %s\n", conn->local_addr); |
||||
printf(" remote_addr: %s\n", conn->remote_addr); |
||||
printf(" peer_public_key: %s\n", conn->peer_public_key_hex); |
||||
printf(" so_mark: %d\n", conn->so_mark); |
||||
printf(" netif: %s\n", conn->netif); |
||||
printf(" tun: %s\n", conn->tun_ifname); |
||||
printf(" tun_ip: %s\n", conn->tun_ip); |
||||
} |
||||
} |
||||
|
||||
// Update keys in configuration file |
||||
int update_config_keys(const char *filename, |
||||
const char *private_key_hex, |
||||
const char *public_key_hex) { |
||||
if (!filename || !private_key_hex || !public_key_hex) { |
||||
errno = EINVAL; |
||||
return -1; |
||||
} |
||||
|
||||
// Read entire file |
||||
FILE *fp = fopen(filename, "r"); |
||||
if (!fp) { |
||||
// Try to create new file |
||||
fp = fopen(filename, "w"); |
||||
if (!fp) return -1; |
||||
|
||||
fprintf(fp, "[global]\n"); |
||||
fprintf(fp, "my_private_key=%s\n", private_key_hex); |
||||
fprintf(fp, "my_public_key=%s\n", public_key_hex); |
||||
fclose(fp); |
||||
return 0; |
||||
} |
||||
|
||||
// Add a server to the config |
||||
static int add_server(utun_config_t *config, const server_config_t *server) { |
||||
if (config->server_count >= config->server_capacity) { |
||||
// Resize array |
||||
int new_capacity = config->server_capacity * 2; |
||||
server_config_t *new_servers = realloc(config->servers, |
||||
new_capacity * sizeof(server_config_t)); |
||||
if (!new_servers) return -1; |
||||
|
||||
config->servers = new_servers; |
||||
config->server_capacity = new_capacity; |
||||
} |
||||
|
||||
// Copy server data |
||||
memcpy(&config->servers[config->server_count], server, sizeof(server_config_t)); |
||||
config->server_count++; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// Add a client to the config |
||||
static int add_client(utun_config_t *config, const client_config_t *client) { |
||||
if (config->client_count >= config->client_capacity) { |
||||
// Resize array |
||||
int new_capacity = config->client_capacity * 2; |
||||
client_config_t *new_clients = realloc(config->clients, |
||||
new_capacity * sizeof(client_config_t)); |
||||
if (!new_clients) return -1; |
||||
|
||||
config->clients = new_clients; |
||||
config->client_capacity = new_capacity; |
||||
} |
||||
|
||||
// Copy client data |
||||
memcpy(&config->clients[config->client_count], client, sizeof(client_config_t)); |
||||
config->client_count++; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// Add a route to connection v2 |
||||
static int add_route_to_connection(connection_config_v2_t *conn_v2, const route_pair_t *route) { |
||||
if (conn_v2->route_count >= conn_v2->route_capacity) { |
||||
// Resize array |
||||
int new_capacity = conn_v2->route_capacity * 2; |
||||
route_pair_t *new_routes = realloc(conn_v2->routes, |
||||
new_capacity * sizeof(route_pair_t)); |
||||
if (!new_routes) return -1; |
||||
|
||||
conn_v2->routes = new_routes; |
||||
conn_v2->route_capacity = new_capacity; |
||||
} |
||||
|
||||
// Copy route data |
||||
memcpy(&conn_v2->routes[conn_v2->route_count], route, sizeof(route_pair_t)); |
||||
conn_v2->route_count++; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// Add a connection v2 to the config |
||||
static int add_connection_v2(utun_config_t *config, const connection_config_v2_t *conn_v2) { |
||||
if (config->connection_v2_count >= config->connection_v2_capacity) { |
||||
// Resize array |
||||
int new_capacity = config->connection_v2_capacity * 2; |
||||
connection_config_v2_t *new_connections = realloc(config->connections_v2, |
||||
new_capacity * sizeof(connection_config_v2_t)); |
||||
if (!new_connections) return -1; |
||||
|
||||
config->connections_v2 = new_connections; |
||||
config->connection_v2_capacity = new_capacity; |
||||
} |
||||
|
||||
// Copy connection data |
||||
memcpy(&config->connections_v2[config->connection_v2_count], conn_v2, sizeof(connection_config_v2_t)); |
||||
config->connection_v2_count++; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// Read all lines |
||||
char **lines = NULL; |
||||
size_t line_count = 0; |
||||
size_t capacity = 0; |
||||
char line[MAX_LINE_LEN]; |
||||
int in_global_section = 0; |
||||
int private_key_found = 0; |
||||
int public_key_found = 0; |
||||
size_t private_key_line = 0; |
||||
size_t public_key_line = 0; |
||||
|
||||
while (fgets(line, sizeof(line), fp)) { |
||||
// Add line to array |
||||
if (line_count >= capacity) { |
||||
size_t new_capacity = capacity ? capacity * 2 : 16; |
||||
char **new_lines = realloc(lines, new_capacity * sizeof(char *)); |
||||
if (!new_lines) { |
||||
// Cleanup |
||||
for (size_t i = 0; i < line_count; i++) free(lines[i]); |
||||
free(lines); |
||||
fclose(fp); |
||||
return -1; |
||||
} |
||||
lines = new_lines; |
||||
capacity = new_capacity; |
||||
} |
||||
|
||||
lines[line_count] = strdup(line); |
||||
if (!lines[line_count]) { |
||||
// Cleanup |
||||
for (size_t i = 0; i < line_count; i++) free(lines[i]); |
||||
free(lines); |
||||
fclose(fp); |
||||
return -1; |
||||
} |
||||
|
||||
char *trimmed = trim(line); |
||||
|
||||
// Track global section |
||||
if (trimmed[0] == '[' && trimmed[strlen(trimmed) - 1] == ']') { |
||||
char section[128]; |
||||
strncpy(section, trimmed + 1, sizeof(section) - 1); |
||||
section[sizeof(section) - 1] = '\0'; |
||||
section[strcspn(section, "]")] = '\0'; |
||||
trim(section); |
||||
|
||||
in_global_section = (strcasecmp(section, "global") == 0); |
||||
} |
||||
|
||||
// Check for keys in global section |
||||
if (in_global_section) { |
||||
char key[256], value[256]; |
||||
if (parse_key_value(trimmed, key, sizeof(key), value, sizeof(value)) == 0) { |
||||
if (strcasecmp(key, "my_private_key") == 0) { |
||||
private_key_found = 1; |
||||
private_key_line = line_count; |
||||
} else if (strcasecmp(key, "my_public_key") == 0) { |
||||
public_key_found = 1; |
||||
public_key_line = line_count; |
||||
} |
||||
} |
||||
} |
||||
|
||||
line_count++; |
||||
} |
||||
fclose(fp); |
||||
|
||||
// Update or add keys |
||||
if (private_key_found) { |
||||
free(lines[private_key_line]); |
||||
char new_line[MAX_LINE_LEN]; |
||||
snprintf(new_line, sizeof(new_line), "my_private_key=%s\n", private_key_hex); |
||||
lines[private_key_line] = strdup(new_line); |
||||
} else { |
||||
// Add after [global] line or at beginning |
||||
// Simplified: add at end |
||||
// We'll need to insert after [global] but for simplicity, append |
||||
} |
||||
|
||||
if (public_key_found) { |
||||
free(lines[public_key_line]); |
||||
char new_line[MAX_LINE_LEN]; |
||||
snprintf(new_line, sizeof(new_line), "my_public_key=%s\n", public_key_hex); |
||||
lines[public_key_line] = strdup(new_line); |
||||
} |
||||
|
||||
// Write back to file |
||||
fp = fopen(filename, "w"); |
||||
if (!fp) { |
||||
// Cleanup |
||||
for (size_t i = 0; i < line_count; i++) free(lines[i]); |
||||
free(lines); |
||||
return -1; |
||||
} |
||||
|
||||
for (size_t i = 0; i < line_count; i++) { |
||||
fputs(lines[i], fp); |
||||
free(lines[i]); |
||||
} |
||||
|
||||
// Add missing keys |
||||
if (!private_key_found) { |
||||
fprintf(fp, "my_private_key=%s\n", private_key_hex); |
||||
} |
||||
if (!public_key_found) { |
||||
fprintf(fp, "my_public_key=%s\n", public_key_hex); |
||||
} |
||||
|
||||
free(lines); |
||||
fclose(fp); |
||||
return 0; |
||||
} |
||||
@ -1,170 +0,0 @@
|
||||
// connection.h - Минималистичный API для защищенных UDP подключений
|
||||
#ifndef CONNECTION_H |
||||
#define CONNECTION_H |
||||
|
||||
#include <stdint.h> |
||||
#include <stddef.h> |
||||
#include "config_parser.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/* Непрозрачный дескриптор подключения */ |
||||
typedef struct conn_handle conn_handle_t; |
||||
|
||||
/* Forward declaration для uasync */ |
||||
typedef struct uasync_s uasync_t; |
||||
|
||||
/* Forward declaration для очередей */ |
||||
typedef struct ll_queue ll_queue_t; |
||||
|
||||
/* Режим подключения */ |
||||
typedef enum { |
||||
CONN_MODE_CLIENT, /* Инициируем подключение к указанному удаленному адресу */ |
||||
CONN_MODE_SERVER /* Ожидаем входящие подключения */ |
||||
} conn_mode_t; |
||||
|
||||
/* Callback для входящих данных */ |
||||
typedef void (*conn_recv_callback_t)(conn_handle_t* conn,
|
||||
const uint8_t* data,
|
||||
size_t len,
|
||||
void* user_data); |
||||
|
||||
/*
|
||||
* Создание дескриптора подключения (только выделение памяти). |
||||
* ua - экземпляр uasync для таймеров (обязательный параметр) |
||||
* Возвращает NULL при ошибке. |
||||
*/ |
||||
conn_handle_t* conn_create(uasync_t* ua); |
||||
|
||||
/*
|
||||
* Установка криптографических ключей. |
||||
* Должна быть вызвана до conn_connect(). |
||||
*
|
||||
* @param conn Дескриптор подключения |
||||
* @param my_pub_key Публичный ключ (64 байта для secp256r1), NULL для авто-генерации |
||||
* @param my_priv_key Приватный ключ (32 байта), NULL для авто-генерации |
||||
* @param peer_pub_key Публичный ключ пира (64 байта), NULL для сервера (получит из первого пакета) |
||||
*
|
||||
* @return 0 при успехе, -1 при ошибке |
||||
*/ |
||||
int conn_set_keys(conn_handle_t* conn, |
||||
const uint8_t* my_pub_key, |
||||
const uint8_t* my_priv_key, |
||||
const uint8_t* peer_pub_key); |
||||
|
||||
/*
|
||||
* Подключение к удаленному узлу или начало ожидания входящих подключений. |
||||
*
|
||||
* @param conn Дескриптор подключения |
||||
* @param local_ip Локальный IP для bind (NULL для "0.0.0.0") |
||||
* @param local_port Локальный порт (0 для авто-выбора) |
||||
* @param remote_ip Удаленный IP (NULL для серверного режима) |
||||
* @param remote_port Удаленный порт (игнорируется если remote_ip NULL) |
||||
* @param mode Режим подключения (CONN_MODE_CLIENT/SERVER) |
||||
*
|
||||
* @return 0 при успехе, -1 при ошибке |
||||
*/ |
||||
int conn_connect(conn_handle_t* conn, |
||||
const char* local_ip, |
||||
uint16_t local_port, |
||||
const char* remote_ip, |
||||
uint16_t remote_port, |
||||
conn_mode_t mode); |
||||
|
||||
/*
|
||||
* Установка callback'а для входящих данных. |
||||
* Callback будет вызываться при получении полных собранных пакетов. |
||||
*/ |
||||
void conn_set_recv_callback(conn_handle_t* conn, |
||||
conn_recv_callback_t callback, |
||||
void* user_data); |
||||
|
||||
/*
|
||||
* Отправка данных. |
||||
* Данные будут автоматически фрагментированы, зашифрованы и отправлены. |
||||
*
|
||||
* @return 0 при успехе, -1 при ошибке |
||||
*/ |
||||
int conn_send(conn_handle_t* conn, const uint8_t* data, size_t len); |
||||
|
||||
/*
|
||||
* Закрытие подключения (немедленное, без протокола завершения). |
||||
* После вызова дескриптор можно уничтожить через conn_destroy(). |
||||
*/ |
||||
void conn_close(conn_handle_t* conn); |
||||
|
||||
/*
|
||||
* Сброс соединения с использованием служебных пакетов. |
||||
* Отправляет пакет сброса через ETCP и сбрасывает состояние всех компонентов. |
||||
* Предназначено для тестирования и восстановления после сбоев. |
||||
*/ |
||||
void conn_reset(conn_handle_t* conn); |
||||
|
||||
/*
|
||||
* Полное уничтожение дескриптора подключения и освобождение всех ресурсов. |
||||
* Автоматически вызывает conn_close() если подключение активно. |
||||
*/ |
||||
void conn_destroy(conn_handle_t* conn); |
||||
|
||||
/*
|
||||
* Получение статистики подключения (опционально). |
||||
*
|
||||
* @return 0 при успехе, -1 при ошибке |
||||
*/ |
||||
typedef struct { |
||||
uint64_t bytes_sent; |
||||
uint64_t bytes_received; |
||||
uint32_t packets_sent; |
||||
uint32_t packets_received; |
||||
uint32_t retransmissions; |
||||
uint32_t fragments_assembled; |
||||
uint16_t current_rtt_ms; /* Текущее RTT в миллисекундах */ |
||||
uint16_t jitter_ms; /* Джиттер в миллисекундах */ |
||||
uint32_t encryption_errors; /* Ошибки шифрования */ |
||||
uint32_t decryption_errors; /* Ошибки расшифрования */ |
||||
uint32_t crc_errors; /* Ошибки CRC32 */ |
||||
} conn_stats_t; |
||||
|
||||
int conn_get_stats(conn_handle_t* conn, conn_stats_t* stats); |
||||
|
||||
/**
|
||||
* @brief Получить очередь отправки (данные от приложения для передачи) |
||||
* @param conn Дескриптор подключения |
||||
* @return Указатель на очередь отправки или NULL при ошибке |
||||
*
|
||||
* Очередь отправки (app_input_queue) содержит данные от приложения, |
||||
* которые ожидают обработки packer'ом и отправки через ETCP. |
||||
* Используется для контроля потока и pacing. |
||||
*/ |
||||
ll_queue_t* conn_get_output_queue(conn_handle_t* conn); |
||||
|
||||
/**
|
||||
* @brief Получить очередь приема (собранные данные для приложения) |
||||
* @param conn Дескриптор подключения |
||||
* @return Указатель на очередь приема или NULL при ошибке |
||||
*
|
||||
* Очередь приема (app_output_queue) содержит собранные пакеты, |
||||
* готовые для передачи приложению через recv_callback. |
||||
*/ |
||||
ll_queue_t* conn_get_input_queue(conn_handle_t* conn); |
||||
|
||||
/**
|
||||
* @brief Initialize connection from v2 configuration |
||||
* @param conn Connection handle (must be created with conn_create) |
||||
* @param config Parsed configuration (must contain at least one connection_v2) |
||||
* @param conn_idx Index of connection_v2 in config (usually 0) |
||||
* @return 0 on success, -1 on error |
||||
*
|
||||
* Sets up multiple routes based on server:client pairs, creates sockets with |
||||
* options (SO_MARK, SO_BINDTODEVICE), sets cryptographic keys, and prepares |
||||
* connection for data transmission using first route as active. |
||||
*/ |
||||
int conn_init_v2(conn_handle_t* conn, const utun_config_t* config, int conn_idx); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* CONNECTION_H */ |
||||
@ -1,457 +0,0 @@
|
||||
/**
|
||||
* @file control_socket.c |
||||
* @brief Control socket implementation for statistics and diagnostics |
||||
*/ |
||||
|
||||
#include "control_socket.h" |
||||
#include "connection.h" |
||||
#include "etcp.h" |
||||
#include "pkt_normalizer.h" |
||||
#include "routing.h" |
||||
#include "tun_if.h" |
||||
#include "config_parser.h" |
||||
#include "utun_state.h" |
||||
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
#include <errno.h> |
||||
#include <time.h> |
||||
#include <sys/socket.h> |
||||
#include <netinet/in.h> |
||||
#include <arpa/inet.h> |
||||
#include <fcntl.h> |
||||
#include <sys/time.h> |
||||
|
||||
// Internal control socket structure
|
||||
struct control_socket { |
||||
int sockfd; |
||||
struct sockaddr_in addr; |
||||
uint16_t port; |
||||
void *app_state; // Pointer to utun_state_t
|
||||
uint16_t sequence_counter; |
||||
|
||||
// Statistics counters (for cumulative stats)
|
||||
uint64_t total_requests; |
||||
uint64_t total_errors; |
||||
|
||||
// Timestamps
|
||||
struct timeval start_time; |
||||
}; |
||||
|
||||
// Forward declarations
|
||||
static int collect_statistics(control_socket_t *cs, control_stats_packet_t *stats); |
||||
static int collect_system_status(control_socket_t *cs, system_status_t *status); |
||||
static void get_etcp_stats(epkt_t *etcp, etcp_stats_t *etcp_stats); |
||||
static void get_pkt_normalizer_stats(pkt_normalizer_pair *pair, pkt_normalizer_stats_t *pn_stats); |
||||
static int handle_stats_request(control_socket_t *cs, struct sockaddr_in *client, uint16_t sequence); |
||||
static int handle_status_request(control_socket_t *cs, struct sockaddr_in *client, uint16_t sequence); |
||||
static int handle_reset_request(control_socket_t *cs, struct sockaddr_in *client, uint16_t sequence); |
||||
static int send_error_response(control_socket_t *cs, struct sockaddr_in *client, uint16_t sequence, control_err_t error); |
||||
static int send_response(control_socket_t *cs, struct sockaddr_in *client, void *data, size_t len); |
||||
|
||||
// Create UDP control socket
|
||||
control_socket_t* control_socket_create(const char *ip, uint16_t port) { |
||||
control_socket_t *cs = calloc(1, sizeof(control_socket_t)); |
||||
if (!cs) { |
||||
return NULL; |
||||
} |
||||
|
||||
// Create UDP socket
|
||||
cs->sockfd = socket(AF_INET, SOCK_DGRAM, 0); |
||||
if (cs->sockfd < 0) { |
||||
perror("control_socket: socket"); |
||||
free(cs); |
||||
return NULL; |
||||
} |
||||
|
||||
// Set non-blocking
|
||||
int flags = fcntl(cs->sockfd, F_GETFL, 0); |
||||
if (flags < 0) { |
||||
perror("control_socket: fcntl GETFL"); |
||||
close(cs->sockfd); |
||||
free(cs); |
||||
return NULL; |
||||
} |
||||
if (fcntl(cs->sockfd, F_SETFL, flags | O_NONBLOCK) < 0) { |
||||
perror("control_socket: fcntl SETFL"); |
||||
close(cs->sockfd); |
||||
free(cs); |
||||
return NULL; |
||||
} |
||||
|
||||
// Enable address reuse
|
||||
int opt = 1; |
||||
if (setsockopt(cs->sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { |
||||
perror("control_socket: setsockopt SO_REUSEADDR"); |
||||
// Continue anyway
|
||||
} |
||||
|
||||
// Bind to address
|
||||
memset(&cs->addr, 0, sizeof(cs->addr)); |
||||
cs->addr.sin_family = AF_INET; |
||||
cs->addr.sin_port = htons(port); |
||||
|
||||
if (ip && ip[0] != '\0') { |
||||
if (inet_pton(AF_INET, ip, &cs->addr.sin_addr) != 1) { |
||||
fprintf(stderr, "control_socket: invalid IP address: %s\n", ip); |
||||
close(cs->sockfd); |
||||
free(cs); |
||||
return NULL; |
||||
} |
||||
} else { |
||||
cs->addr.sin_addr.s_addr = INADDR_ANY; |
||||
} |
||||
|
||||
if (bind(cs->sockfd, (struct sockaddr*)&cs->addr, sizeof(cs->addr)) < 0) { |
||||
perror("control_socket: bind"); |
||||
close(cs->sockfd); |
||||
free(cs); |
||||
return NULL; |
||||
} |
||||
|
||||
cs->port = port; |
||||
cs->sequence_counter = 0; |
||||
cs->total_requests = 0; |
||||
cs->total_errors = 0; |
||||
gettimeofday(&cs->start_time, NULL); |
||||
|
||||
printf("[CONTROL] Control socket listening on %s:%u\n",
|
||||
ip ? ip : "0.0.0.0", port); |
||||
|
||||
return cs; |
||||
} |
||||
|
||||
// Destroy control socket
|
||||
void control_socket_destroy(control_socket_t *cs) { |
||||
if (!cs) return; |
||||
|
||||
if (cs->sockfd >= 0) { |
||||
close(cs->sockfd); |
||||
} |
||||
|
||||
free(cs); |
||||
} |
||||
|
||||
// Get socket file descriptor for polling
|
||||
int control_socket_get_fd(const control_socket_t *cs) { |
||||
return cs ? cs->sockfd : -1; |
||||
} |
||||
|
||||
// Set application state pointer
|
||||
void control_socket_set_state(control_socket_t *cs, void *state) { |
||||
if (cs) { |
||||
cs->app_state = state; |
||||
} |
||||
} |
||||
|
||||
// Process incoming requests
|
||||
int control_socket_process(control_socket_t *cs, void *state) { |
||||
if (!cs || !state) return -1; |
||||
|
||||
// Use provided state or stored state
|
||||
if (state) { |
||||
cs->app_state = state; |
||||
} |
||||
|
||||
struct sockaddr_in client_addr; |
||||
socklen_t addr_len = sizeof(client_addr); |
||||
control_request_packet_t request; |
||||
|
||||
// Receive request
|
||||
ssize_t n = recvfrom(cs->sockfd, &request, sizeof(request), 0, |
||||
(struct sockaddr*)&client_addr, &addr_len); |
||||
|
||||
if (n < 0) { |
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) { |
||||
return 0; // No data available
|
||||
} |
||||
perror("control_socket: recvfrom"); |
||||
return -1; |
||||
} |
||||
|
||||
if (n != sizeof(request)) { |
||||
fprintf(stderr, "control_socket: invalid request size: %zd\n", n); |
||||
return 0; |
||||
} |
||||
|
||||
cs->total_requests++; |
||||
|
||||
// Handle request based on command
|
||||
switch (request.command) { |
||||
case CONTROL_CMD_GET_STATS: |
||||
return handle_stats_request(cs, &client_addr, request.sequence); |
||||
|
||||
case CONTROL_CMD_GET_STATUS: |
||||
return handle_status_request(cs, &client_addr, request.sequence); |
||||
|
||||
case CONTROL_CMD_RESET_STATS: |
||||
return handle_reset_request(cs, &client_addr, request.sequence); |
||||
|
||||
default: |
||||
return send_error_response(cs, &client_addr, request.sequence,
|
||||
CONTROL_ERR_INVALID_CMD); |
||||
} |
||||
} |
||||
|
||||
// Handle statistics request
|
||||
static int handle_stats_request(control_socket_t *cs, struct sockaddr_in *client,
|
||||
uint16_t sequence) { |
||||
control_stats_packet_t stats_packet = {0}; |
||||
|
||||
// Fill response header
|
||||
stats_packet.response_type = CONTROL_RESP_STATS; |
||||
stats_packet.error_code = CONTROL_ERR_NONE; |
||||
stats_packet.sequence = sequence; |
||||
|
||||
// Get current timestamp
|
||||
struct timeval tv; |
||||
gettimeofday(&tv, NULL); |
||||
stats_packet.timestamp = (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec; |
||||
|
||||
// Collect statistics from all modules
|
||||
if (collect_statistics(cs, &stats_packet) < 0) { |
||||
return send_error_response(cs, client, sequence, CONTROL_ERR_INTERNAL); |
||||
} |
||||
|
||||
// Send response
|
||||
return send_response(cs, client, &stats_packet, sizeof(stats_packet)); |
||||
} |
||||
|
||||
// Handle status request
|
||||
static int handle_status_request(control_socket_t *cs, struct sockaddr_in *client, |
||||
uint16_t sequence) { |
||||
control_status_packet_t status_packet = {0}; |
||||
|
||||
status_packet.response_type = CONTROL_RESP_STATUS; |
||||
status_packet.error_code = CONTROL_ERR_NONE; |
||||
status_packet.sequence = sequence; |
||||
|
||||
// Collect system status
|
||||
if (collect_system_status(cs, &status_packet.system) < 0) { |
||||
status_packet.system.uptime_seconds = 0; |
||||
} |
||||
|
||||
// Set running flag
|
||||
if (cs->app_state) { |
||||
// Assume utun_state_t has 'running' field at same offset
|
||||
// We'll fix this when integrating with utun.c
|
||||
status_packet.running = 1; |
||||
} |
||||
|
||||
snprintf(status_packet.status_msg, sizeof(status_packet.status_msg), |
||||
"utun control socket active on port %u", cs->port); |
||||
|
||||
return send_response(cs, client, &status_packet, sizeof(status_packet)); |
||||
} |
||||
|
||||
// Handle reset statistics request
|
||||
static int handle_reset_request(control_socket_t *cs, struct sockaddr_in *client, |
||||
uint16_t sequence) { |
||||
// TODO: Implement statistics reset
|
||||
// For now, just send success response
|
||||
|
||||
control_status_packet_t response = {0}; |
||||
response.response_type = CONTROL_RESP_STATUS; |
||||
response.error_code = CONTROL_ERR_NONE; |
||||
response.sequence = sequence; |
||||
|
||||
// Get basic system status
|
||||
collect_system_status(cs, &response.system); |
||||
|
||||
snprintf(response.status_msg, sizeof(response.status_msg), |
||||
"Statistics reset requested (not implemented)"); |
||||
|
||||
return send_response(cs, client, &response, sizeof(response)); |
||||
} |
||||
|
||||
// Send error response
|
||||
static int send_error_response(control_socket_t *cs, struct sockaddr_in *client, |
||||
uint16_t sequence, control_err_t error) { |
||||
control_status_packet_t response = {0}; |
||||
|
||||
response.response_type = CONTROL_RESP_ERROR; |
||||
response.error_code = error; |
||||
response.sequence = sequence; |
||||
|
||||
const char *error_msg = "Unknown error"; |
||||
switch (error) { |
||||
case CONTROL_ERR_INVALID_CMD: |
||||
error_msg = "Invalid command"; |
||||
break; |
||||
case CONTROL_ERR_INTERNAL: |
||||
error_msg = "Internal error"; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
|
||||
snprintf(response.status_msg, sizeof(response.status_msg), "%s", error_msg); |
||||
|
||||
cs->total_errors++; |
||||
|
||||
return send_response(cs, client, &response, sizeof(response)); |
||||
} |
||||
|
||||
// Send response to client
|
||||
static int send_response(control_socket_t *cs, struct sockaddr_in *client, |
||||
void *data, size_t len) { |
||||
ssize_t sent = sendto(cs->sockfd, data, len, 0, |
||||
(struct sockaddr*)client, sizeof(*client)); |
||||
|
||||
if (sent != (ssize_t)len) { |
||||
perror("control_socket: sendto"); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// Collect statistics from all modules
|
||||
static int collect_statistics(control_socket_t *cs, control_stats_packet_t *stats) { |
||||
if (!cs->app_state) { |
||||
return -1; |
||||
} |
||||
|
||||
utun_state_t *state = (utun_state_t*)cs->app_state; |
||||
|
||||
// Zero out stats
|
||||
memset(&stats->pkt_normalizer, 0, sizeof(stats->pkt_normalizer)); |
||||
memset(&stats->etcp, 0, sizeof(stats->etcp)); |
||||
memset(&stats->routing, 0, sizeof(stats->routing)); |
||||
memset(&stats->tun, 0, sizeof(stats->tun)); |
||||
stats->conn_count = 0; |
||||
|
||||
// Collect connection statistics
|
||||
if (state->connections && state->connection_count > 0) { |
||||
int max_conn = state->connection_count; |
||||
if (max_conn > MAX_CONN_STATS) max_conn = MAX_CONN_STATS; |
||||
|
||||
for (int i = 0; i < max_conn; i++) { |
||||
conn_handle_t *conn = state->connections[i]; |
||||
if (!conn) continue; |
||||
|
||||
conn_stats_t conn_stats; |
||||
if (conn_get_stats(conn, &conn_stats) == 0) { |
||||
// Aggregate connection stats
|
||||
stats->etcp.bytes_sent_total += conn_stats.bytes_sent; |
||||
stats->etcp.bytes_received_total += conn_stats.bytes_received; |
||||
stats->etcp.retransmissions += conn_stats.retransmissions; |
||||
stats->pkt_normalizer.fragments_assembled += conn_stats.fragments_assembled; |
||||
// Note: etcp and pkt_normalizer stats are aggregated across connections
|
||||
// Additional per-connection stats could be stored in conn_stats_entry_t
|
||||
} |
||||
|
||||
// Fill connection entry (if we have array)
|
||||
if (i < MAX_CONN_STATS) { |
||||
snprintf(stats->connections[i].name, sizeof(stats->connections[i].name), "conn%d", i); |
||||
stats->connections[i].bytes_sent = conn_stats.bytes_sent; |
||||
stats->connections[i].bytes_received = conn_stats.bytes_received; |
||||
stats->connections[i].packets_sent = conn_stats.packets_sent; |
||||
stats->connections[i].packets_received = conn_stats.packets_received; |
||||
stats->connections[i].retransmissions = conn_stats.retransmissions; |
||||
stats->connections[i].fragments_assembled = conn_stats.fragments_assembled; |
||||
stats->connections[i].current_rtt_ms = conn_stats.current_rtt_ms; |
||||
stats->connections[i].jitter_ms = conn_stats.jitter_ms; |
||||
stats->connections[i].is_active = 1; |
||||
} |
||||
} |
||||
stats->conn_count = max_conn; |
||||
} |
||||
|
||||
// Collect routing statistics
|
||||
if (state->routing_table) { |
||||
routing_get_stats(state->routing_table, &stats->routing); |
||||
} |
||||
|
||||
// Collect TUN statistics
|
||||
stats->tun.bytes_read = state->tun.bytes_read; |
||||
stats->tun.bytes_written = state->tun.bytes_written; |
||||
stats->tun.packets_read = state->tun.packets_read; |
||||
stats->tun.packets_written = state->tun.packets_written; |
||||
stats->tun.read_errors = state->tun.read_errors; |
||||
stats->tun.write_errors = state->tun.write_errors; |
||||
strncpy(stats->tun.ifname, state->tun.ifname, sizeof(stats->tun.ifname) - 1); |
||||
stats->tun.ifname[sizeof(stats->tun.ifname) - 1] = '\0'; |
||||
strncpy(stats->tun.ip_addr, state->tun.ip_addr, sizeof(stats->tun.ip_addr) - 1); |
||||
stats->tun.ip_addr[sizeof(stats->tun.ip_addr) - 1] = '\0'; |
||||
|
||||
// Collect system status
|
||||
return collect_system_status(cs, &stats->system); |
||||
} |
||||
|
||||
// Collect system status
|
||||
static int collect_system_status(control_socket_t *cs, system_status_t *status) { |
||||
if (!cs) return -1; |
||||
|
||||
memset(status, 0, sizeof(*status)); |
||||
|
||||
// Calculate uptime
|
||||
struct timeval now; |
||||
gettimeofday(&now, NULL); |
||||
status->uptime_seconds = now.tv_sec - cs->start_time.tv_sec; |
||||
|
||||
// Get memory usage (simplified)
|
||||
FILE *meminfo = fopen("/proc/self/statm", "r"); |
||||
if (meminfo) { |
||||
unsigned long pages; |
||||
if (fscanf(meminfo, "%lu", &pages) == 1) { |
||||
status->memory_usage_kb = pages * 4; // Assume 4KB pages
|
||||
} |
||||
fclose(meminfo); |
||||
} |
||||
|
||||
// CPU usage (placeholder)
|
||||
status->cpu_usage_percent = 0; |
||||
|
||||
// Version info
|
||||
snprintf(status->version, sizeof(status->version), "utun v0.1"); |
||||
|
||||
// Control port
|
||||
status->control_port = cs->port; |
||||
|
||||
// Running flag and connection count will be set by caller
|
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// Get ETCP statistics
|
||||
static void get_etcp_stats(epkt_t *etcp, etcp_stats_t *etcp_stats) { |
||||
if (!etcp || !etcp_stats) return; |
||||
|
||||
memset(etcp_stats, 0, sizeof(*etcp_stats)); |
||||
|
||||
// Use etcp_get_stats if available
|
||||
// etcp_get_stats(etcp, &retransmissions, ...);
|
||||
|
||||
// Direct access to epkt fields (assuming struct is visible)
|
||||
etcp_stats->retransmissions = etcp->retransmissions_count; |
||||
etcp_stats->total_packets_sent = etcp->total_packets_sent; |
||||
etcp_stats->unique_packets_sent = etcp->unique_packets_sent; |
||||
etcp_stats->bytes_sent_total = etcp->bytes_sent_total; |
||||
etcp_stats->bytes_received_total = etcp->bytes_received_total; |
||||
etcp_stats->ack_packets_count = etcp->ack_packets_count; |
||||
etcp_stats->control_packets_count = etcp->control_packets_count; |
||||
etcp_stats->current_rtt = etcp->rtt_avg_10; |
||||
etcp_stats->jitter = etcp->jitter; |
||||
etcp_stats->window_size = etcp->window_size; |
||||
etcp_stats->unacked_bytes = etcp->unacked_bytes; |
||||
} |
||||
|
||||
// Get packet normalizer statistics
|
||||
static void get_pkt_normalizer_stats(pkt_normalizer_pair *pair,
|
||||
pkt_normalizer_stats_t *pn_stats) { |
||||
if (!pair || !pn_stats) return; |
||||
|
||||
memset(pn_stats, 0, sizeof(*pn_stats)); |
||||
|
||||
if (pair->packer) { |
||||
pn_stats->packer_errors = pkt_normalizer_get_error_count(pair->packer); |
||||
} |
||||
|
||||
if (pair->unpacker) { |
||||
pn_stats->unpacker_errors = pkt_normalizer_get_error_count(pair->unpacker); |
||||
// Fragments assembled count might need to be tracked separately
|
||||
} |
||||
} |
||||
@ -1,208 +0,0 @@
|
||||
/**
|
||||
* @file control_socket.h |
||||
* @brief Control socket for statistics and diagnostics over UDP |
||||
* @details Provides remote monitoring of utun VPN tunnel statistics |
||||
*/ |
||||
|
||||
#ifndef CONTROL_SOCKET_H |
||||
#define CONTROL_SOCKET_H |
||||
|
||||
#include <stdint.h> |
||||
#include <stddef.h> |
||||
#include <stdbool.h> |
||||
|
||||
// Forward declarations for application state
|
||||
struct utun_state; |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
// Maximum connections for statistics
|
||||
#define MAX_CONN_STATS 16 |
||||
#define MAX_ROUTE_STATS 100 |
||||
|
||||
// Control socket command types
|
||||
typedef enum { |
||||
CONTROL_CMD_GET_STATS = 1, // Request statistics
|
||||
CONTROL_CMD_RESET_STATS = 2, // Reset statistics counters
|
||||
CONTROL_CMD_GET_STATUS = 3, // Request status information
|
||||
} control_cmd_t; |
||||
|
||||
// Response types
|
||||
typedef enum { |
||||
CONTROL_RESP_STATS = 1, // Statistics response
|
||||
CONTROL_RESP_STATUS = 2, // Status response
|
||||
CONTROL_RESP_ERROR = 0xFF, // Error response
|
||||
} control_resp_t; |
||||
|
||||
// Error codes
|
||||
typedef enum { |
||||
CONTROL_ERR_NONE = 0, |
||||
CONTROL_ERR_INVALID_CMD = 1, |
||||
CONTROL_ERR_INTERNAL = 2, |
||||
} control_err_t; |
||||
|
||||
// Packet normalizer statistics
|
||||
typedef struct __attribute__((packed)) { |
||||
uint32_t packer_errors; // Packer error count
|
||||
uint32_t unpacker_errors; // Unpacker error count
|
||||
uint32_t fragments_assembled; // Fragments assembled successfully
|
||||
uint32_t service_packets; // Service packets processed
|
||||
} pkt_normalizer_stats_t; |
||||
|
||||
// ETCP statistics
|
||||
typedef struct __attribute__((packed)) { |
||||
uint32_t retransmissions; // Retransmission count
|
||||
uint32_t total_packets_sent; // Total packets sent (including retransmissions)
|
||||
uint32_t unique_packets_sent; // Unique packets sent (excluding retransmissions)
|
||||
uint32_t bytes_sent_total; // Total bytes sent
|
||||
uint32_t bytes_received_total;// Total bytes received
|
||||
uint32_t ack_packets_count; // ACK packets sent
|
||||
uint32_t control_packets_count; // Control packets sent
|
||||
uint16_t current_rtt; // Current RTT (0.1ms units)
|
||||
uint16_t jitter; // Jitter (0.1ms units)
|
||||
uint32_t window_size; // Current window size (bytes)
|
||||
uint32_t unacked_bytes; // Unacknowledged bytes in flight
|
||||
} etcp_stats_t; |
||||
|
||||
// Connection statistics
|
||||
typedef struct __attribute__((packed)) { |
||||
uint64_t bytes_sent; |
||||
uint64_t bytes_received; |
||||
uint32_t packets_sent; |
||||
uint32_t packets_received; |
||||
uint32_t retransmissions; |
||||
uint32_t fragments_assembled; |
||||
uint16_t current_rtt_ms; |
||||
uint16_t jitter_ms; |
||||
uint8_t is_active; // 1 if connection is active
|
||||
char name[32]; // Connection name from config
|
||||
} conn_stats_entry_t; |
||||
|
||||
// Routing statistics
|
||||
typedef struct __attribute__((packed)) { |
||||
uint32_t total_routes; // Total routes in table
|
||||
uint32_t static_routes; // Static routes count
|
||||
uint32_t dynamic_routes; // Dynamic routes count
|
||||
uint32_t local_routes; // Local routes count
|
||||
uint32_t learned_routes; // Learned routes count
|
||||
uint32_t lookup_count; // Total lookups performed
|
||||
uint32_t hit_count; // Successful lookups
|
||||
uint32_t miss_count; // Failed lookups (no route)
|
||||
} routing_stats_t; |
||||
|
||||
// TUN interface statistics
|
||||
typedef struct __attribute__((packed)) { |
||||
uint64_t bytes_read; // Bytes read from TUN
|
||||
uint64_t bytes_written; // Bytes written to TUN
|
||||
uint32_t packets_read; // Packets read from TUN
|
||||
uint32_t packets_written; // Packets written to TUN
|
||||
uint32_t read_errors; // Read errors
|
||||
uint32_t write_errors; // Write errors
|
||||
char ifname[16]; // Interface name
|
||||
char ip_addr[32]; // IP address with prefix
|
||||
} tun_stats_t; |
||||
|
||||
// System status
|
||||
typedef struct __attribute__((packed)) { |
||||
uint32_t uptime_seconds; // Seconds since startup
|
||||
uint32_t memory_usage_kb; // Memory usage in KB (approximate)
|
||||
uint16_t cpu_usage_percent; // CPU usage percentage (0-100)
|
||||
uint8_t running; // 1 if main loop is running
|
||||
uint8_t connection_count; // Number of active connections
|
||||
uint16_t control_port; // Control socket port
|
||||
char version[32]; // Version string
|
||||
} system_status_t; |
||||
|
||||
// Complete statistics packet
|
||||
typedef struct __attribute__((packed)) { |
||||
uint8_t response_type; // CONTROL_RESP_STATS
|
||||
uint8_t error_code; // CONTROL_ERR_NONE if success
|
||||
uint16_t sequence; // Sequence number (echoed from request)
|
||||
|
||||
// Timestamps
|
||||
uint64_t timestamp; // Unix timestamp in microseconds
|
||||
|
||||
// Module statistics
|
||||
pkt_normalizer_stats_t pkt_normalizer; |
||||
etcp_stats_t etcp; |
||||
routing_stats_t routing; |
||||
tun_stats_t tun; |
||||
|
||||
// Connection statistics (variable length, up to MAX_CONN_STATS)
|
||||
uint8_t conn_count; // Number of connection entries
|
||||
conn_stats_entry_t connections[MAX_CONN_STATS]; |
||||
|
||||
// System status
|
||||
system_status_t system; |
||||
|
||||
// Reserved for future expansion
|
||||
uint8_t reserved[64]; |
||||
} control_stats_packet_t; |
||||
|
||||
// Status response packet
|
||||
typedef struct __attribute__((packed)) { |
||||
uint8_t response_type; // CONTROL_RESP_STATUS
|
||||
uint8_t error_code; // CONTROL_ERR_NONE if success
|
||||
uint16_t sequence; // Sequence number
|
||||
|
||||
system_status_t system; |
||||
uint8_t running; // Main loop running flag
|
||||
char status_msg[128]; // Human-readable status message
|
||||
} control_status_packet_t; |
||||
|
||||
// Request packet (from client)
|
||||
typedef struct __attribute__((packed)) { |
||||
uint8_t command; // CONTROL_CMD_*
|
||||
uint16_t sequence; // Sequence number for response matching
|
||||
uint8_t reserved; // Reserved, must be 0
|
||||
} control_request_packet_t; |
||||
|
||||
// Control socket handle (opaque)
|
||||
typedef struct control_socket control_socket_t; |
||||
|
||||
/**
|
||||
* @brief Create and initialize control socket |
||||
* @param ip IP address to bind to (NULL for any) |
||||
* @param port UDP port to listen on |
||||
* @return Control socket handle or NULL on error |
||||
*/ |
||||
control_socket_t* control_socket_create(const char *ip, uint16_t port); |
||||
|
||||
/**
|
||||
* @brief Destroy control socket and free resources |
||||
* @param cs Control socket handle |
||||
*/ |
||||
void control_socket_destroy(control_socket_t *cs); |
||||
|
||||
/**
|
||||
* @brief Get file descriptor for polling |
||||
* @param cs Control socket handle |
||||
* @return Socket file descriptor or -1 if invalid |
||||
*/ |
||||
int control_socket_get_fd(const control_socket_t *cs); |
||||
|
||||
/**
|
||||
* @brief Process incoming control socket requests |
||||
* @param cs Control socket handle |
||||
* @param state Application state (for collecting statistics) |
||||
* @return 0 on success, -1 on error |
||||
*
|
||||
* This function should be called when the socket is readable. |
||||
* It will handle incoming requests and send responses. |
||||
*/ |
||||
int control_socket_process(control_socket_t *cs, void *state); |
||||
|
||||
/**
|
||||
* @brief Set application state pointer for statistics collection |
||||
* @param cs Control socket handle |
||||
* @param state Application state (utun_state_t*) |
||||
*/ |
||||
void control_socket_set_state(control_socket_t *cs, void *state); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif /* CONTROL_SOCKET_H */ |
||||
@ -0,0 +1,66 @@
|
||||
// crc32.c - CRC32 checksum implementation
|
||||
#include "crc32.h" |
||||
#include <string.h> |
||||
|
||||
static uint32_t crc32_table[256]; |
||||
static int crc32_table_initialized = 0; |
||||
|
||||
static void init_crc32_table(void) { |
||||
if (crc32_table_initialized) return; |
||||
|
||||
for (uint32_t i = 0; i < 256; i++) { |
||||
uint32_t crc = i; |
||||
for (int j = 0; j < 8; j++) { |
||||
if (crc & 1) { |
||||
crc = (crc >> 1) ^ 0xEDB88320; |
||||
} else { |
||||
crc >>= 1; |
||||
} |
||||
} |
||||
crc32_table[i] = crc; |
||||
} |
||||
|
||||
crc32_table_initialized = 1; |
||||
} |
||||
|
||||
void crc32_init(void) { |
||||
init_crc32_table(); |
||||
} |
||||
|
||||
uint32_t crc32_calc(const uint8_t *data, size_t len) { |
||||
if (!crc32_table_initialized) { |
||||
init_crc32_table(); |
||||
} |
||||
|
||||
if (!data || len == 0) return 0xFFFFFFFF; |
||||
|
||||
uint32_t crc = 0xFFFFFFFF; |
||||
for (size_t i = 0; i < len; i++) { |
||||
uint8_t byte = data[i]; |
||||
uint32_t table_index = (crc ^ byte) & 0xFF; |
||||
crc = (crc >> 8) ^ crc32_table[table_index]; |
||||
} |
||||
|
||||
return ~crc; |
||||
} |
||||
|
||||
uint32_t crc32_calc_ex(const uint8_t *data, size_t len, uint32_t initial_crc) { |
||||
if (!crc32_table_initialized) { |
||||
init_crc32_table(); |
||||
} |
||||
|
||||
if (!data || len == 0) return initial_crc; |
||||
|
||||
uint32_t crc = initial_crc; |
||||
for (size_t i = 0; i < len; i++) { |
||||
uint8_t byte = data[i]; |
||||
uint32_t table_index = (crc ^ byte) & 0xFF; |
||||
crc = (crc >> 8) ^ crc32_table[table_index]; |
||||
} |
||||
|
||||
return crc; |
||||
} |
||||
|
||||
uint32_t crc32_update(uint32_t crc, const uint8_t *data, size_t len) { |
||||
return crc32_calc_ex(data, len, crc); |
||||
} |
||||
@ -0,0 +1,19 @@
|
||||
#ifndef CRC32_H |
||||
#define CRC32_H |
||||
|
||||
#include <stdint.h> |
||||
#include <stddef.h> |
||||
|
||||
// Initialize CRC32 table (called automatically on first use)
|
||||
void crc32_init(void); |
||||
|
||||
// Calculate CRC32 checksum
|
||||
uint32_t crc32_calc(const uint8_t *data, size_t len); |
||||
|
||||
// Calculate CRC32 with initial value
|
||||
uint32_t crc32_calc_ex(const uint8_t *data, size_t len, uint32_t initial_crc); |
||||
|
||||
// Update CRC32 incrementally
|
||||
uint32_t crc32_update(uint32_t crc, const uint8_t *data, size_t len); |
||||
|
||||
#endif // CRC32_H
|
||||
@ -0,0 +1,530 @@
|
||||
#include "etcp_connections.h" |
||||
#include <arpa/inet.h> |
||||
#include "routing.h" |
||||
#include "utun_instance.h" |
||||
#include "crc32.h" |
||||
#include "etcp.h" |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <time.h> |
||||
|
||||
// Forward declaration
|
||||
struct utun_instance; |
||||
|
||||
// Бинарный поиск линка по ip_port_hash
|
||||
static int find_link_index(struct ETCP_CONNECTIONS* conns, uint32_t hash) { |
||||
if (!conns || conns->num_channels == 0) return -1; |
||||
|
||||
int left = 0; |
||||
int right = conns->num_channels - 1; |
||||
|
||||
while (left <= right) { |
||||
int mid = left + (right - left) / 2; |
||||
if (conns->links[mid]->ip_port_hash == hash) { |
||||
return mid; |
||||
} else if (conns->links[mid]->ip_port_hash < hash) { |
||||
left = mid + 1; |
||||
} else { |
||||
right = mid - 1; |
||||
} |
||||
} |
||||
|
||||
return -(left + 1); |
||||
} |
||||
|
||||
// Реалокация массива линков с увеличением в 2 раза
|
||||
static int realloc_links(struct ETCP_CONNECTIONS* conns) { |
||||
size_t new_max = conns->max_channels == 0 ? 8 : conns->max_channels * 2; |
||||
struct ETCP_LINK** new_links = realloc(conns->links, new_max * sizeof(struct ETCP_LINK*)); |
||||
if (!new_links) return -1; |
||||
|
||||
conns->links = new_links; |
||||
conns->max_channels = new_max; |
||||
return 0; |
||||
} |
||||
|
||||
// Вставка линка в отсортированный массив
|
||||
static int insert_link(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link) { |
||||
if (!conns || !link) return -1; |
||||
|
||||
if (conns->num_channels >= conns->max_channels) { |
||||
if (realloc_links(conns) < 0) return -1; |
||||
} |
||||
|
||||
int idx = find_link_index(conns, link->ip_port_hash); |
||||
if (idx >= 0) return -1; |
||||
|
||||
idx = -(idx + 1); |
||||
|
||||
if (idx < (int)conns->num_channels) { |
||||
memmove(&conns->links[idx + 1], &conns->links[idx],
|
||||
(conns->num_channels - idx) * sizeof(struct ETCP_LINK*)); |
||||
} |
||||
|
||||
conns->links[idx] = link; |
||||
conns->num_channels++; |
||||
return 0; |
||||
} |
||||
|
||||
// Удаление линка из массива
|
||||
static void remove_link(struct ETCP_CONNECTIONS* conns, uint32_t hash) { |
||||
if (!conns || conns->num_channels == 0) return; |
||||
|
||||
int idx = find_link_index(conns, hash); |
||||
if (idx < 0) return; |
||||
|
||||
if (idx < (int)conns->num_channels - 1) { |
||||
memmove(&conns->links[idx], &conns->links[idx + 1],
|
||||
(conns->num_channels - idx - 1) * sizeof(struct ETCP_LINK*)); |
||||
} |
||||
|
||||
conns->num_channels--; |
||||
} |
||||
|
||||
struct ETCP_CONNECTIONS* etcp_connections_init(etcp_socket_t* socket) { |
||||
if (!socket) return NULL; |
||||
|
||||
struct ETCP_CONNECTIONS* conns = calloc(1, sizeof(struct ETCP_CONNECTIONS)); |
||||
if (!conns) return NULL; |
||||
|
||||
memcpy(&conns->socket, socket, sizeof(struct ETCP_SOCKET)); |
||||
return conns; |
||||
} |
||||
|
||||
void etcp_connections_destroy(struct ETCP_CONNECTIONS* conns) { |
||||
if (!conns) return; |
||||
|
||||
for (size_t i = 0; i < conns->num_channels; i++) { |
||||
etcp_link_close(conns->links[i]); |
||||
} |
||||
|
||||
free(conns->links); |
||||
free(conns); |
||||
} |
||||
|
||||
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, etcp_socket_t* socket,
|
||||
const struct sockaddr* remote_addr, socklen_t addr_len) { |
||||
if (!etcp || !socket || !remote_addr || addr_len == 0) return NULL; |
||||
|
||||
struct ETCP_LINK* link = calloc(1, sizeof(struct ETCP_LINK)); |
||||
if (!link) return NULL; |
||||
|
||||
link->socket = (struct ETCP_SOCKET*)socket; |
||||
link->etcp = etcp; |
||||
memcpy(&link->remote_addr, remote_addr, addr_len); |
||||
link->remote_addr_len = addr_len; |
||||
link->last_activity = time(NULL); |
||||
|
||||
uint8_t* addr_bytes = (uint8_t*)remote_addr; |
||||
link->ip_port_hash = crc32(0, addr_bytes, addr_len); |
||||
|
||||
return link; |
||||
} |
||||
|
||||
void etcp_link_close(struct ETCP_LINK* link) { |
||||
if (!link) return; |
||||
free(link); |
||||
} |
||||
|
||||
int etcp_input(packet_buffer_t* pkt, etcp_socket_t* socket, struct ETCP_CONNECTIONS* conns) { |
||||
if (!pkt || !socket || !conns) return -1; |
||||
return 0; |
||||
} |
||||
|
||||
int etcp_link_send(struct ETCP_CONN* etcp, struct ETCP_LINK* link,
|
||||
const uint8_t* data, size_t len) { |
||||
if (!etcp || !link || !data || len == 0) return -1; |
||||
|
||||
link->last_activity = time(NULL); |
||||
|
||||
return etcp_socket_send((etcp_socket_t*)link->socket, data, len,
|
||||
(struct sockaddr*)&link->remote_addr); |
||||
} |
||||
|
||||
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_CONNECTIONS* conns,
|
||||
const struct sockaddr* addr, socklen_t addr_len) { |
||||
if (!conns || !addr || addr_len == 0) return NULL; |
||||
|
||||
uint32_t hash = crc32(0, (uint8_t*)addr, addr_len); |
||||
int idx = find_link_index(conns, hash); |
||||
|
||||
if (idx >= 0) { |
||||
return conns->links[idx]; |
||||
} |
||||
return NULL; |
||||
} |
||||
|
||||
int etcp_link_add_to_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link) { |
||||
if (!conns || !link) return -1; |
||||
return insert_link(conns, link); |
||||
} |
||||
|
||||
void etcp_link_remove_from_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link) { |
||||
if (!conns || !link) return; |
||||
remove_link(conns, link->ip_port_hash); |
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
// SOCKET FUNCTIONS (moved from etcp_sockets.c)
|
||||
#include <unistd.h> |
||||
#include <fcntl.h> |
||||
#include <errno.h> |
||||
#include <arpa/inet.h> |
||||
|
||||
etcp_socket_t* etcp_socket_create(struct ETCP_CONN* etcp, const char* bind_addr, uint16_t port) { |
||||
if (!etcp) return NULL; |
||||
|
||||
etcp_socket_t* sock = calloc(1, sizeof(etcp_socket_t)); |
||||
if (!sock) return NULL; |
||||
|
||||
sock->etcp = etcp; |
||||
sock->socket_id = (uint32_t)(uintptr_t)sock; |
||||
|
||||
sock->fd = socket(AF_INET, SOCK_DGRAM, 0); |
||||
if (sock->fd < 0) { |
||||
free(sock); |
||||
return NULL; |
||||
} |
||||
|
||||
int flags = fcntl(sock->fd, F_GETFL, 0); |
||||
fcntl(sock->fd, F_SETFL, flags | O_NONBLOCK); |
||||
|
||||
struct sockaddr_in* addr = (struct sockaddr_in*)&sock->local_addr; |
||||
addr->sin_family = AF_INET; |
||||
addr->sin_port = htons(port); |
||||
sock->local_addr_len = sizeof(struct sockaddr_in); |
||||
|
||||
if (bind_addr) { |
||||
inet_pton(AF_INET, bind_addr, &addr->sin_addr); |
||||
} else { |
||||
addr->sin_addr.s_addr = INADDR_ANY; |
||||
} |
||||
|
||||
if (bind(sock->fd, (struct sockaddr*)addr, sock->local_addr_len) < 0) { |
||||
close(sock->fd); |
||||
free(sock); |
||||
return NULL; |
||||
} |
||||
|
||||
if (getsockname(sock->fd, (struct sockaddr*)addr, &sock->local_addr_len) < 0) { |
||||
close(sock->fd); |
||||
free(sock); |
||||
return NULL; |
||||
} |
||||
|
||||
return sock; |
||||
} |
||||
|
||||
void etcp_socket_destroy(etcp_socket_t* sock) { |
||||
if (!sock) return; |
||||
|
||||
if (sock->fd >= 0) { |
||||
close(sock->fd); |
||||
} |
||||
|
||||
free(sock); |
||||
} |
||||
|
||||
int etcp_socket_send(etcp_socket_t* sock, const uint8_t* data, size_t len, const struct sockaddr* to_addr) { |
||||
if (!sock || sock->fd < 0 || !data || !to_addr) { |
||||
return -1; |
||||
} |
||||
|
||||
socklen_t addr_len = (to_addr->sa_family == AF_INET) ?
|
||||
sizeof(struct sockaddr_in) :
|
||||
sizeof(struct sockaddr_in6); |
||||
|
||||
return sendto(sock->fd, data, len, 0, to_addr, addr_len); |
||||
} |
||||
|
||||
int etcp_socket_get_fd(const etcp_socket_t* sock) { |
||||
if (!sock) return -1; |
||||
return sock->fd; |
||||
} |
||||
|
||||
void etcp_socket_get_local_addr(const etcp_socket_t* sock, struct sockaddr_storage* addr, socklen_t* addr_len) { |
||||
if (!sock || !addr || !addr_len) return; |
||||
|
||||
memcpy(addr, &sock->local_addr, sock->local_addr_len); |
||||
*addr_len = sock->local_addr_len; |
||||
} |
||||
|
||||
int etcp_socket_set_option(etcp_socket_t* sock, int level, int optname, const void* optval, socklen_t optlen) { |
||||
if (!sock || sock->fd < 0) return -1; |
||||
return setsockopt(sock->fd, level, optname, optval, optlen); |
||||
} |
||||
// Connection management functions
|
||||
void conn_destroy(conn_handle_t* handle) { |
||||
if (!handle) return; |
||||
if (handle->conns) { |
||||
etcp_socket_destroy((etcp_socket_t*)&handle->conns->socket); |
||||
etcp_connections_destroy(handle->conns); |
||||
} |
||||
if (handle->etcp) { |
||||
etcp_destroy(handle->etcp); |
||||
} |
||||
free(handle); |
||||
} |
||||
|
||||
int conn_send(conn_handle_t* handle, const uint8_t* data, size_t len) { |
||||
if (!handle || !handle->conns || handle->conns->num_channels == 0 || !data || len == 0) { |
||||
return -1; |
||||
} |
||||
|
||||
struct ETCP_LINK* link = handle->conns->links[0]; |
||||
if (!link || !link->etcp) return -1; |
||||
|
||||
return etcp_link_send(link->etcp, link, data, len); |
||||
} |
||||
|
||||
// Parse address in format "IP:port" or "[IPv6]:port" and fill sockaddr
|
||||
static int parse_addr_port(const char* addr_str, struct sockaddr_storage* addr, socklen_t* addr_len) { |
||||
if (!addr_str || !addr || !addr_len) return -1; |
||||
|
||||
char addr_copy[MAX_ADDR_LEN]; |
||||
strncpy(addr_copy, addr_str, MAX_ADDR_LEN - 1); |
||||
addr_copy[MAX_ADDR_LEN - 1] = '\0'; |
||||
|
||||
// Check for IPv6 format: [address]:port
|
||||
if (addr_copy[0] == '[') { |
||||
char* closing_bracket = strchr(addr_copy, ']'); |
||||
if (!closing_bracket) return -1; |
||||
|
||||
// Find port after ']:'
|
||||
char* colon = strchr(closing_bracket + 1, ':'); |
||||
if (!colon) return -1; |
||||
|
||||
*closing_bracket = '\0'; |
||||
uint16_t port = (uint16_t)atoi(colon + 1); |
||||
|
||||
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; |
||||
memset(sin6, 0, sizeof(struct sockaddr_in6)); |
||||
sin6->sin6_family = AF_INET6; |
||||
sin6->sin6_port = htons(port); |
||||
|
||||
// Extract IP string and port from sockaddr_storage
|
||||
static int sockaddr_to_ip_port(const struct sockaddr_storage* addr, char* ip_out, size_t ip_out_len, uint16_t* port_out) { |
||||
if (!addr || !ip_out || !port_out) return -1; |
||||
|
||||
if (addr->ss_family == AF_INET) { |
||||
struct sockaddr_in* sin = (struct sockaddr_in*)addr; |
||||
*port_out = ntohs(sin->sin_port); |
||||
if (inet_ntop(AF_INET, &sin->sin_addr, ip_out, ip_out_len) == NULL) return -1; |
||||
} else if (addr->ss_family == AF_INET6) { |
||||
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; |
||||
*port_out = ntohs(sin6->sin6_port); |
||||
if (inet_ntop(AF_INET6, &sin6->sin6_addr, ip_out, ip_out_len) == NULL) return -1; |
||||
} else { |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
for (int i = 0; i < config->server_count; i++) { |
||||
server_config_t* server = &config->servers[i]; |
||||
|
||||
char bind_addr[INET_ADDRSTRLEN]; |
||||
struct sockaddr_storage server_addr; |
||||
socklen_t server_addr_len; |
||||
if (parse_addr_port(server->addr, &server_addr, &server_addr_len) < 0) { |
||||
fprintf(stderr, "Invalid server address '%s'\n", server->addr); |
||||
goto cleanup; |
||||
} |
||||
char bind_addr_str[INET6_ADDRSTRLEN]; |
||||
uint16_t bind_port; |
||||
if (sockaddr_to_ip_port(&server_addr, bind_addr_str, sizeof(bind_addr_str), &bind_port) < 0) { |
||||
etcp_destroy(etcp); |
||||
goto cleanup; |
||||
} |
||||
} |
||||
|
||||
etcp_socket_t* sock = etcp_socket_create(etcp, bind_addr, bind_port); |
||||
if (!sock) { |
||||
fprintf(stderr, "Failed to create socket for %s\n", server->name); |
||||
etcp_destroy(etcp); |
||||
goto cleanup; |
||||
} |
||||
|
||||
if (i == 0) { |
||||
instance->first_listen_socket = (struct ETCP_SOCKET*)sock; |
||||
} |
||||
|
||||
struct ETCP_CONNECTIONS* conns = etcp_connections_init(sock); |
||||
if (!conns) { |
||||
etcp_socket_destroy(sock); |
||||
etcp_destroy(etcp); |
||||
goto cleanup; |
||||
} |
||||
|
||||
conn_handle_t* handle = calloc(1, sizeof(conn_handle_t)); |
||||
if (!handle) { |
||||
etcp_connections_destroy(conns); |
||||
etcp_socket_destroy(sock); |
||||
etcp_destroy(etcp); |
||||
goto cleanup; |
||||
} |
||||
|
||||
handle->etcp = etcp; |
||||
handle->conns = conns; |
||||
|
||||
for (int j = 0; j < config->client_count; j++) { |
||||
client_config_t* client = &config->clients[j]; |
||||
if (strcmp(client->from, server->name) != 0) continue; |
||||
|
||||
struct sockaddr_storage client_addr; |
||||
socklen_t client_addr_len; |
||||
if (parse_addr_port(client->to_addr, &client_addr, &client_addr_len) < 0) { |
||||
fprintf(stderr, "Invalid client address '%s'\n", client->to_addr); |
||||
continue; |
||||
} |
||||
memcpy(&addr, &client_addr, client_addr_len); |
||||
addr_len = client_addr_len; |
||||
struct sockaddr_in* sin = (struct sockaddr_in*)&addr; |
||||
memset(sin, 0, sizeof(struct sockaddr_in)); |
||||
sin->sin_family = AF_INET; |
||||
sin->sin_port = htons(client_port); |
||||
if (inet_pton(AF_INET, client_addr, &sin->sin_addr) != 1) continue; |
||||
addr_len = sizeof(struct sockaddr_in); |
||||
|
||||
struct ETCP_LINK* link = etcp_link_new(etcp, sock, (struct sockaddr*)&addr, addr_len); |
||||
if (link) { |
||||
etcp_link_add_to_connections(conns, link); |
||||
} |
||||
} |
||||
|
||||
instance->connections[i] = handle; |
||||
} |
||||
|
||||
instance->connection_count = config->server_count; |
||||
return 0; |
||||
|
||||
cleanup: |
||||
if (instance->connections) { |
||||
for (int j = 0; j < 0; j++) { |
||||
if (instance->connections[j]) { |
||||
conn_destroy(instance->connections[j]); |
||||
} |
||||
} |
||||
free(instance->connections); |
||||
instance->connections = NULL; |
||||
} |
||||
instance->first_listen_socket = NULL; |
||||
instance->connection_count = 0; |
||||
return -1; |
||||
} |
||||
#include "etcp_connections.h" |
||||
#include "etcp.h" |
||||
#include "config_parser.h" |
||||
#include "utun_instance.h" |
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
// Parse address in format "IP:port" or "[IPv6]:port" and fill sockaddr
|
||||
if (!addr || !ip_out || !port_out) return -1; |
||||
|
||||
if (addr->ss_family == AF_INET) { |
||||
struct sockaddr_in* sin = (struct sockaddr_in*)addr; |
||||
*port_out = ntohs(sin->sin_port); |
||||
if (inet_ntop(AF_INET, &sin->sin_addr, ip_out, ip_out_len) == NULL) return -1; |
||||
} else if (addr->ss_family == AF_INET6) { |
||||
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; |
||||
*port_out = ntohs(sin6->sin6_port); |
||||
if (inet_ntop(AF_INET6, &sin6->sin6_addr, ip_out, ip_out_len) == NULL) return -1; |
||||
} else { |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
|
||||
int init_connections(utun_instance_t* instance) { |
||||
if (!instance || !instance->config) { |
||||
fprintf(stderr, "Invalid instance or config\n"); |
||||
return -1; |
||||
} |
||||
|
||||
utun_config_t* config = instance->config; |
||||
int total_connections = config->server_count, i = 0; |
||||
|
||||
if (total_connections == 0) { |
||||
fprintf(stderr, "No servers found in configuration\n"); |
||||
return -1; |
||||
} |
||||
|
||||
instance->connections = calloc(total_connections, sizeof(conn_handle_t*)); |
||||
if (!instance->connections) { |
||||
fprintf(stderr, "Failed to allocate connections array\n"); |
||||
return -1; |
||||
} |
||||
|
||||
for (i = 0; i < config->server_count; i++) { |
||||
server_config_t* server = &config->servers[i]; |
||||
|
||||
struct sockaddr_storage server_addr; |
||||
socklen_t server_addr_len; |
||||
if (parse_addr_port(server->addr, &server_addr, &server_addr_len) < 0) { |
||||
fprintf(stderr, "Invalid server address '%s'\n", server->addr); |
||||
goto cleanup; |
||||
} |
||||
|
||||
struct ETCP_CONN* etcp = etcp_create(instance); |
||||
if (!etcp) { |
||||
fprintf(stderr, "Failed to create ETCP for server %s\n", server->name); |
||||
goto cleanup; |
||||
} |
||||
|
||||
char bind_addr_str[INET6_ADDRSTRLEN]; |
||||
} |
||||
|
||||
conn_handle_t* handle = calloc(1, sizeof(conn_handle_t)); |
||||
if (!handle) { |
||||
etcp_connections_destroy(conns); |
||||
etcp_socket_destroy(sock); |
||||
etcp_destroy(etcp); |
||||
goto cleanup; |
||||
} |
||||
|
||||
handle->etcp = etcp; |
||||
handle->conns = conns; |
||||
|
||||
for (int j = 0; j < config->client_count; j++) { |
||||
client_config_t* client = &config->clients[j]; |
||||
if (strcmp(client->from, server->name) != 0) continue; |
||||
|
||||
struct sockaddr_storage client_addr; |
||||
socklen_t client_addr_len; |
||||
if (parse_addr_port(client->to_addr, &client_addr, &client_addr_len) < 0) { |
||||
fprintf(stderr, "Invalid client address '%s'\n", client->to_addr); |
||||
continue; |
||||
} |
||||
|
||||
struct sockaddr_storage addr; |
||||
socklen_t addr_len = client_addr_len; |
||||
|
||||
struct ETCP_LINK* link = etcp_link_new(etcp, sock, (struct sockaddr*)&addr, addr_len); |
||||
if (link) { |
||||
etcp_link_add_to_connections(conns, link); |
||||
} |
||||
} |
||||
|
||||
instance->connections[i] = handle; |
||||
} |
||||
|
||||
instance->connection_count = config->server_count; |
||||
return 0; |
||||
|
||||
cleanup: |
||||
if (instance->connections) { |
||||
for (int j = 0; j < i; j++) { |
||||
if (instance->connections[j]) { |
||||
conn_destroy(instance->connections[j]); |
||||
} |
||||
} |
||||
free(instance->connections); |
||||
instance->connections = NULL; |
||||
} |
||||
instance->first_listen_socket = NULL; |
||||
instance->connection_count = 0; |
||||
return -1; |
||||
@ -0,0 +1,78 @@
|
||||
#ifndef ETCP_CONNECTIONS_H |
||||
#define ETCP_CONNECTIONS_H |
||||
|
||||
#include "packet_buffer.h" |
||||
#include <stdint.h> |
||||
#include <sys/socket.h> |
||||
|
||||
// Forward declarations
|
||||
typedef struct utun_instance utun_instance_t; |
||||
typedef struct etcp_socket etcp_socket_t; |
||||
|
||||
struct ETCP_SOCKET { |
||||
int fd; // Файловый дескриптор UDP сокета
|
||||
struct sockaddr_storage local_addr; // Локальный адрес
|
||||
socklen_t local_addr_len; // Длина локального адреса
|
||||
|
||||
struct ETCP_CONN* etcp; // Родительский ETCP instance
|
||||
uint32_t socket_id; // Уникальный ID сокета
|
||||
|
||||
// Callback при получении данных не нужен. вызываем сразу etcp_input -> etcp_conn_input
|
||||
}; |
||||
|
||||
|
||||
// список активных подключений которые обслуживает сокет. каждый сокет может обслуживать много подключений
|
||||
struct ETCP_CONNECTIONS { |
||||
struct ETCP_CONNECTIONS* next; // Linked list для всех соединений
|
||||
|
||||
// сокет для отправки/приема
|
||||
struct ETCP_SOCKET socket; // Сокет через который идет трафик
|
||||
|
||||
// для входящих подключений - массив упорядоченный по ip_port_hash
|
||||
size_t max_channels; // сколько выделено памяти
|
||||
size_t num_channels; // сколько активно
|
||||
struct ETCP_LINK** links;// массив указателей на линки, сортированный по ip_port_hash
|
||||
}; |
||||
|
||||
// ETCP Link - одно динамическое соединение (один путь)
|
||||
struct ETCP_LINK { |
||||
uint32_t ip_port_hash; // crc32 для быстрого поиска
|
||||
struct ETCP_LINK* next; // Linked list подключений для ETCP_CONN
|
||||
|
||||
struct ETCP_SOCKET* socket; // Сокет через который идет трафик
|
||||
struct ETCP_CONN* etcp; // Родительский ETCP instance
|
||||
|
||||
// Путь соединения
|
||||
struct sockaddr_storage remote_addr; // Удалённый адрес
|
||||
socklen_t remote_addr_len; |
||||
|
||||
uint64_t last_activity; // Время последней активности
|
||||
uint8_t initialized; |
||||
}; |
||||
|
||||
// SOCKET FUNCTIONS (moved from etcp_sockets)
|
||||
etcp_socket_t* etcp_socket_create(struct ETCP_CONN* etcp, const char* bind_addr, uint16_t port); |
||||
void etcp_socket_destroy(etcp_socket_t* sock); |
||||
int etcp_socket_send(etcp_socket_t* sock, const uint8_t* data, size_t len, const struct sockaddr* to_addr); |
||||
void etcp_socket_set_recv_callback(etcp_socket_t* sock, void (*callback)(struct etcp_socket*, packet_buffer_t*, void*), void* user_data); |
||||
int etcp_socket_get_fd(const etcp_socket_t* sock); |
||||
void etcp_socket_get_local_addr(const etcp_socket_t* sock, struct sockaddr_storage* addr, socklen_t* addr_len); |
||||
int etcp_socket_set_option(etcp_socket_t* sock, int level, int optname, const void* optval, socklen_t optlen); |
||||
|
||||
// CONNECTIONS FUNCTIONS
|
||||
struct ETCP_CONNECTIONS* etcp_connections_init(etcp_socket_t* socket); |
||||
void etcp_connections_destroy(struct ETCP_CONNECTIONS* conns); |
||||
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, etcp_socket_t* socket, const struct sockaddr* remote_addr, socklen_t addr_len); |
||||
void etcp_link_close(struct ETCP_LINK* link); |
||||
int etcp_input(packet_buffer_t* pkt, etcp_socket_t* socket, struct ETCP_CONNECTIONS* conns); |
||||
int etcp_link_send(struct ETCP_CONN* etcp, struct ETCP_LINK* link, const uint8_t* data, size_t len); |
||||
|
||||
// CONNECTION MANAGEMENT
|
||||
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_CONNECTIONS* conns, const struct sockaddr* addr, socklen_t addr_len); |
||||
int etcp_link_add_to_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link); |
||||
void etcp_link_remove_from_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link); |
||||
|
||||
// INITIALIZATION
|
||||
int init_connections(struct utun_instance* instance); |
||||
|
||||
#endif // ETCP_CONNECTIONS_H
|
||||
@ -0,0 +1,49 @@
|
||||
Протокол ETCP. Как работает |
||||
протокол похож на QUICK но с небольшими отличиями: |
||||
- устанавливает между клиентом и сервером только одно ETCP соединение, но для доставки пакетов может использовать разные маршруты (несколько разных сокетов и адресов назначения). |
||||
ETCP соединение не должно накапливать очереди (где это возможно отправлять пакет сразу, в остальных случаях pacing - он есть в ll_queue). только держать отправленные но не подтвержденные пакеты. |
||||
|
||||
|
||||
|
||||
utun при запуске генерирует свой рандом ID (64bit). назовём его ID узла. если его нет в конфиге, он прописывается в конфиг. т.е. при перезапуске не меняется. |
||||
|
||||
- сокеты открываются при запуске, при работе не меняются. исходящие подключения - через эти же сокеты. |
||||
|
||||
лучше сделать так - пакет полуен из сокета -> в функции обработки сразу выделение буфера, расшифровка в этот буфер, вызов из etcp_channel функции разбора пакета с аргументом структуры и ip_port. |
||||
в etcp_chanel функция разобра пакета: |
||||
- осуществит подсчет хеша, поиск канала |
||||
- если найден - добавляем пакет в очередь, |
||||
- не найден и пакет не init - отправляем RST (надо переподключиться) |
||||
- init и не найден - создание инстанса и добавление пакета в его очередь. (создаём новое подключение) |
||||
|
||||
в ETCP: |
||||
Если получили RST: |
||||
- очищаем все состояния, буфера итд, но сохраняем исходящую очередь, |
||||
- инициируем подключение заново (периодически отправляем init request и ждем init_response) |
||||
|
||||
ip_port: |
||||
uinion{ addr (v4 или v6)} |
||||
u16 port |
||||
u8 ip_type (v4/v6) |
||||
|
||||
packet bufer: |
||||
*etcp_channel (заполняется при нахождении или создании) |
||||
payload расшифрованного пакета |
||||
|
||||
etcp_channel: |
||||
- *next (linked-list каналов для одного etcp инстанса) чтобы быстро удалять подключение. |
||||
- ip:port удаленной стороны в бинарном виде |
||||
- указатель на etcp_socket (сокеты открываются при запуске те что в конфиге и через них весь обмен в т.ч. исходящий трафик. к каждому узлу может быть только одно etcp подключение) |
||||
- хеш ip:port (32bit, crc32) |
||||
- указатель на инстанс etcp |
||||
|
||||
etcp_socket: |
||||
socket |
||||
uasync *socket |
||||
|
||||
записи etcp_channel при создании записываются в массив и сортируются по хешу ip:port для быстрого поиска. |
||||
|
||||
etcp содержит: |
||||
- вместо unique id подключения - ID удалённого узла (которое передается в init request и init response) |
||||
- *etcp_first_channel |
||||
- очереди и прочее необходимое для работы |
||||
@ -0,0 +1,40 @@
|
||||
#ifndef ETCP_SOCKET_H |
||||
#define ETCP_SOCKET_H |
||||
|
||||
#include "packet_buffer.h" |
||||
#include <sys/socket.h> |
||||
#include <sys/types.h> |
||||
#include <netinet/in.h> |
||||
#include <stdint.h> |
||||
|
||||
// Forward declarations
|
||||
struct ETCP_CONN; |
||||
struct ETCP_CONNECTIONS; |
||||
typedef struct utun_instance utun_instance_t; |
||||
|
||||
// Handle для одного соединения (peer)
|
||||
typedef struct conn_handle { |
||||
struct ETCP_CONN* etcp; |
||||
struct ETCP_CONNECTIONS* conns; |
||||
} conn_handle_t; |
||||
|
||||
// ETCP Socket - интегрированный UDP сокет с ETCP функциональностью
|
||||
typedef struct etcp_socket { |
||||
int fd; // Файловый дескриптор UDP сокета
|
||||
struct sockaddr_storage local_addr; // Локальный адрес
|
||||
socklen_t local_addr_len; // Длина локального адреса
|
||||
|
||||
struct ETCP_CONN* etcp; // Родительский ETCP instance
|
||||
uint32_t socket_id; // Уникальный ID сокета
|
||||
|
||||
// Callback для получения данных
|
||||
void (*recv_callback)(struct etcp_socket* sock, packet_buffer_t* pkt, void* user_data); |
||||
void* recv_user_data; // User data для callback
|
||||
} etcp_socket_t; |
||||
|
||||
// Callback для получения данных
|
||||
typedef void (*etcp_socket_recv_cb)(struct etcp_socket* sock, |
||||
packet_buffer_t* pkt, |
||||
void* user_data); |
||||
|
||||
#endif // ETCP_SOCKET_H
|
||||
@ -0,0 +1,62 @@
|
||||
#include "memory_pool.h" |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
// Инициализировать пул памяти
|
||||
void memory_pool_init(memory_pool_t* pool, size_t object_size) { |
||||
pool->object_size = object_size; |
||||
pool->free_count = 0; |
||||
pool->allocations = 0; |
||||
pool->reuse_count = 0; |
||||
memset(pool->free_list, 0, sizeof(pool->free_list)); |
||||
} |
||||
|
||||
// Выделить объект из пула или из malloc
|
||||
void* memory_pool_alloc(memory_pool_t* pool) { |
||||
pool->allocations++; |
||||
|
||||
// Если есть свободные объекты в пуле, использовать их
|
||||
if (pool->free_count > 0) { |
||||
pool->free_count--; |
||||
pool->reuse_count++; |
||||
void* obj = pool->free_list[pool->free_count]; |
||||
pool->free_list[pool->free_count] = NULL; |
||||
return obj; |
||||
} |
||||
|
||||
// Иначе выделить через malloc
|
||||
return malloc(pool->object_size); |
||||
} |
||||
|
||||
// Освободить объект в пул или в free
|
||||
void memory_pool_free(memory_pool_t* pool, void* obj) { |
||||
if (!obj) return; |
||||
|
||||
// Если пул не заполнен, сохранить объект для повторного использования
|
||||
if (pool->free_count < MEMORY_POOL_SIZE) { |
||||
pool->free_list[pool->free_count] = obj; |
||||
pool->free_count++; |
||||
return; |
||||
} |
||||
|
||||
// Иначе освободить через free
|
||||
free(obj); |
||||
} |
||||
|
||||
// Получить статистику пула
|
||||
void memory_pool_get_stats(memory_pool_t* pool, size_t* allocations, size_t* reuse_count) { |
||||
if (allocations) *allocations = pool->allocations; |
||||
if (reuse_count) *reuse_count = pool->reuse_count; |
||||
} |
||||
|
||||
// Очистить пул памяти
|
||||
void memory_pool_destroy(memory_pool_t* pool) { |
||||
// Освободить все объекты в пуле
|
||||
for (int i = 0; i < pool->free_count; i++) { |
||||
if (pool->free_list[i]) { |
||||
free(pool->free_list[i]); |
||||
pool->free_list[i] = NULL; |
||||
} |
||||
} |
||||
pool->free_count = 0; |
||||
} |
||||
@ -0,0 +1,33 @@
|
||||
#ifndef MEMORY_POOL_H |
||||
#define MEMORY_POOL_H |
||||
|
||||
#include <stddef.h> // для size_t |
||||
|
||||
// Размер пула для объектов одинакового размера
|
||||
#define MEMORY_POOL_SIZE 64 |
||||
|
||||
// Структура пула памяти для оптимизации аллокаций
|
||||
typedef struct memory_pool { |
||||
void* free_list[MEMORY_POOL_SIZE]; // Список свободных блоков
|
||||
int free_count; // Количество свободных блоков
|
||||
size_t object_size; // Размер объектов в пуле
|
||||
size_t allocations; // Статистика: всего аллокаций
|
||||
size_t reuse_count; // Статистика: повторное использование
|
||||
} memory_pool_t; |
||||
|
||||
// Инициализировать пул памяти
|
||||
void memory_pool_init(memory_pool_t* pool, size_t object_size); |
||||
|
||||
// Выделить объект из пула или из malloc
|
||||
void* memory_pool_alloc(memory_pool_t* pool); |
||||
|
||||
// Освободить объект в пул или в free
|
||||
void memory_pool_free(memory_pool_t* pool, void* obj); |
||||
|
||||
// Получить статистику пула
|
||||
void memory_pool_get_stats(memory_pool_t* pool, size_t* allocations, size_t* reuse_count); |
||||
|
||||
// Очистить пул памяти
|
||||
void memory_pool_destroy(memory_pool_t* pool); |
||||
|
||||
#endif // MEMORY_POOL_H
|
||||
@ -0,0 +1,76 @@
|
||||
#ifndef PACKET_BUFFER_H |
||||
#define PACKET_BUFFER_H |
||||
|
||||
#include <stdint.h> |
||||
#include <sys/socket.h> |
||||
#include <netinet/in.h> |
||||
#include <string.h> |
||||
|
||||
|
||||
// Стадии обработки пакета
|
||||
typedef enum { |
||||
PACKET_STAGE_RX_SOCKET, // Получен из сокета (зашифрован)
|
||||
PACKET_STAGE_RX_DECRYPTED, // Расшифрован (plain-text)
|
||||
PACKET_STAGE_RX_ETCP, // Обработан ETCP (собран фрагмент)
|
||||
PACKET_STAGE_RX_APP, // Передан в приложение
|
||||
PACKET_STAGE_TX_APP, // Из приложения на отправку
|
||||
PACKET_STAGE_TX_ETCP, // Обработан ETCP (фрагментирован)
|
||||
PACKET_STAGE_TX_ENCRYPTED // Зашифрован, готов к отправке
|
||||
} packet_stage_t; |
||||
|
||||
// Флаги пакета
|
||||
#define PACKET_FLAG_ENCRYPTED (1 << 0) |
||||
#define PACKET_FLAG_FRAGMENTED (1 << 1) |
||||
#define PACKET_FLAG_RETRANSMIT (1 << 2) |
||||
#define PACKET_FLAG_CONTROL (1 << 3) // Служебный пакет (RST, INIT)
|
||||
|
||||
// Размеры
|
||||
#define PACKET_METADATA_SIZE 64 |
||||
#define PACKET_DATA_SIZE 1536 |
||||
#define PACKET_TOTAL_SIZE 1600 |
||||
#define PACKET_POOL_SIZE 64 |
||||
|
||||
// Основная структура пакета
|
||||
typedef struct packet_buffer { |
||||
|
||||
// Метаданные (64 байта)
|
||||
struct { |
||||
struct ETCP_SOCKET* s; // Сокет через который получен/ планируется отправить
|
||||
struct etcp_channel* channel; // Канал ETCP (NULL до поиска)
|
||||
struct sockaddr_storage remote_addr;// Адрес удаленного endpoint
|
||||
uint64_t timestamp_us; // Время создания пакета
|
||||
uint16_t data_len; // Длина данных в буфере
|
||||
uint8_t stage; // Стадия обработки пакета
|
||||
} metadata; |
||||
struct packet_buffer* next_free; // Для пула - используется только когда пакет свободен
|
||||
|
||||
uint8_t data[0]; // Данные (variable size)
|
||||
|
||||
} packet_buffer_t; |
||||
|
||||
// Инлайн функции для удобного доступа
|
||||
|
||||
// Доступ к данным
|
||||
static inline uint8_t* packet_data(packet_buffer_t* pkt) { |
||||
return pkt->data; |
||||
} |
||||
|
||||
static inline struct sockaddr* packet_remote_addr(packet_buffer_t* pkt) { |
||||
return (struct sockaddr*)&pkt->metadata.remote_addr; |
||||
} |
||||
|
||||
static inline uint16_t packet_remote_port(const packet_buffer_t* pkt) { |
||||
if (pkt->metadata.remote_addr.ss_family == AF_INET) { |
||||
return ((struct sockaddr_in*)&pkt->metadata.remote_addr)->sin_port; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
// Инициализация
|
||||
static inline void packet_init(packet_buffer_t* pkt) { |
||||
memset(&pkt->metadata, 0, sizeof(pkt->metadata)); |
||||
pkt->metadata.timestamp_us = 0; // TODO: get current time
|
||||
pkt->metadata.stage = PACKET_STAGE_RX_SOCKET; |
||||
} |
||||
|
||||
#endif // PACKET_BUFFER_H
|
||||
@ -0,0 +1,85 @@
|
||||
#include "packet_pool.h" |
||||
#include "packet_buffer.h" |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdio.h> |
||||
|
||||
void packet_pool_init(packet_pool_t* pool) { |
||||
pool->free_count = 0; |
||||
pool->allocations = 0; |
||||
pool->reuse_count = 0; |
||||
pool->overflow_count = 0; |
||||
memset(pool->free_list, 0, sizeof(pool->free_list)); |
||||
|
||||
// Предварительно аллоцируем пакеты
|
||||
for (int i = 0; i < PACKET_POOL_SIZE / 2; i++) { |
||||
packet_buffer_t* pkt = calloc(1, PACKET_TOTAL_SIZE); |
||||
if (pkt) { |
||||
pool->free_list[pool->free_count++] = pkt; |
||||
} |
||||
} |
||||
} |
||||
|
||||
packet_buffer_t* packet_pool_get(packet_pool_t* pool) { |
||||
pool->allocations++; |
||||
|
||||
// Если есть свободные пакеты в пуле, использовать их
|
||||
if (pool->free_count > 0) { |
||||
pool->reuse_count++; |
||||
pool->free_count--; |
||||
packet_buffer_t* pkt = pool->free_list[pool->free_count]; |
||||
pool->free_list[pool->free_count] = NULL; |
||||
|
||||
// Инициализировать метаданные
|
||||
packet_init(pkt); |
||||
pkt->next_free = NULL; |
||||
|
||||
return pkt; |
||||
} |
||||
|
||||
// Иначе выделить через malloc
|
||||
pool->overflow_count++; |
||||
packet_buffer_t* pkt = calloc(1, PACKET_TOTAL_SIZE); |
||||
if (pkt) { |
||||
packet_init(pkt); |
||||
} |
||||
|
||||
return pkt; |
||||
} |
||||
|
||||
void packet_pool_put(packet_pool_t* pool, packet_buffer_t* pkt) { |
||||
if (!pkt) return; |
||||
|
||||
// Если пул не заполнен, сохранить пакет для повторного использования
|
||||
if (pool->free_count < PACKET_POOL_SIZE) { |
||||
pool->free_list[pool->free_count] = pkt; |
||||
pool->free_count++; |
||||
return; |
||||
} |
||||
|
||||
// Иначе освободить через free (переполнение)
|
||||
free(pkt); |
||||
} |
||||
|
||||
void packet_pool_get_stats(const packet_pool_t* pool,
|
||||
size_t* allocations,
|
||||
size_t* reuse_count, |
||||
size_t* overflow_count) { |
||||
if (allocations) *allocations = pool->allocations; |
||||
if (reuse_count) *reuse_count = pool->reuse_count; |
||||
if (overflow_count) *overflow_count = pool->overflow_count; |
||||
} |
||||
|
||||
void packet_pool_destroy(packet_pool_t* pool) { |
||||
// Освободить все пакеты в пуле
|
||||
for (int i = 0; i < pool->free_count; i++) { |
||||
if (pool->free_list[i]) { |
||||
free(pool->free_list[i]); |
||||
pool->free_list[i] = NULL; |
||||
} |
||||
} |
||||
pool->free_count = 0; |
||||
pool->allocations = 0; |
||||
pool->reuse_count = 0; |
||||
pool->overflow_count = 0; |
||||
} |
||||
@ -0,0 +1,35 @@
|
||||
#ifndef PACKET_POOL_H |
||||
#define PACKET_POOL_H |
||||
|
||||
#include "packet_buffer.h" |
||||
|
||||
// Размер пула пакетов
|
||||
#define PACKET_POOL_SIZE 64 |
||||
|
||||
typedef struct packet_pool { |
||||
packet_buffer_t* free_list[PACKET_POOL_SIZE]; // Список свободных пакетов
|
||||
int free_count; // Количество свободных пакетов
|
||||
size_t allocations; // Всего аллокаций
|
||||
size_t reuse_count; // Повторное использование
|
||||
size_t overflow_count; // Переполнения пула
|
||||
} packet_pool_t; |
||||
|
||||
// Инициализация пула
|
||||
void packet_pool_init(packet_pool_t* pool); |
||||
|
||||
// Получить пакет из пула
|
||||
packet_buffer_t* packet_pool_get(packet_pool_t* pool); |
||||
|
||||
// Вернуть пакет в пул
|
||||
void packet_pool_put(packet_pool_t* pool, packet_buffer_t* pkt); |
||||
|
||||
// Получить статистику
|
||||
void packet_pool_get_stats(const packet_pool_t* pool,
|
||||
size_t* allocations,
|
||||
size_t* reuse_count, |
||||
size_t* overflow_count); |
||||
|
||||
// Очистить пул и освободить всю память
|
||||
void packet_pool_destroy(packet_pool_t* pool); |
||||
|
||||
#endif // PACKET_POOL_H
|
||||
@ -1,170 +1,87 @@
|
||||
/**
|
||||
* @file routing.h |
||||
* @brief Улучшенная система маршрутизации с поддержкой пропускной способности и типов маршрутов |
||||
* @details Поддерживает статические/динамические маршруты с метриками и валидацией |
||||
*/ |
||||
#ifndef ROUTING_H |
||||
#define ROUTING_H |
||||
#include <stdint.h> |
||||
#include <stddef.h> |
||||
#include <stdbool.h> |
||||
#include "connection.h" |
||||
#include "control_socket.h" |
||||
|
||||
// Forward declarations
|
||||
struct ETCP_CONNECTIONS; |
||||
|
||||
// Типы маршрутов
|
||||
typedef enum { |
||||
ROUTE_TYPE_STATIC = 0, // Статические маршруты, загружаемые при старте
|
||||
ROUTE_TYPE_DYNAMIC = 1, // Динамические маршруты, полученные от соседей
|
||||
ROUTE_TYPE_LOCAL = 2, // Локальные подсети
|
||||
ROUTE_TYPE_LEARNED = 3 // Маршруты, полученные от других узлов
|
||||
ROUTE_TYPE_STATIC = 0, |
||||
ROUTE_TYPE_DYNAMIC = 1, |
||||
ROUTE_TYPE_LOCAL = 2, |
||||
ROUTE_TYPE_LEARNED = 3 |
||||
} route_type_t; |
||||
|
||||
// Флаги маршрута
|
||||
typedef enum { |
||||
ROUTE_FLAG_ACTIVE = (1 << 0), // Маршрут активен (лучший путь)
|
||||
ROUTE_FLAG_VALIDATED = (1 << 1), // Маршрут прошёл валидацию (по разрешенным подсетям в конфиге)
|
||||
ROUTE_FLAG_ADVERTISED = (1 << 2), // Маршрут анонсируется другим узлам
|
||||
ROUTE_FLAG_LEARNED = (1 << 3) // Маршрут получен от соседа
|
||||
ROUTE_FLAG_ACTIVE = (1 << 0), |
||||
ROUTE_FLAG_VALIDATED = (1 << 1), |
||||
ROUTE_FLAG_ADVERTISED = (1 << 2), |
||||
ROUTE_FLAG_LEARNED = (1 << 3) |
||||
} route_flags_t; |
||||
|
||||
// Расширенные метрики маршрута
|
||||
typedef struct { |
||||
uint32_t bandwidth_kbps; // Доступная (минимальная по всем узлам) пропускная способность до узла по этому маршруту, кбит/с
|
||||
uint16_t packet_loss_rate; // Процент потерь пакетов (0–10000 = 0.00–100.00%)
|
||||
uint16_t latency_ms; // Задержка в миллисекундах (суммарная до узла, не вулючает задержку до next hop)
|
||||
uint8_t hop_count; // Количество хопов до цели (не включая next hop)
|
||||
uint64_t last_updated; // Время последнего обновления (в timebase 0.1ms)
|
||||
uint32_t bandwidth_kbps; |
||||
uint16_t packet_loss_rate; |
||||
uint16_t latency_ms; |
||||
uint8_t hop_count; |
||||
uint64_t last_updated; |
||||
} route_metrics_t; |
||||
|
||||
// Расширенная запись маршрута
|
||||
typedef struct { |
||||
uint32_t network; // Адрес сети (в сетевом порядке байтов)
|
||||
uint8_t prefix_length; // Длина префикса (0–32)
|
||||
uint32_t next_hop_ip; // IP-адрес следующего хопа (узла который анонсировал маршрут)
|
||||
conn_handle_t* next_hop; // Указатель на соединение со следующим хопом
|
||||
|
||||
route_type_t type; // Тип маршрута
|
||||
uint8_t flags; // Флаги маршрута
|
||||
|
||||
route_metrics_t metrics; // Метрики маршрута
|
||||
uint64_t created_time; // Время создания записи
|
||||
uint64_t last_update; // Время последнего обновления
|
||||
uint64_t last_used; // Время последнего использования
|
||||
|
||||
uint32_t network; |
||||
uint8_t prefix_length; |
||||
uint32_t next_hop_ip; |
||||
struct ETCP_CONNECTIONS* next_hop; |
||||
route_type_t type; |
||||
uint8_t flags; |
||||
route_metrics_t metrics; |
||||
uint64_t created_time; |
||||
uint64_t last_update; |
||||
uint64_t last_used; |
||||
} route_entry_t; |
||||
|
||||
|
||||
|
||||
// Расширенная таблица маршрутизации (элементы сортированы по IP), может содержать дублирующие маршруты по альтернативным каналам.
|
||||
// Таблица маршрутизации
|
||||
typedef struct { |
||||
route_entry_t *entries; // Массив записей маршрутов
|
||||
size_t count; // Количество активных записей
|
||||
size_t capacity; // Выделенная ёмкость массива (расширение x2 при нехватке места)
|
||||
|
||||
// Диапазоны для валидации подсетей
|
||||
uint32_t *dynamic_subnets; // Разрешённые диапазоны динамических маршрутов (анонс не принимается если вне этой таблицы)
|
||||
size_t dynamic_subnet_count; |
||||
|
||||
// Локальные подсети, которые будут анонсироваться
|
||||
uint32_t *local_subnets; |
||||
size_t local_subnet_count; |
||||
routing_stats_t stats; // Statistics counters
|
||||
route_entry_t *entries; |
||||
size_t count; |
||||
size_t capacity; |
||||
uint32_t *dynamic_subnets; |
||||
size_t dynamic_subnet_count; |
||||
uint32_t *local_subnets; |
||||
size_t local_subnet_count; |
||||
struct { |
||||
uint64_t total_routes; |
||||
uint64_t static_routes; |
||||
uint64_t dynamic_routes; |
||||
uint64_t local_routes; |
||||
uint64_t learned_routes; |
||||
uint64_t routes_added; |
||||
uint64_t routes_deleted; |
||||
uint64_t lookup_count; |
||||
uint64_t hit_count; |
||||
uint64_t routes_lookup_hits; |
||||
uint64_t routes_lookup_misses; |
||||
uint64_t validation_failures; |
||||
} stats; |
||||
} routing_table_t; |
||||
|
||||
/**
|
||||
* @brief Создаёт улучшенную таблицу маршрутизации |
||||
* @return указатель на новую таблицу или NULL при ошибке |
||||
*/ |
||||
routing_table_t *routing_table_create(void); |
||||
|
||||
/**
|
||||
* @brief Уничтожает таблицу маршрутизации |
||||
*/ |
||||
routing_table_t* routing_table_create(void); |
||||
void routing_table_destroy(routing_table_t *table); |
||||
|
||||
/**
|
||||
* @brief Получить статистику таблицы маршрутизации |
||||
* @param table таблица маршрутизации |
||||
* @param stats указатель на структуру для заполнения статистики |
||||
*/ |
||||
void routing_get_stats(const routing_table_t *table, routing_stats_t *stats); |
||||
|
||||
/**
|
||||
* @brief Добавляет маршрут в таблицу |
||||
* |
||||
* @param table таблица маршрутизации |
||||
* @param entry структура маршрута для вставки |
||||
* @return true при успехе, false при ошибке |
||||
*/ |
||||
bool routing_table_insert(routing_table_t *table, const route_entry_t *entry); |
||||
|
||||
/**
|
||||
* @brief Удаляет маршрут из таблицы |
||||
* |
||||
* @param table таблица маршрутизации |
||||
* @param network адрес сети |
||||
* @param prefix_length длина префикса |
||||
* @param source_node_id идентификатор узла-источника |
||||
* @return true — маршрут удалён, false — не найден |
||||
*/ |
||||
bool routing_table_delete(routing_table_t *table, uint32_t network, uint8_t prefix_length, uint32_t source_node_id); |
||||
|
||||
/**
|
||||
* @brief Ищет лучший маршрут до указанного адреса |
||||
* |
||||
* @param table таблица маршрутизации |
||||
* @param dest_ip целевой IP-адрес |
||||
* @param best_route [out] сюда будет записан лучший маршрут |
||||
* @return true — маршрут найден, false — нет подходящего маршрута |
||||
*/ |
||||
bool routing_table_lookup(routing_table_t *table, uint32_t dest_ip, route_entry_t *best_route); |
||||
|
||||
/**
|
||||
* @brief Проверяет маршрут на соответствие разрешённым подсетям |
||||
* |
||||
* @param table таблица с диапазонами валидации |
||||
* @param network адрес сети |
||||
* @param prefix_length длина префикса |
||||
* @param route_type ожидаемый тип маршрута |
||||
* @return true — маршрут разрешён, false — запрещён |
||||
*/ |
||||
bool routing_validate_route(routing_table_t *table, uint32_t network, uint8_t prefix_length, route_type_t route_type); |
||||
|
||||
/**
|
||||
* @brief Добавляет диапазон подсети для динамической валидации |
||||
*/ |
||||
bool routing_add_dynamic_subnet(routing_table_t *table, uint32_t network, uint8_t prefix_length); |
||||
|
||||
/**
|
||||
* @brief Добавляет локальную подсеть, которая будет анонсироваться соседям |
||||
*/ |
||||
bool routing_add_local_subnet(routing_table_t *table, uint32_t network, uint8_t prefix_length); |
||||
|
||||
/**
|
||||
* @brief Возвращает все маршруты для указанной сети/префикса |
||||
* |
||||
* @param routes [out] массив указателей (выделяется внутри, нужно освободить) |
||||
*/ |
||||
bool routing_get_all_routes(const routing_table_t *table, uint32_t network, uint8_t prefix_length, |
||||
route_entry_t **routes, size_t *count); |
||||
|
||||
/**
|
||||
* @brief Выводит содержимое таблицы маршрутизации (для отладки) |
||||
*/ |
||||
bool routing_get_all_routes(const routing_table_t *table, uint32_t network, uint8_t prefix_length, route_entry_t **routes, size_t *count); |
||||
void routing_table_print(const routing_table_t *table); |
||||
const char* route_type_to_string(route_type_t type); |
||||
char* ip_to_string(uint32_t ip, char *buffer); |
||||
|
||||
/**
|
||||
* @brief Преобразует тип маршрута в строку |
||||
* @return текстовое представление типа |
||||
*/ |
||||
const char *route_type_to_string(route_type_t type); |
||||
|
||||
/**
|
||||
* @brief Преобразует IP-адрес в строку (для отладки) |
||||
* |
||||
* @param ip адрес в сетевом порядке байтов |
||||
* @param buffer буфер минимум 16 байт |
||||
* @return указатель на заполненный буфер |
||||
*/ |
||||
char *ip_to_string(uint32_t ip, char *buffer); |
||||
#endif // ROUTING_H
|
||||
@ -1,6 +0,0 @@
|
||||
// settings.c
|
||||
#include "settings.h" |
||||
|
||||
settings_t settings = { |
||||
.max_fragment_size = 1400 |
||||
}; |
||||
@ -1,11 +0,0 @@
|
||||
// settings.h
|
||||
#ifndef SETTINGS_H |
||||
#define SETTINGS_H |
||||
|
||||
typedef struct { |
||||
int max_fragment_size; |
||||
} settings_t; |
||||
|
||||
extern settings_t settings; |
||||
|
||||
#endif // SETTINGS_H
|
||||
@ -0,0 +1,52 @@
|
||||
#include "utun_instance.h" |
||||
#include "routing.h" |
||||
#include "etcp_connections.h" |
||||
#include "config_parser.h" |
||||
#include "../u_async/u_async.h" |
||||
|
||||
conn_handle_t* route_get_next_hop(routing_table_t* rt, uint32_t dest_ip) { |
||||
route_entry_t route = {0}; |
||||
if (!rt || !routing_table_lookup(rt, dest_ip, &route)) return NULL; |
||||
return (conn_handle_t*)route.next_hop; |
||||
} |
||||
|
||||
static int add_default_route(utun_instance_t *instance) { |
||||
if (!instance || !instance->routing_table || instance->connection_count == 0) return -1; |
||||
|
||||
route_entry_t default_route = {0}; |
||||
default_route.network = 0; // 0.0.0.0
|
||||
default_route.prefix_length = 0; |
||||
default_route.next_hop_ip = 0; |
||||
default_route.next_hop = (struct ETCP_CONNECTIONS*)instance->connections[0]->conns; |
||||
default_route.type = ROUTE_TYPE_STATIC; |
||||
default_route.flags = ROUTE_FLAG_ACTIVE; |
||||
default_route.created_time = 0; |
||||
|
||||
return routing_table_insert((routing_table_t*)instance->routing_table, &default_route) ? 0 : -1; |
||||
} |
||||
|
||||
void conn_destroy(conn_handle_t* conn); |
||||
int conn_send(conn_handle_t* conn, const uint8_t* data, size_t len); |
||||
uint32_t get_dest_ip(const uint8_t *packet, size_t len); |
||||
int parse_subnet(const char* subnet_str, uint32_t* network, uint8_t* prefix_len); |
||||
|
||||
conn_handle_t* route_find_connection(utun_instance_t* instance, uint32_t dest_ip) { |
||||
if (!instance || !instance->routing_table) return NULL; |
||||
|
||||
route_entry_t route = {0}; |
||||
if (!routing_table_lookup((routing_table_t*)instance->routing_table, dest_ip, &route)) { |
||||
return NULL; |
||||
} |
||||
|
||||
struct ETCP_CONNECTIONS* conns = route.next_hop; |
||||
if (!conns) return NULL; |
||||
|
||||
// Find matching connection handle
|
||||
for (int i = 0; i < instance->connection_count; i++) { |
||||
if (instance->connections[i] && instance->connections[i]->conns == conns) { |
||||
return instance->connections[i]; |
||||
} |
||||
} |
||||
|
||||
return NULL; |
||||
} |
||||
@ -0,0 +1,184 @@
|
||||
// utun_instance.c - Root instance implementation
|
||||
#include "utun_instance.h" |
||||
#include "config_parser.h" |
||||
#include "tun_if.h" |
||||
#include "routing.h" |
||||
#include "etcp_connections.h" |
||||
#include "../u_async/u_async.h" |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <errno.h> |
||||
#include <unistd.h> |
||||
|
||||
// Global instance for signal handlers
|
||||
static utun_instance_t *g_instance = NULL; |
||||
|
||||
// Create and initialize root instance
|
||||
utun_instance_t* utun_instance_create(const char *config_file, const char *log_file) { |
||||
utun_instance_t *instance = calloc(1, sizeof(utun_instance_t)); |
||||
if (!instance) return NULL; |
||||
|
||||
// Initialize basic fields
|
||||
instance->running = 0; |
||||
instance->log_fp = NULL; |
||||
|
||||
// Load configuration
|
||||
instance->config = parse_config(config_file); |
||||
if (!instance->config) { |
||||
fprintf(stderr, "Failed to load config from %s\n", config_file); |
||||
free(instance); |
||||
return NULL; |
||||
} |
||||
|
||||
// Open log file
|
||||
if (log_file) { |
||||
instance->log_fp = fopen(log_file, "a"); |
||||
if (!instance->log_fp) { |
||||
fprintf(stderr, "Failed to open log file %s: %s\n", log_file, strerror(errno)); |
||||
} |
||||
} |
||||
|
||||
// Create uasync instance
|
||||
instance->ua = uasync_create(); |
||||
if (!instance->ua) { |
||||
fprintf(stderr, "Failed to create uasync instance\n"); |
||||
utun_instance_destroy(instance); |
||||
return NULL; |
||||
} |
||||
|
||||
// uasync has built-in wakeup mechanism, no need for manual pipe
|
||||
|
||||
// Set global instance
|
||||
utun_instance_set(instance); |
||||
|
||||
return instance; |
||||
} |
||||
|
||||
// Destroy instance and cleanup resources
|
||||
void utun_instance_destroy(utun_instance_t *instance) { |
||||
if (!instance) return; |
||||
|
||||
// Stop running
|
||||
instance->running = 0; |
||||
|
||||
// Cleanup components
|
||||
if (instance->ua) { |
||||
uasync_destroy(instance->ua); |
||||
} |
||||
|
||||
if (instance->routing_table) { |
||||
routing_table_destroy(instance->routing_table); |
||||
} |
||||
|
||||
// Cleanup connections
|
||||
if (instance->connections) { |
||||
for (int i = 0; i < instance->connection_count; i++) { |
||||
if (instance->connections[i]) { |
||||
conn_destroy(instance->connections[i]); |
||||
} |
||||
} |
||||
free(instance->connections); |
||||
} |
||||
|
||||
// Cleanup TUN
|
||||
if (instance->tun.fd >= 0) { |
||||
tun_close(&instance->tun); |
||||
} |
||||
|
||||
// Cleanup config
|
||||
if (instance->config) { |
||||
free_config(instance->config); |
||||
} |
||||
|
||||
// Close log file
|
||||
if (instance->log_fp) { |
||||
fclose(instance->log_fp); |
||||
} |
||||
|
||||
// uasync cleanup will handle its built-in wakeup pipe
|
||||
|
||||
// Clear global instance
|
||||
if (g_instance == instance) { |
||||
g_instance = NULL; |
||||
} |
||||
|
||||
free(instance); |
||||
} |
||||
|
||||
// Initialize all components
|
||||
int utun_instance_init(utun_instance_t *instance) { |
||||
if (!instance || !instance->config) return -1; |
||||
|
||||
// Initialize TUN device
|
||||
if (tun_create(&instance->tun) < 0) { |
||||
fprintf(stderr, "Failed to create TUN device\n"); |
||||
return -1; |
||||
} |
||||
|
||||
// Configure TUN device
|
||||
if (tun_set_ip(instance->tun.ifname, instance->config->global.tun_ip) < 0) { |
||||
fprintf(stderr, "Failed to set TUN IP\n"); |
||||
tun_close(&instance->tun); |
||||
return -1; |
||||
} |
||||
|
||||
if (instance->config->global.mtu > 0) { |
||||
if (tun_set_mtu(instance->tun.ifname, instance->config->global.mtu) < 0) { |
||||
fprintf(stderr, "Failed to set TUN MTU\n"); |
||||
tun_close(&instance->tun); |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
if (tun_set_up(instance->tun.ifname) < 0) { |
||||
fprintf(stderr, "Failed to bring up TUN interface\n"); |
||||
tun_close(&instance->tun); |
||||
return -1; |
||||
} |
||||
|
||||
// Create routing table
|
||||
instance->routing_table = routing_table_create(); |
||||
if (!instance->routing_table) { |
||||
fprintf(stderr, "Failed to create routing table\n"); |
||||
return -1; |
||||
} |
||||
|
||||
// Subnets configuration will be added later from connection configs
|
||||
// For now just return success
|
||||
return 0; |
||||
} |
||||
|
||||
// Start main event loop
|
||||
int utun_instance_run(utun_instance_t *instance) { |
||||
if (!instance || !instance->ua) return -1; |
||||
|
||||
instance->running = 1; |
||||
|
||||
// Run main loop
|
||||
while (instance->running) { |
||||
uasync_poll(instance->ua, 1000); // 100ms timeout
|
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// Stop instance
|
||||
void utun_instance_stop(utun_instance_t *instance) { |
||||
if (!instance) return; |
||||
instance->running = 0; |
||||
|
||||
// Wakeup main loop using built-in uasync wakeup
|
||||
if (instance->ua) { |
||||
uasync_wakeup(instance->ua); |
||||
} |
||||
} |
||||
|
||||
// Global instance access
|
||||
utun_instance_t* utun_instance_get(void) { |
||||
return g_instance; |
||||
} |
||||
|
||||
void utun_instance_set(utun_instance_t *instance) { |
||||
g_instance = instance; |
||||
} |
||||
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* @file utun_instance.h |
||||
* @brief Main application state structure |
||||
* @details Contains all module instances and configuration |
||||
*/ |
||||
|
||||
#ifndef UTUN_INSTANCE_H |
||||
#define UTUN_INSTANCE_H |
||||
|
||||
#include <stdio.h> |
||||
#include "tun_if.h" |
||||
#include "etcp_sockets.h" |
||||
#include "config_parser.h" |
||||
#include "u_async/u_async.h" |
||||
|
||||
// Root instance structure - single point of access to all components
|
||||
typedef struct utun_instance { |
||||
// Configuration (moved from utun_state)
|
||||
utun_config_t *config; |
||||
|
||||
// First listening socket for server connections
|
||||
struct ETCP_SOCKET* first_listen_socket; |
||||
|
||||
// Main components (from utun_state)
|
||||
tun_config_t tun; |
||||
conn_handle_t **connections; // Array of connection handles
|
||||
int connection_count; |
||||
void *routing_table; // routing_table_t* - void* to avoid circular dependency
|
||||
int running; |
||||
uasync_t *ua; |
||||
FILE *log_fp; |
||||
|
||||
} utun_instance_t; |
||||
|
||||
// Functions for root instance management
|
||||
utun_instance_t* utun_instance_create(const char *config_file, const char *log_file); |
||||
void utun_instance_destroy(utun_instance_t *instance); |
||||
int utun_instance_init(utun_instance_t *instance); |
||||
int utun_instance_run(utun_instance_t *instance); |
||||
void utun_instance_stop(utun_instance_t *instance); |
||||
|
||||
// Global instance for signal handlers
|
||||
utun_instance_t* utun_instance_get(void); |
||||
void utun_instance_set(utun_instance_t *instance); |
||||
|
||||
#endif /* UTUN_INSTANCE_H */ |
||||
@ -0,0 +1,23 @@
|
||||
// Add default route via first connection
|
||||
static int add_default_route(utun_instance_t *instance) { |
||||
if (!instance || !instance->routing_table || instance->connection_count == 0) return -1; |
||||
|
||||
route_entry_t default_route = {0}; |
||||
default_route.network = 0; // 0.0.0.0
|
||||
default_route.prefix_length = 0; |
||||
default_route.next_hop_ip = 0; // Unknown
|
||||
default_route.next_hop = (struct ETCP_CONNECTIONS*)instance->connections[0]->conns; |
||||
default_route.type = ROUTE_TYPE_STATIC; |
||||
default_route.flags = ROUTE_FLAG_ACTIVE; |
||||
default_route.created_time = 0; // Not used
|
||||
default_route.last_update = 0; |
||||
default_route.last_used = 0; |
||||
|
||||
if (!routing_table_insert(instance->routing_table, &default_route)) { |
||||
fprintf(stderr, "Failed to add default route\n"); |
||||
return -1; |
||||
} |
||||
|
||||
printf("Added default route via first connection\n"); |
||||
return 0; |
||||
} |
||||
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* @file utun_state.h |
||||
* @brief Main application state structure |
||||
* @details Contains all module instances and configuration |
||||
*/ |
||||
|
||||
#ifndef UTUN_STATE_H |
||||
#define UTUN_STATE_H |
||||
|
||||
#include <stdio.h> |
||||
#include "tun_if.h" |
||||
#include "connection.h" |
||||
#include "routing.h" |
||||
#include "control_socket.h" |
||||
#include "config_parser.h" |
||||
|
||||
// Main application state
|
||||
typedef struct utun_state { |
||||
tun_config_t tun; |
||||
conn_handle_t **connections; |
||||
int connection_count; |
||||
routing_table_t *routing_table; |
||||
control_socket_t *control_socket; |
||||
int running; |
||||
uasync_t *ua; |
||||
utun_config_t *config; |
||||
FILE *log_fp; |
||||
} utun_state_t; |
||||
|
||||
#endif /* UTUN_STATE_H */ |
||||
@ -1,31 +0,0 @@
|
||||
# utun configuration file example |
||||
# Global settings |
||||
[global] |
||||
# Generate keys using: openssl ecparam -name prime256v1 -genkey -noout | openssl ec -text -noout |
||||
# Private key (32 bytes in hex) |
||||
my_private_key=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef |
||||
# Public key (64 bytes in hex) |
||||
my_public_key=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef |
||||
# Global option (optional) |
||||
option=value |
||||
|
||||
# Server connection (listening for incoming connections) |
||||
[connection: server1] |
||||
mode=server |
||||
addr=0.0.0.0:51820 # Listen on all interfaces, port 51820 |
||||
peer_public_key=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 |
||||
so_mark=0 # Socket mark (0 = disabled) |
||||
netif=eth0 # Bind to specific network interface |
||||
tun=tun0 # TUN interface name |
||||
tun_ip=10.0.0.1/24 # TUN interface IP address |
||||
|
||||
# Client connection (initiate connection to server) |
||||
[connection: client1] |
||||
mode=client |
||||
from_addr=0.0.0.0:51821 # Local address to bind (0.0.0.0 = any) |
||||
to_addr=192.168.1.100:51820 # Remote server address |
||||
peer_public_key=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 |
||||
so_mark=0 |
||||
netif=eth0 |
||||
tun=tun1 |
||||
tun_ip=10.0.0.2/24 |
||||
@ -1,21 +1,35 @@
|
||||
[global] |
||||
my_private_key=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef |
||||
my_public_key=abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789 |
||||
my_node_id=babababa1123 # 64bit ID |
||||
option=value1,value2 |
||||
my_private_key=1F1C8A.... (HEX) |
||||
my_public_key=1F1C8A.... (HEX) |
||||
tun_ip=1.2.3.4 |
||||
tun_ip=10.0.0.1/24 |
||||
mtu=1500 # MTU for all connections (0 = use default 1500) |
||||
control_ip=127.0.0.1 |
||||
control_port=12345 |
||||
net_debug=0 |
||||
|
||||
# remote connections |
||||
[connection: srv_conn1] |
||||
mode=server |
||||
addr=127.0.0.1:1234 |
||||
peer_public_key=1F1C8A.... (HEX) |
||||
# мои адреса и каналы |
||||
[server: wired1_fast] |
||||
addr=192.168.0.10:1234 |
||||
so_mark=100 |
||||
netif=eth0 |
||||
type=nat # public / nat / private |
||||
|
||||
[connection: conn2_blablabla] (это имя подключения и опции надо запомнить в структуре подключения) |
||||
mode=client |
||||
from_addr=127.0.0.1:1234 |
||||
to_addr=127.0.0.1:1234 |
||||
so_mark=100 (надо установить для этого подключения) |
||||
netif=lo0 (биндим к конкретному интерфейсу если это можно) |
||||
peer_public_key=1F1C8A.... (HEX) |
||||
[server: wireless_bkp] |
||||
addr=192.168.0.10:1234 |
||||
so_mark=100 |
||||
netif=eth0 |
||||
type=public # public / nat / private |
||||
|
||||
(подключений может быть много, надо все распарсить и открыть. имя должно загружаться в структуру connection) |
||||
[client: client_test1] |
||||
|
||||
# линки |
||||
link=wired1_fast:192.168.0.20:1234 |
||||
link=wired1_fast:1.2.3.4:1234 |
||||
link=wireless_bkp:192.168.0.20:1234 |
||||
link=wireless_bkp:1.2.3.4:1234 |
||||
|
||||
#keepalive после ресета etcp передаём серверу |
||||
keepalive=1 |
||||
peer_public_key=deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef |
||||
|
||||
@ -1,22 +0,0 @@
|
||||
[global] |
||||
my_private_key=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef |
||||
my_public_key=abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789 |
||||
option=value1,value2 |
||||
tun_ip=10.0.0.1/24 |
||||
control_ip=127.0.0.1 |
||||
control_port=12345 |
||||
net_debug=0 |
||||
|
||||
[server: wired1_fast] |
||||
addr=192.168.0.10:1234 |
||||
so_mark=100 |
||||
netif=eth0 |
||||
|
||||
[client: wireless] |
||||
from=wired1_fast |
||||
to_addr=192.168.0.20:1234 |
||||
|
||||
[connection] |
||||
link=wired1_fast:wireless |
||||
keepalive=1 |
||||
peer_public_key=deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef |
||||
Loading…
Reference in new issue