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
|
||||||
@ -1,250 +1,59 @@ |
|||||||
// etcp.h - Расширенный протокол управления передачей (Extended Transmission Control Protocol)
|
// etcp.h - Расширенный протокол управления передачей (ETCP Protocol)
|
||||||
#ifndef ETCP_H |
#ifndef ETCP_H |
||||||
#define ETCP_H |
#define ETCP_H |
||||||
|
|
||||||
#include <stdint.h> |
#include "etcp_sockets.h" |
||||||
#include <stddef.h> |
#include "etcp_connections.h" |
||||||
|
#include "packet_pool.h" |
||||||
#include "ll_queue.h" |
#include "ll_queue.h" |
||||||
#include "u_async.h" |
#include <stdint.h> |
||||||
|
|
||||||
// Отладочное логирование
|
|
||||||
#ifdef ETCP_DEBUG |
|
||||||
#include <stdio.h> |
|
||||||
#define ETCP_LOG(fmt, ...) printf("[ETCP] " fmt, ##__VA_ARGS__) |
|
||||||
#ifdef ETCP_DEBUG_EXT |
|
||||||
#define ETCP_DEBUG_LOG(fmt, ...) printf("[ETCP_DEBUG] " fmt, ##__VA_ARGS__) |
|
||||||
#else |
|
||||||
#define ETCP_DEBUG_LOG(fmt, ...) ((void)0) |
|
||||||
#endif |
|
||||||
#else |
|
||||||
#define ETCP_LOG(fmt, ...) ((void)0) |
|
||||||
#define ETCP_DEBUG_LOG(fmt, ...) ((void)0) |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef __cplusplus |
#ifdef __cplusplus |
||||||
extern "C" { |
extern "C" { |
||||||
#endif |
#endif |
||||||
|
|
||||||
// Предварительные объявления
|
// Forward declarations
|
||||||
typedef struct epkt epkt_t; |
typedef struct utun_instance utun_instance_t; |
||||||
|
typedef struct ETCP_CONN etcp_t; |
||||||
|
|
||||||
// Тип обратного вызова для отправки пакетов через UDP
|
struct ETCP_CONN { |
||||||
typedef void (*etcp_tx_callback_t)(epkt_t* epkt, uint8_t* pkt, uint16_t len, void* arg); |
// Node ID (64-bit, генерируется при первом запуске)
|
||||||
|
uint64_t node_id; |
||||||
|
int mtu; |
||||||
|
|
||||||
// Тип обратного вызова для уведомления о reset
|
utun_instance_t *instance; |
||||||
typedef void (*etcp_reset_callback_t)(epkt_t* epkt, void* arg); |
|
||||||
|
|
||||||
// Основная структура ETCP
|
// Каналы (соединения) - linked list
|
||||||
struct epkt { |
struct ETCP_LINK* channels; |
||||||
// Очереди
|
|
||||||
ll_queue_t* tx_queue; // Очередь данных для отправки
|
|
||||||
ll_queue_t* output_queue; // Выходная очередь (собранные данные)
|
|
||||||
|
|
||||||
// Список полученных пакетов (отсортированный связанный список)
|
|
||||||
struct rx_packet* rx_list; |
|
||||||
|
|
||||||
// Отправленные пакеты (для повторной передачи)
|
|
||||||
struct sent_packet* sent_list; |
|
||||||
|
|
||||||
// Метрики
|
|
||||||
uint16_t rtt_last; // Последнее RTT (в единицах времени 0.1 мкс)
|
|
||||||
uint16_t rtt_avg_10; // Среднее RTT за последние 10 пакетов
|
|
||||||
uint16_t rtt_avg_100; // Среднее RTT за последние 100 пакетов
|
|
||||||
uint16_t jitter; // Джиттер (усредненный)
|
|
||||||
uint16_t bandwidth; // Текущая пропускная способность (байты за единицу времени)
|
|
||||||
uint32_t bytes_sent_total; // Общее количество отправленных байт
|
|
||||||
uint16_t last_sent_timestamp; // Временная метка последнего отправленного пакета
|
|
||||||
uint32_t bytes_allowed; // Рассчитанное количество разрешенных к отправке байт
|
|
||||||
|
|
||||||
// Статистика
|
|
||||||
uint32_t retransmissions_count; // Количество ретрансмиссий
|
|
||||||
uint32_t ack_packets_count; // Количество отправленных пакетов подтверждения
|
|
||||||
uint32_t control_packets_count; // Количество отправленных управляющих пакетов (ACK + запросы ретрансмиссии)
|
|
||||||
uint32_t total_packets_sent; // Общее количество отправленных пакетов (включая ретрансмиссии)
|
|
||||||
uint32_t unique_packets_sent; // Количество уникальных отправленных пакетов (без ретрансмиссий)
|
|
||||||
uint32_t bytes_received_total; // Общее количество полученных байт
|
|
||||||
|
|
||||||
// Состояние
|
|
||||||
uint16_t next_tx_id; // Следующий ID для передачи
|
|
||||||
uint16_t last_sent_id; // Последний отправленный ID (для ретрансмиссии самого нового пакета)
|
|
||||||
uint16_t last_rx_id; // Последний полученный ID (для подтверждения)
|
|
||||||
uint16_t last_delivered_id; // Последний ID, переданный в output_queue
|
|
||||||
|
|
||||||
// Таймеры
|
|
||||||
void* next_tx_timer; // Таймер для следующей передачи
|
|
||||||
void* retransmit_timer; // Таймер для повторных передач
|
|
||||||
uasync_t* ua; // Экземпляр uasync для таймеров
|
|
||||||
|
|
||||||
// Обратный вызов
|
|
||||||
etcp_tx_callback_t tx_callback; |
|
||||||
void* tx_callback_arg; |
|
||||||
|
|
||||||
// История RTT для усреднения
|
|
||||||
uint16_t rtt_history[100]; |
|
||||||
uint8_t rtt_history_idx; |
|
||||||
uint8_t rtt_history_count; |
|
||||||
|
|
||||||
// Ожидающие подтверждения
|
|
||||||
uint16_t pending_ack_ids[32]; |
|
||||||
uint16_t pending_ack_timestamps[32]; |
|
||||||
uint8_t pending_ack_count; |
|
||||||
|
|
||||||
// Ожидающие запросы на повторную передачу
|
// Пул пакетов (общий для всего ETCP instance)
|
||||||
uint16_t pending_retransmit_ids[32]; |
packet_pool_t packet_pool; |
||||||
uint8_t pending_retransmit_count; |
|
||||||
|
|
||||||
// Управление окном
|
uint8_t state; // 0 - just created, 1 - initialized, 2 - connection established
|
||||||
uint32_t unacked_bytes; // Количество байт, отправленных но еще не подтвержденных
|
|
||||||
uint32_t window_size; // Текущий размер окна в байтах (рассчитывается)
|
|
||||||
uint16_t last_acked_id; // Последний подтвержденный ID пакета
|
|
||||||
uint16_t last_rx_ack_id; // Последний полученный ID подтверждения от получателя
|
|
||||||
uint16_t retrans_timer_period; // Текущий период таймера повторной передачи (в единицах времени)
|
|
||||||
uint16_t next_retrans_time; // Время следующей проверки повторной передачи
|
|
||||||
uint8_t window_blocked; // Флаг: передача заблокирована из-за ограничения окна
|
|
||||||
|
|
||||||
// Forward progress tracking
|
|
||||||
uint16_t oldest_missing_id; // Oldest missing packet ID
|
|
||||||
uint16_t missing_since_time; // Time when oldest missing packet was first detected
|
|
||||||
|
|
||||||
// Reset state
|
|
||||||
uint8_t reset_pending; // Reset packet sent, waiting for ACK
|
|
||||||
uint8_t reset_ack_received; // Reset ACK received
|
|
||||||
void* reset_timer; // Timer for reset retransmission
|
|
||||||
uint16_t reset_retry_count; // Number of reset retries
|
|
||||||
|
|
||||||
// Reset callback and initialization state
|
|
||||||
uint8_t initialized; // 1 = connection initialized after first reset handshake
|
|
||||||
etcp_reset_callback_t reset_callback; // Callback for reset notification
|
|
||||||
void* reset_callback_arg; // Callback argument
|
|
||||||
}; |
|
||||||
|
|
||||||
// Функции API
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Инициализировать новый экземпляр ETCP |
|
||||||
* @param ua Экземпляр uasync для таймеров (обязательный параметр) |
|
||||||
* @return Указатель на новый экземпляр или NULL в случае ошибки |
|
||||||
*/ |
|
||||||
epkt_t* etcp_init(uasync_t* ua); |
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Освободить экземпляр ETCP и все связанные ресурсы |
|
||||||
* @param epkt Экземпляр для освобождения |
|
||||||
*/ |
|
||||||
void etcp_free(epkt_t* epkt); |
|
||||||
|
|
||||||
/**
|
// Криптография и состояние (TODO: определить sc_context_t)
|
||||||
* @brief Установить обратный вызов для отправки пакетов через UDP |
// void* crypto_ctx;
|
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* @param cb Функция обратного вызова |
|
||||||
* @param arg Пользовательский аргумент, передаваемый в обратный вызов |
|
||||||
*/ |
|
||||||
void etcp_set_callback(epkt_t* epkt, etcp_tx_callback_t cb, void* arg); |
|
||||||
|
|
||||||
/**
|
ll_queue_t* input_queue;// отправитель -> input_queue -> etcp -> etcp_channel -> отправка (etcp_sockets)
|
||||||
* @brief Установить обратный вызов для уведомления о reset |
ll_queue_t* output_queue;// etcp_sockets -> etcp_channel -> etcp -> output_queue -> получатель
|
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* @param cb Функция обратного вызова |
|
||||||
* @param arg Пользовательский аргумент, передаваемый в обратный вызов |
|
||||||
*/ |
|
||||||
void etcp_set_reset_callback(epkt_t* epkt, etcp_reset_callback_t cb, void* arg); |
|
||||||
|
|
||||||
/**
|
}; |
||||||
* @brief Обработать полученный UDP пакет |
|
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* @param pkt Данные пакета |
|
||||||
* @param len Длина пакета |
|
||||||
* @return 0 при успехе, -1 при ошибке |
|
||||||
*/ |
|
||||||
int etcp_rx_input(epkt_t* epkt, uint8_t* pkt, uint16_t len); |
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Получить общее количество пакетов, ожидающих в очередях передачи |
|
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* @return Количество пакетов |
|
||||||
*/ |
|
||||||
int etcp_tx_queue_size(epkt_t* epkt); |
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Поместить данные в очередь передачи |
|
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* @param data Данные для отправки |
|
||||||
* @param len Длина данных |
|
||||||
* @return 0 при успехе, -1 при ошибке |
|
||||||
*/ |
|
||||||
int etcp_tx_put(epkt_t* epkt, uint8_t* data, uint16_t len); |
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Получить выходную очередь для чтения полученных данных |
|
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* @return Указатель на выходную очередь (ll_queue_t*) |
|
||||||
*/ |
|
||||||
ll_queue_t* etcp_get_output_queue(epkt_t* epkt); |
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Установить ограничение пропускной способности |
|
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* @param bandwidth Байты за единицу времени (0.1 мкс) |
|
||||||
*/ |
|
||||||
void etcp_set_bandwidth(epkt_t* epkt, uint16_t bandwidth); |
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Обновить размер окна на основе текущего RTT и пропускной способности |
|
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* Размер окна = RTT * пропускная способность * 2 (байт в пути) |
|
||||||
*/ |
|
||||||
void etcp_update_window(epkt_t* epkt); |
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Получить текущее RTT |
|
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* @return RTT в единицах времени |
|
||||||
*/ |
|
||||||
uint16_t etcp_get_rtt(epkt_t* epkt); |
|
||||||
|
|
||||||
/**
|
// Создание пустого ETCP instance (после надо добавить ключи, каналы)
|
||||||
* @brief Получить текущий джиттер |
struct ETCP_CONN* etcp_create(utun_instance_t *instance); |
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* @return Джиттер в единицах времени |
|
||||||
*/ |
|
||||||
uint16_t etcp_get_jitter(epkt_t* epkt); |
|
||||||
|
|
||||||
/**
|
// Уничтожение ETCP instance
|
||||||
* @brief Сбросить состояние соединения (очистить очереди, метрики, таймеры) |
void etcp_destroy(struct ETCP_CONN* etcp); |
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* Примечание: Сохраняет настройки пропускной способности и обратного вызова |
|
||||||
*/ |
|
||||||
void etcp_reset(epkt_t* epkt); |
|
||||||
|
|
||||||
/**
|
// Обработать входящий пакет (вызывается из etcp_connections)
|
||||||
* @brief Инициировать сброс соединения через служебные пакеты |
void etcp_conn_input(struct ETCP_CONN* etcp, packet_buffer_t* pkt); |
||||||
* @param epkt Экземпляр ETCP |
|
||||||
* Отправляет пакет сброса (0x02) и ждет подтверждения (0x03) с повторными попытками каждые 100мс |
|
||||||
*/ |
|
||||||
void etcp_reset_connection(epkt_t* epkt); |
|
||||||
|
|
||||||
/**
|
// Получение статистики
|
||||||
* @brief Получить статистику ETCP |
void etcp_get_stats(struct ETCP_CONN* etcp, size_t* packets_sent, size_t* packets_recv, |
||||||
* @param epkt Экземпляр ETCP |
size_t* pool_allocs, size_t* pool_reuse); |
||||||
* @param retransmissions Указатель для возврата количества ретрансмиссий |
|
||||||
* @param total_packets_sent Указатель для возврата общего количества отправленных пакетов |
|
||||||
* @param unique_packets_sent Указатель для возврата количества уникальных отправленных пакетов |
|
||||||
* @param bytes_sent_total Указатель для возврата общего количества отправленных байт |
|
||||||
* @param bytes_received_total Указатель для возврата общего количества полученных байт |
|
||||||
* @param ack_packets_count Указатель для возврата количества отправленных пакетов подтверждения |
|
||||||
* @param control_packets_count Указатель для возврата количества отправленных управляющих пакетов |
|
||||||
*/ |
|
||||||
void etcp_get_stats(epkt_t* epkt, |
|
||||||
uint32_t* retransmissions, |
|
||||||
uint32_t* total_packets_sent, |
|
||||||
uint32_t* unique_packets_sent, |
|
||||||
uint32_t* bytes_sent_total, |
|
||||||
uint32_t* bytes_received_total, |
|
||||||
uint32_t* ack_packets_count, |
|
||||||
uint32_t* control_packets_count); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
#ifdef __cplusplus |
||||||
} |
} |
||||||
#endif |
#endif |
||||||
|
|
||||||
#endif // ETCP_H
|
#endif // ETCP_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 |
#ifndef ROUTING_H |
||||||
#define ROUTING_H |
#define ROUTING_H |
||||||
#include <stdint.h> |
#include <stdint.h> |
||||||
#include <stddef.h> |
#include <stddef.h> |
||||||
#include <stdbool.h> |
#include <stdbool.h> |
||||||
#include "connection.h" |
|
||||||
#include "control_socket.h" |
// Forward declarations
|
||||||
|
struct ETCP_CONNECTIONS; |
||||||
|
|
||||||
// Типы маршрутов
|
// Типы маршрутов
|
||||||
typedef enum { |
typedef enum { |
||||||
ROUTE_TYPE_STATIC = 0, // Статические маршруты, загружаемые при старте
|
ROUTE_TYPE_STATIC = 0, |
||||||
ROUTE_TYPE_DYNAMIC = 1, // Динамические маршруты, полученные от соседей
|
ROUTE_TYPE_DYNAMIC = 1, |
||||||
ROUTE_TYPE_LOCAL = 2, // Локальные подсети
|
ROUTE_TYPE_LOCAL = 2, |
||||||
ROUTE_TYPE_LEARNED = 3 // Маршруты, полученные от других узлов
|
ROUTE_TYPE_LEARNED = 3 |
||||||
} route_type_t; |
} route_type_t; |
||||||
|
|
||||||
// Флаги маршрута
|
// Флаги маршрута
|
||||||
typedef enum { |
typedef enum { |
||||||
ROUTE_FLAG_ACTIVE = (1 << 0), // Маршрут активен (лучший путь)
|
ROUTE_FLAG_ACTIVE = (1 << 0), |
||||||
ROUTE_FLAG_VALIDATED = (1 << 1), // Маршрут прошёл валидацию (по разрешенным подсетям в конфиге)
|
ROUTE_FLAG_VALIDATED = (1 << 1), |
||||||
ROUTE_FLAG_ADVERTISED = (1 << 2), // Маршрут анонсируется другим узлам
|
ROUTE_FLAG_ADVERTISED = (1 << 2), |
||||||
ROUTE_FLAG_LEARNED = (1 << 3) // Маршрут получен от соседа
|
ROUTE_FLAG_LEARNED = (1 << 3) |
||||||
} route_flags_t; |
} route_flags_t; |
||||||
|
|
||||||
// Расширенные метрики маршрута
|
// Расширенные метрики маршрута
|
||||||
typedef struct { |
typedef struct { |
||||||
uint32_t bandwidth_kbps; // Доступная (минимальная по всем узлам) пропускная способность до узла по этому маршруту, кбит/с
|
uint32_t bandwidth_kbps; |
||||||
uint16_t packet_loss_rate; // Процент потерь пакетов (0–10000 = 0.00–100.00%)
|
uint16_t packet_loss_rate; |
||||||
uint16_t latency_ms; // Задержка в миллисекундах (суммарная до узла, не вулючает задержку до next hop)
|
uint16_t latency_ms; |
||||||
uint8_t hop_count; // Количество хопов до цели (не включая next hop)
|
uint8_t hop_count; |
||||||
uint64_t last_updated; // Время последнего обновления (в timebase 0.1ms)
|
uint64_t last_updated; |
||||||
} route_metrics_t; |
} route_metrics_t; |
||||||
|
|
||||||
// Расширенная запись маршрута
|
// Расширенная запись маршрута
|
||||||
typedef struct { |
typedef struct { |
||||||
uint32_t network; // Адрес сети (в сетевом порядке байтов)
|
uint32_t network; |
||||||
uint8_t prefix_length; // Длина префикса (0–32)
|
uint8_t prefix_length; |
||||||
uint32_t next_hop_ip; // IP-адрес следующего хопа (узла который анонсировал маршрут)
|
uint32_t next_hop_ip; |
||||||
conn_handle_t* next_hop; // Указатель на соединение со следующим хопом
|
struct ETCP_CONNECTIONS* next_hop; |
||||||
|
route_type_t type; |
||||||
route_type_t type; // Тип маршрута
|
uint8_t flags; |
||||||
uint8_t flags; // Флаги маршрута
|
route_metrics_t metrics; |
||||||
|
uint64_t created_time; |
||||||
route_metrics_t metrics; // Метрики маршрута
|
uint64_t last_update; |
||||||
uint64_t created_time; // Время создания записи
|
uint64_t last_used; |
||||||
uint64_t last_update; // Время последнего обновления
|
|
||||||
uint64_t last_used; // Время последнего использования
|
|
||||||
|
|
||||||
} route_entry_t; |
} route_entry_t; |
||||||
|
|
||||||
|
// Таблица маршрутизации
|
||||||
|
|
||||||
// Расширенная таблица маршрутизации (элементы сортированы по IP), может содержать дублирующие маршруты по альтернативным каналам.
|
|
||||||
typedef struct { |
typedef struct { |
||||||
route_entry_t *entries; // Массив записей маршрутов
|
route_entry_t *entries; |
||||||
size_t count; // Количество активных записей
|
size_t count; |
||||||
size_t capacity; // Выделенная ёмкость массива (расширение x2 при нехватке места)
|
size_t capacity; |
||||||
|
uint32_t *dynamic_subnets; |
||||||
// Диапазоны для валидации подсетей
|
size_t dynamic_subnet_count; |
||||||
uint32_t *dynamic_subnets; // Разрешённые диапазоны динамических маршрутов (анонс не принимается если вне этой таблицы)
|
uint32_t *local_subnets; |
||||||
size_t dynamic_subnet_count; |
size_t local_subnet_count; |
||||||
|
struct { |
||||||
// Локальные подсети, которые будут анонсироваться
|
uint64_t total_routes; |
||||||
uint32_t *local_subnets; |
uint64_t static_routes; |
||||||
size_t local_subnet_count; |
uint64_t dynamic_routes; |
||||||
routing_stats_t stats; // Statistics counters
|
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; |
} routing_table_t; |
||||||
|
|
||||||
/**
|
routing_table_t* routing_table_create(void); |
||||||
* @brief Создаёт улучшенную таблицу маршрутизации |
|
||||||
* @return указатель на новую таблицу или NULL при ошибке |
|
||||||
*/ |
|
||||||
routing_table_t *routing_table_create(void); |
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Уничтожает таблицу маршрутизации |
|
||||||
*/ |
|
||||||
void routing_table_destroy(routing_table_t *table); |
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); |
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); |
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); |
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); |
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); |
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); |
bool routing_add_local_subnet(routing_table_t *table, uint32_t network, uint8_t prefix_length); |
||||||
|
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 Возвращает все маршруты для указанной сети/префикса |
|
||||||
* |
|
||||||
* @param routes [out] массив указателей (выделяется внутри, нужно освободить) |
|
||||||
*/ |
|
||||||
bool routing_get_all_routes(const routing_table_t *table, uint32_t network, uint8_t prefix_length, |
|
||||||
route_entry_t **routes, size_t *count); |
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Выводит содержимое таблицы маршрутизации (для отладки) |
|
||||||
*/ |
|
||||||
void routing_table_print(const routing_table_t *table); |
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); |
||||||
|
|
||||||
/**
|
#endif // ROUTING_H
|
||||||
* @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] |
[global] |
||||||
|
my_private_key=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef |
||||||
|
my_public_key=abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789 |
||||||
|
my_node_id=babababa1123 # 64bit ID |
||||||
option=value1,value2 |
option=value1,value2 |
||||||
my_private_key=1F1C8A.... (HEX) |
tun_ip=10.0.0.1/24 |
||||||
my_public_key=1F1C8A.... (HEX) |
mtu=1500 # MTU for all connections (0 = use default 1500) |
||||||
tun_ip=1.2.3.4 |
control_ip=127.0.0.1 |
||||||
|
control_port=12345 |
||||||
|
net_debug=0 |
||||||
|
|
||||||
# remote connections |
# мои адреса и каналы |
||||||
[connection: srv_conn1] |
[server: wired1_fast] |
||||||
mode=server |
addr=192.168.0.10:1234 |
||||||
addr=127.0.0.1:1234 |
so_mark=100 |
||||||
peer_public_key=1F1C8A.... (HEX) |
netif=eth0 |
||||||
|
type=nat # public / nat / private |
||||||
|
|
||||||
[connection: conn2_blablabla] (это имя подключения и опции надо запомнить в структуре подключения) |
[server: wireless_bkp] |
||||||
mode=client |
addr=192.168.0.10:1234 |
||||||
from_addr=127.0.0.1:1234 |
so_mark=100 |
||||||
to_addr=127.0.0.1:1234 |
netif=eth0 |
||||||
so_mark=100 (надо установить для этого подключения) |
type=public # public / nat / private |
||||||
netif=lo0 (биндим к конкретному интерфейсу если это можно) |
|
||||||
peer_public_key=1F1C8A.... (HEX) |
|
||||||
|
|
||||||
(подключений может быть много, надо все распарсить и открыть. имя должно загружаться в структуру 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