Compare commits
120
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37417ba49d | ||
|
|
cca3511503 | ||
|
|
d70cdbb040 | ||
|
|
c3f946fb63 | ||
|
|
93de0aa591 | ||
|
|
0c2eeb8820 | ||
|
|
637b21da9e | ||
|
|
c0935aba6c | ||
|
|
6bf267924d | ||
|
|
5318476cdf | ||
|
|
24294b22f6 | ||
|
|
43d95016ae | ||
|
|
41e0736b75 | ||
|
|
1d29cab751 | ||
|
|
d0f5a2970f | ||
|
|
03701f525f | ||
|
|
89b1a01448 | ||
|
|
e3b32cf4d3 | ||
|
|
2d93dd66c8 | ||
|
|
85a91a1320 | ||
|
|
102816d0dc | ||
|
|
c65940f499 | ||
|
|
79bdafc9a7 | ||
|
|
cb1021f231 | ||
|
|
58503907b5 | ||
|
|
666507d3fe | ||
|
|
f0f20c158b | ||
|
|
ed5ccd1ffb | ||
|
|
65611790fa | ||
|
|
e74240f0dd | ||
|
|
e438e19f9e | ||
|
|
de7841f33a | ||
|
|
a9a6b6bb10 | ||
|
|
3f874a80ab | ||
|
|
1f4749a1b9 | ||
|
|
28e5950d29 | ||
|
|
3c3d9a65d9 | ||
|
|
c7ac42eb5f | ||
|
|
62954ca0a9 | ||
|
|
1889d23240 | ||
|
|
f5c93266dc | ||
|
|
861d81815f | ||
|
|
9dafb4c31e | ||
|
|
19a4505a03 | ||
|
|
46f5ff7eca | ||
|
|
7628f77b93 | ||
|
|
118ab04788 | ||
|
|
bfb83f7459 | ||
|
|
797c0736a7 | ||
|
|
8756f561be | ||
|
|
7139969acd | ||
|
|
59d307f98b | ||
|
|
90ad64a106 | ||
|
|
4914348557 | ||
|
|
55fa5c7759 | ||
|
|
0b5a710fe6 | ||
|
|
f1f533a552 | ||
|
|
03490cfc00 | ||
|
|
ba14e31e09 | ||
|
|
5b850ea5da | ||
|
|
2fba5c31c4 | ||
|
|
4f168c93f6 | ||
|
|
6c938ed2ba | ||
|
|
2cb85cbc3d | ||
|
|
754c08e9c2 | ||
|
|
71ccccc2d5 | ||
|
|
c6d1ebeeaf | ||
|
|
eea0ed0a48 | ||
|
|
0738dcfd5a | ||
|
|
4862c30261 | ||
|
|
3c5d82a6ee | ||
|
|
389ef00da6 | ||
|
|
487cb8718e | ||
|
|
42d96f118d | ||
|
|
8962f58ea4 | ||
|
|
1f541d6432 | ||
|
|
e66156eb49 | ||
|
|
2cd38988e6 | ||
|
|
d58b532741 | ||
|
|
4716e54069 | ||
|
|
aa73e9d8cb | ||
|
|
993c265125 | ||
|
|
5ca0bbf9e7 | ||
|
|
0ef7ee597c | ||
|
|
7d460ca156 | ||
|
|
b738b2ff1b | ||
|
|
cc5289e37c | ||
|
|
0c5f25c96d | ||
|
|
b53758bdbe | ||
|
|
f5b1c3e789 | ||
|
|
0edd21e335 | ||
|
|
e0c38676e7 | ||
|
|
fefd2bcc52 | ||
|
|
8427e18c98 | ||
|
|
7329fa65f1 | ||
|
|
de8cce521a | ||
|
|
f06ebd2ab4 | ||
|
|
a97a38d867 | ||
|
|
aff3b2d00f | ||
|
|
ccdbab95a8 | ||
|
|
a01e2f0899 | ||
|
|
dfdec06a17 | ||
|
|
393a641e18 | ||
|
|
53d41d5c62 | ||
|
|
c60ba6eaec | ||
|
|
c5123e4baf | ||
|
|
928c2fe057 | ||
|
|
b5b6875bc5 | ||
|
|
7c953c1929 | ||
|
|
1c09ad2e5a | ||
|
|
3284dffaaf | ||
|
|
c893285d71 | ||
|
|
75028f278a | ||
|
|
7fc1b5419f | ||
|
|
dee9889423 | ||
|
|
35871891db | ||
|
|
373e4b33c7 | ||
|
|
090e2f28e4 | ||
|
|
a2c7f1e6cb | ||
|
|
21da640ebe |
No files matched your search
@@ -1,5 +1,6 @@
|
||||
# Build directory (out-of-tree build)
|
||||
/build/
|
||||
build-win/
|
||||
tools/chatgui/build/
|
||||
|
||||
# Build outputs
|
||||
@@ -113,3 +114,4 @@ build_win.log
|
||||
/wintun.dll
|
||||
src/lwip_orig/
|
||||
lib/timeout/
|
||||
utun.pid
|
||||
@@ -56,6 +56,16 @@ cd tests && make # Build only tests
|
||||
./build_direct.sh # Компиляция вручную с x86_64-w64-mingw32-gcc
|
||||
```
|
||||
|
||||
### ASAN Build (AddressSanitizer, Linux)
|
||||
```bash
|
||||
./build.sh --asan -j4 # lib/ + src/ only (tests not ASAN-compatible)
|
||||
```
|
||||
Запуск с ASAN (detect_leaks отключены чтобы не спамить при shutdown):
|
||||
```bash
|
||||
ASAN_OPTIONS=detect_leaks=0:halt_on_error=0 ./src/utun -f -p /tmp/utun.pid -l /tmp/utun.log
|
||||
```
|
||||
Краш-логи ASAN пишутся в `/tmp/utun_asan.<pid>`.
|
||||
|
||||
## Test Commands
|
||||
|
||||
### Run All Tests
|
||||
@@ -184,11 +194,12 @@ sc_obfuscate_pubkey(salt, peer_pubkey_bin, my_pubkey_bin, obfuscated_output);
|
||||
### Debug Levels (по возрастанию)
|
||||
`none < error < warn < info < debug < trace`
|
||||
|
||||
### Debug Categories (21 категория)
|
||||
### Debug Categories (25 категорий)
|
||||
```
|
||||
NONE=0, UASYNC=1, LL_QUEUE=2, CONNECTION=3, ETCP=4, CRYPTO=5, MEMORY=6,
|
||||
TIMING=7, CONFIG=8, TUN=9, ROUTING=10, TIMERS=11, NORMALIZER=12, BGP=13,
|
||||
SOCKET=14, CONTROL=15, DUMP=16, TRAFFIC=17, DEBUG=18, GENERAL=19, NAT=20
|
||||
SOCKET=14, CONTROL=15, DUMP=16, TRAFFIC=17, DEBUG=18, GENERAL=19, NAT=20,
|
||||
KEEPALIVE=21, ETCPROUTE=22, BBR=23, ETCP_DUMP=24
|
||||
```
|
||||
|
||||
### Настройка отладки
|
||||
@@ -205,9 +216,9 @@ SOCKET=14, CONTROL=15, DUMP=16, TRAFFIC=17, DEBUG=18, GENERAL=19, NAT=20
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
├── lib/ # Core libraries (13 .c + 14 .h)
|
||||
├── src/ # Main source code (28 .c + 23 .h)
|
||||
├── tests/ # 31+ test programs
|
||||
├── lib/ # Core libraries (17 .c + 17 .h)
|
||||
├── src/ # Main source code (42 .c + 44 .h root + 17 в поддиректориях)
|
||||
├── tests/ # 65 .c файлов, 55 в check_PROGRAMS
|
||||
├── doc/ # Technical Specifications
|
||||
├── tools/ # Auxiliary tools
|
||||
│ ├── etcpmon/ # GUI монитор ETCP
|
||||
@@ -235,17 +246,34 @@ SOCKET=14, CONTROL=15, DUMP=16, TRAFFIC=17, DEBUG=18, GENERAL=19, NAT=20
|
||||
- `etcp_connections.c/h` - Socket and link management, INIT handshake, keepalive, PING/PONG
|
||||
- `etcp_loadbalancer.c/h` - Multi-link load balancing with traffic shaper
|
||||
- `etcp_debug.c/h` - ETCP packet dump/formatting
|
||||
- `etcp_router.c/h` - ETCP router/multiplexer (мультиплексирование каналов)
|
||||
- `etcp_connect.c/h` - ETCP outbound connections
|
||||
- `etcp_dump.c/h` - ETCP дамп/декодирование пакетов
|
||||
- `etcp_bbr.c/h` - BBR congestion control for ETCP
|
||||
- `pkt_normalizer.c/h` - Packet fragmentation/reassembly (packer/unpacker)
|
||||
- `packet_dump.c/h` - Packet hex dump utility
|
||||
- `firewall.c/h` - Firewall rules (per-interface filtering)
|
||||
- `dummynet.c/h` - Network emulator integrated into utun (for testing)
|
||||
|
||||
**Routing (src/)**
|
||||
- `routing.c/h` - Routing table management (local routes)
|
||||
- `route_lib.c/h` - Routing library utilities
|
||||
- `route_bgp.c/h` - BGP-style route exchange between peers
|
||||
**BBR (src/BBR/)**
|
||||
- `bbr_v3.c` - BBR congestion control algorithm v3
|
||||
|
||||
**STCP (src/)**
|
||||
- `stcp.c/h` - STCP протокол (Secure TCP)
|
||||
- `stcp_server.c/h` - STCP сервер
|
||||
- `stcp_client.c/h` - STCP клиент
|
||||
- `stcp_link.c/h` - STCP линк-уровень
|
||||
|
||||
**Топология / Маршрутизация (src/)**
|
||||
- `routing.c/h` - Таблица маршрутов (локальные)
|
||||
- `route_lib.c/h` - Библиотека маршрутизации
|
||||
- `route6_lib.c/h` - IPv6 маршрутизация
|
||||
- `route_ping.c/h` - Route ping probing (NAT check, liveness)
|
||||
- `route_node.c/h` - Route node (peer) management
|
||||
- `route_connectivity.c/h` - Проверка связности маршрутов
|
||||
- `topo_group.c/h` - BGP-подобный обмен маршрутами между узлами
|
||||
- `topo_node.c/h` - Управление узлами (peer management)
|
||||
- `topo_node_lmdb.c/h` - LMDB-персистентность узлов
|
||||
- `conn_mgr.c/h` - Менеджер соединений
|
||||
|
||||
**Crypto (src/)**
|
||||
- `secure_channel.c/h` - AES-CCM encryption with X25519 key exchange, pubkey obfuscation
|
||||
@@ -262,6 +290,23 @@ SOCKET=14, CONTROL=15, DUMP=16, TRAFFIC=17, DEBUG=18, GENERAL=19, NAT=20
|
||||
**Control (src/)**
|
||||
- `control_server.c/h` - Control/monitoring server (etcpmon backend API)
|
||||
|
||||
**Транспорт сообщений (src/)**
|
||||
- `msg_transport.c/h` - Транспорт сообщений (чат, команды)
|
||||
- `db_sync.c/h` - Синхронизация БД между узлами
|
||||
|
||||
**Прокси (src/proxy/)**
|
||||
- `socks_proxy.c/h` - SOCKS5 прокси
|
||||
- `udp_proxy.c/h` - UDP прокси
|
||||
- `icmp_proxy.c/h` - ICMP прокси
|
||||
- `tcp_proxy_server.c/h` - TCP прокси (серверная сторона)
|
||||
- `tcp_proxy_client.c/h` - TCP прокси (клиентская сторона)
|
||||
|
||||
**lwIP TCP (src/lwip_tcp/)**
|
||||
- `lwip_tcp.c/h` - lwIP TCP стек
|
||||
- `lwip_tcp_out.c` / `lwip_tcp_in.c` - Исходящий/входящий трафик
|
||||
- `lwip_pbuf.c/h` - Пакетные буферы lwIP
|
||||
- `lwip_tcp_priv.h`, `lwip_tcp_opts.h` - Внутренние настройки lwIP
|
||||
|
||||
**Libraries (lib/)**
|
||||
- `u_async.c/h` - Async event loop (epoll/poll/select, timers via timeout_heap)
|
||||
- `ll_queue.c/h` - Lock-free linked list queue with callbacks, hash index, threshold waiter
|
||||
@@ -276,6 +321,9 @@ SOCKET=14, CONTROL=15, DUMP=16, TRAFFIC=17, DEBUG=18, GENERAL=19, NAT=20
|
||||
- `myip.c` - My IP helper (no header)
|
||||
- `socket_compat.c/h` - Cross-platform socket compatibility (Linux/FreeBSD/Windows)
|
||||
- `platform_compat.c/h` - Cross-platform compatibility layer (byte order, time, random)
|
||||
- `radix.c/h` - Radix tree for IP routing lookups
|
||||
- `tcp_io.c/h` - TCP I/O abstraction (Windows IOCP, Linux epoll)
|
||||
- `wintun.h` - Wintun API header for Windows TUN driver
|
||||
|
||||
**Memory Pools in UTUN_INSTANCE:**
|
||||
- `data_pool` — для данных пакетов (payload), используется input_queue/output_queue фрагментами
|
||||
@@ -298,7 +346,8 @@ SOCKET=14, CONTROL=15, DUMP=16, TRAFFIC=17, DEBUG=18, GENERAL=19, NAT=20
|
||||
|
||||
### Запись в очередь
|
||||
- Очереди забивать нельзя. Добавляй следующий элемент только когда очередь стала пустой.
|
||||
- Используй `queue_wait_threshold` для ожидания освобождения очереди до заданного порога.
|
||||
- Порог задаётся через `queue_set_threshold(q, max_packets, max_bytes)`.
|
||||
- Используй `queue_waiter_wait` для ожидания освобождения очереди до заданного порога.
|
||||
|
||||
### Чтение из очереди
|
||||
- Используй `queue_set_callback`: при вызове callback обработай один или несколько элементов, потом вызови `queue_resume_callback`.
|
||||
@@ -323,6 +372,40 @@ SOCKET=14, CONTROL=15, DUMP=16, TRAFFIC=17, DEBUG=18, GENERAL=19, NAT=20
|
||||
Всегда когда начинаешь диагностику ознакомься со скиллом (используй skills)
|
||||
Диагностика и поиск ошибок - это в первую очередь продумывание отладочных механизмов которые покажут понятную картину и точное представление об ошибке. Рассуждать надо как диагностикой добиться точной картины. И как приавльнее добавить диагностику чтобы не спамила лишними сообщениями и была понятной, логичной и информативной.
|
||||
|
||||
Важное правило отладки: вся отладка сводится к тому чтобы сделать удобные логи которые наглядно показывают поведение и проблемы. без лишнего мусора, компактно и по существу. очень желательно с деталями которые сильно повышают качество отладки.
|
||||
при отладке нельзя: гадать и долго пытаться разбирать код пытаяясь найти причину. гораздо надёжнее с помощью логов понято точное поведение.
|
||||
Если логов много - записывай в файл и потом анализируй.
|
||||
Если видишь спам-логи - подумай как их выборочно отключить чтобы не мешали.
|
||||
|
||||
Для отладки добавляй в DEBUG_CATEGORY_DEBUG диагностические сообщения где надо. И включи эту категорию в настройках. Убирай только после проверки (путём запуска) когда все ошибки устранены.
|
||||
сообщение выводится по или - либо debug_set_level(DEBUG_LEVEL_TRACE) - выводится ВСЁ независимо от настроек по категориям. Тоесть берется max(global level, category lavel)
|
||||
|
||||
Твой бич - ты постоянно гадаешь и анализируешь код. Что приводит к снежному кобу ошибок и неверных гипотез. 10 раз повторяю - только логи логи логи и никакого гадания. Лоооги!!! правильные логи покажут всё с предельной точностью. Вся суть отладки - информативные логи
|
||||
И смотри логи. не задавливай их grep-ом. лучше больше. единственное с чем борись - это бесполезные спам логи.
|
||||
Но полезные смотри всегда, и всегда оставляй логи которые выводятся нечасто.
|
||||
Частые логи - это трафик которые >100 раз повторяются. Но которые мало раз обязательно оставлять и выводить.
|
||||
можешь в тесте включить логи в нужный интервал чтобы не спамить. debug_set_level(DEBUG_LEVEL_TRACE) и выключить debug_set_level(DEBUG_LEVEL_NONE)
|
||||
Если видишь проблему и ее решение неочевидно то выстраивай диагностику вокруг неё пока не будет очевидно где и что происходит не так. Это базовое и обязательное требование к отладке.
|
||||
Подробные логи ты должен выводить не обрезая всегда и анализировать. Если лог большой - выведи в файл и анализируй файл.
|
||||
Рассуждение должно быть примерно таким:
|
||||
- данные повреждаются при отправке.
|
||||
- где и почему непонятно. надо продумать ключевые точки где получим максимум полезной информации и не было большого объёма вывода лога.
|
||||
- где лучше? каждый пакет логировать - много, но будет предельно точная картина
|
||||
- Еще хорошо бы время - так мы заодно сможем найти проблемы с производительностью, найти проблемные места застревания кода.
|
||||
- Но это большой объём. Можно ли без него? можно но это будет малоэффективно и хороших альтернатив пока не видно.
|
||||
- Значит логируем весь трафик, а заодно включим полную трассировку функций.
|
||||
- Запустили. Записали весь процесс в файл. Теперь можно анализировать. Давай сперва посчитаю количество строк с дампом: ... 50000.
|
||||
- давай сделаю простой скрипт который дамп прочитает и сохранит в файл. И сверю с оригинальным файлом
|
||||
- Давай заодно возьму произвольный фрагмент лога и изучу еа предмет явных проблем. Особенно инициализацию и освобождение ресурсов.
|
||||
Сколько времени занимает, нет ли заклиниваний, нет ли ошибок или странностей
|
||||
|
||||
### Диагностические сообщения (DEBUG_WARN DEBUG_ERROR)
|
||||
Сообщения должны говорить что случилось и с какими деталями.
|
||||
Обязательно выводить подробные собощения об ошибках (даже если нужен небольшой дополнительный код чтобы эти детали сформировать и привести в читаемый вид).
|
||||
Полезны подробности об инициализации-завершении которые помогают понять внутренние состояния
|
||||
Диагностические сообщения - важный момент. надо про диагностику помнить.
|
||||
Для хорошей диагностики надо проанализировать как архитектор - какие есть нюансы в архитектуре и что полезно будет видеть в дебаг выводе чтобы понять полную картину происходящего.
|
||||
|
||||
### Прочие правила:
|
||||
- sed для редактирования исходников - запрещено
|
||||
- Проверяй на дублирование кода - не сделано ли это уже в другом месте
|
||||
@@ -341,11 +424,15 @@ SOCKET=14, CONTROL=15, DUMP=16, TRAFFIC=17, DEBUG=18, GENERAL=19, NAT=20
|
||||
|
||||
## chatgui (GUI Chat Client)
|
||||
|
||||
Chatgui — десктопный GUI-чат на Qt 5, отдельный проект внутри репозитория. Не связан с autotools-сборкой utun.
|
||||
Chatgui — десктопный GUI-чат на Qt 6 (Qt 5 fallback), отдельный проект внутри репозитория. Не связан с autotools-сборкой utun.
|
||||
|
||||
В chat gui интегрированы библиотеки utun. Чат и библиотеки работают в разных потоках. Поэтому нужно использовать семафоры, сокеты или другие механизмы синхронизации (uasync_post, uasync_memsync, uasync_get_wakeup_fd)
|
||||
|
||||
База данных sqlite в chatgui: можно писать (update) из потока utun / instance. можно только читать из gui потока.
|
||||
|
||||
### Технологии
|
||||
- **Язык:** C++17
|
||||
- **Фреймворк:** Qt 5.15+ (Widgets + Network)
|
||||
- **Язык:** C++20
|
||||
- **Фреймворк:** Qt 6 (предпочитаемый) или Qt 5.15+ (Widgets + Network)
|
||||
- **Сборка:** CMake 3.16+
|
||||
- **БД:** SQLite3 (WAL mode, встроенный `db/sqlite3.c` amalgamation)
|
||||
- **Анимации:** rlottie (C API) + zlib (gzip-декомпрессия TGS)
|
||||
@@ -353,7 +440,8 @@ Chatgui — десктопный GUI-чат на Qt 5, отдельный про
|
||||
### Сборка
|
||||
```bash
|
||||
# Установка зависимостей
|
||||
sudo apt install librlottie-dev zlib1g-dev qtbase5-dev
|
||||
sudo apt install librlottie-dev zlib1g-dev qt6-base-dev
|
||||
# или qtbase5-dev для Qt 5 fallback
|
||||
|
||||
# Сборка
|
||||
cd tools/chatgui && mkdir -p build && cd build
|
||||
@@ -365,6 +453,7 @@ cmake .. && make -j4
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|-----------|
|
||||
| `main.cpp` | Точка входа, инициализация QApplication |
|
||||
| `mainwindow.h/cpp` | Главное окно, QSplitter (ChannelList \| MessageList \| AccountList), трей |
|
||||
| `chatview.h/cpp` | QListView с фоновым изображением, hover-сигнал, drag-to-select |
|
||||
| `messagelist.h/cpp` | QStandardItemModel, stub-данные, инициализация анимаций, hover→activate |
|
||||
@@ -377,10 +466,18 @@ cmake .. && make -j4
|
||||
| `animtimer.h/cpp` | Синглтон-таймер 30fps, refcount activate/deactivate, сигнал ticked |
|
||||
| `channellist.h/cpp` `channeldelegate.h/cpp` | Список каналов с аватарами и превью |
|
||||
| `accountlist.h/cpp` | Список аккаунтов/пользователей |
|
||||
| `joindialog.h/cpp` | Диалог присоединения к группе/каналу |
|
||||
| `invitedialog.h/cpp` | Диалог приглашения пользователя |
|
||||
| `invite_link.h/cpp` | Логика invite-ссылок |
|
||||
| `settingsdialog.h/cpp` | Окно настроек |
|
||||
| `creategroupdialog.h/cpp` | Диалог создания группы |
|
||||
| `networksettingspage.h/cpp` | Страница сетевых настроек |
|
||||
| `qrcode_utils.h/cpp` | Утилиты для QR-кодов |
|
||||
| `debug_ui.h` | Отладочный UI |
|
||||
|
||||
### Другие поддиректории
|
||||
- `db/` — SQLite3 amalgamation (`sqlite3.c/h`), `db_manager.h/cpp` (схема: nodes, channels, messages, keys, addresses)
|
||||
- `transport/` — TCP-клиент для связи с uTun (`msg_client.h/cpp`, бинарный протокол `[size:2][type:1][id:8][options:1][data...]`)
|
||||
- `db/` — SQLite3 amalgamation (`sqlite3.c/h`), `db_manager.h/cpp` (схема: nodes, node_addresses, channels, msg_<channel>, peers_<channel>, local_identity, accounts, ui_state)
|
||||
- `transport/` — интеграция с uTun: `gui_bridge.h/gui_bridge_impl.cpp` (Qt signal-based обмен), `chat_core.h/c` (управление), `chat_sync.h/c` (синхронизация), `db_sync_stub.c` (заглушка БД), `node_config.h/cpp`, `config_updater.h/cpp`, `utun_node.h/cpp`, `topo_node_sqlite.h/c`
|
||||
- `resources/` — `bg.jpg`, `chatgui.qrc` (встраивает 5 TGS в бинарник), `animations/*.tgs`
|
||||
|
||||
### Система анимированных эмодзи
|
||||
@@ -478,7 +575,9 @@ void lottie_animation_destroy(Lottie_Animation *anim);
|
||||
- `/doc/etcp_protocol.txt` - ETCP протокол (формат кодограмм, ACK, handshake, keepalive)
|
||||
- `/doc/etcp_arch.md` - ETCP архитектура
|
||||
- `/doc/etcp_config.txt` - Конфигурация ETCP
|
||||
- `/doc/etcp_router_arch.md` - ETCP роутер архитектура
|
||||
- `/doc/route_p2pconn.txt` - Route peer-to-peer соединения
|
||||
- `/src/route_bgp.txt` - BGP обмен маршрутами (дизайн-документ)
|
||||
|
||||
## Runtime
|
||||
- Запуск utun от root (для tun): `/home/vnc1/proj/utun3/utun_start.sh`
|
||||
@@ -490,7 +589,7 @@ void lottie_animation_destroy(Lottie_Animation *anim);
|
||||
- **Commit Messages:** Use imperative mood, concise (50-72 chars)
|
||||
- **Language:** Mix of English (technical) and Russian (business logic)
|
||||
- **Tags:** Version tags follow vX.Y.Z format
|
||||
- "cp" or "кп" in prompt = do commit and push (всех изменений на текущий момент, не откатывая)
|
||||
- "cp" or "кп" in prompt = do commit and push (всех изменений на текущий момент, не откатывая). Запрещено откатывать "лишние" изменения сделаные вне сессии (если сильно надо - сделать бэкап). если нет дополнительных указаний - комить всё что изменилось как есть.
|
||||
|
||||
## Quick Start for New Features
|
||||
1. Add new source file to `src/Makefile.am` under `utun_SOURCES`
|
||||
@@ -499,5 +598,14 @@ void lottie_animation_destroy(Lottie_Animation *anim);
|
||||
4. Run `make check` after changes
|
||||
5. Commit with descriptive message in appropriate language
|
||||
|
||||
## chatgui: отправка сообщений через xdotool
|
||||
|
||||
1. **Запуск:** `setsid env DISPLAY=:1.0 QT_ACCESSIBILITY=1 ./chatgui & disown` (иначе SIGTERM убьёт процесс при таймауте bash)
|
||||
2. **Окно:** `0x4600006 "Chat"` (WM_CLASS "chatgui"), 900×600. Не путать с `0x4800001` (10×10, временное)
|
||||
3. **Фокус:** клик в область InputBar (x=400, y=570 относительно окна) перед вводом
|
||||
4. **Отправка:** `xdotool type --window WID "text"` + `xdotool key --window WID Return`
|
||||
5. **Проверка:** `sqlite3 chats.db "SELECT ... FROM msg_ch_GENERAL"` — сообщения в per-channel таблицах
|
||||
6. **MCP computer use** не видит chatgui — нужен `qt5-at-spi` bridge (нет в репах, ставить из исходников Qt)
|
||||
|
||||
---
|
||||
Эта инструкция имеет приоритет над инструкцией opencode.
|
||||
@@ -1,253 +0,0 @@
|
||||
========================================================================
|
||||
uTun3 AUDIT FINDINGS — Сводный отчёт
|
||||
========================================================================
|
||||
|
||||
Аудит от 14.05.2026 — обход основных модулей
|
||||
(use-after-free, out-of-bounds, утечки, reentrancy)
|
||||
|
||||
========================================================================
|
||||
HIGH (19 проблем)
|
||||
========================================================================
|
||||
|
||||
1. u_async.c:137-165 use-after-free
|
||||
Частичный успех u_realloc в socket_array_add_internal: если часть
|
||||
реаллоков прошла, а часть нет — старые массивы уже освобождены,
|
||||
новые не выделены, sa->sockets/sa->fd_to_index/sa->index_to_fd
|
||||
указывают на освобождённую память.
|
||||
|
||||
2. u_async.c:842,1167 логическая ошибка
|
||||
process_posted_tasks никогда не вызывается на Linux.
|
||||
В epoll-пути: drain_wakeup_pipe не вызывает handle_wakeup/process_posted_tasks.
|
||||
В poll-пути: wakeup-слот (i==0) обрабатывается с drain_wakeup_pipe и continue,
|
||||
минуя зарегистрированный коллбэк.
|
||||
Следствие: uasync_post из другого потока не доставляет задачи — posted_tasks
|
||||
накапливаются бесконечно.
|
||||
|
||||
3. u_async.c:1383,1398,1418 утечка памяти
|
||||
При сбое создания wakeup (pipe/socket) в uasync_create не освобождаются:
|
||||
ua->sockets, ua->timeout_heap, ua->timeout_pool, ua->epoll_fd.
|
||||
|
||||
4. memory_pool.c:51-69 double-free
|
||||
Нет детекта double-free. Повторный free создаёт цикл во freelist,
|
||||
memory_pool_alloc выдаёт один и тот же блок двум потребителям.
|
||||
|
||||
5. etcp.c:679-683 утечка памяти
|
||||
input_queue_cb: при исчерпании inflight_pool вызывается
|
||||
queue_entry_free(in_pkt) без queue_dgram_free(&in_pkt->ll).
|
||||
dgram из data_pool утекает.
|
||||
|
||||
6. etcp_connections.c:87,876-926 use-after-free
|
||||
burst_resp_timer не отменяется в etcp_link_close.
|
||||
Коллбэк burst_resp_timeout_cb срабатывает на освобождённом link.
|
||||
|
||||
7. etcp_connections.c:846-854 use-after-free
|
||||
stats_timer не отменяется при провале insert_link в etcp_link_new.
|
||||
link_stats_timer_cb срабатывает на освобождённом link.
|
||||
|
||||
8. etcp_connections.c:879-886 use-after-free
|
||||
Ветка conn==NULL в etcp_link_close пропускает отмену ВСЕХ таймеров
|
||||
(stats_timer, init_timer, shaper_timer, keepalive_timer, burst_resp_timer).
|
||||
Любой из них сработает на освобождённом link.
|
||||
|
||||
9. etcp_connections.c:1388,1531,1561 out-of-bounds read
|
||||
INIT request парсинг: проверка pkt_len < 12 должна быть pkt_len < 26
|
||||
(23 байта заголовка). Пакет с длиной 12-25 пройдёт проверку,
|
||||
но читает за границей расшифрованных данных.
|
||||
|
||||
10. pkt_normalizer.c:33 null deref
|
||||
if (!pn) pn->alloc_errors++ — разыменование NULL после u_calloc.
|
||||
|
||||
11. pkt_normalizer.c:300 null deref
|
||||
pn_buf_renew в цикле without проверки возврата → memcpy(NULL, ...)
|
||||
при сбое memory_pool_alloc.
|
||||
|
||||
12. eim_nat.c:80-93 логическая ошибка
|
||||
Переполнение uint16_t порта: next_port с 65535→0, затем порты
|
||||
ниже port_start выделяются (включая порт 0). Соединения нерабочие.
|
||||
|
||||
13. eim_nat.c (весь файл) логическая ошибка
|
||||
Нет механизма истечения/удаления NAT-записей. Таблица навсегда
|
||||
исчерпывается при долгой работе, eim_nat_egress начинает отказывать.
|
||||
|
||||
14. route_bgp.c:749,762-766 утечка памяти
|
||||
route_bgp_process_nodeinfo: при пересоздании nodeinfo1 старая
|
||||
очередь paths сохраняется в локальную переменную, но затем
|
||||
перезаписывается новой queue_new() — утечка.
|
||||
|
||||
15. route_bgp.c:904-907 утечка памяти
|
||||
route_bgp_send_nodeinfo: при провале etcp_send не освобождаются
|
||||
p (u_malloc) и e (queue_entry_new).
|
||||
|
||||
16. route_ping.c:126-134 утечка памяти
|
||||
route_ping_cancel_for_conn: nat_check_arg (p->arg, u_calloc) не
|
||||
освобождается. В нормальном и таймаутном путях коллбэк освобождает,
|
||||
но cancel-путь коллбэк не вызывает.
|
||||
|
||||
17. ll_queue.c:321,439 reentrancy
|
||||
Синхронный вызов коллбэка при первом queue_data_put (count==1)
|
||||
без отложенного возобновления через uasync → рекурсивный
|
||||
ack_timeout_check с устаревшим указателем current.
|
||||
|
||||
18. secure_channel.c:379,711 потенциальный OOB write
|
||||
sc_encrypt пишет в caller buffer без параметра capacity.
|
||||
Контракт API опасен: вызывающий должен сам гарантировать размер буфера.
|
||||
|
||||
19. secure_channel.c:432,756 потенциальный OOB write
|
||||
sc_decrypt — аналогично, нет проверки capacity выходного буфера.
|
||||
|
||||
|
||||
========================================================================
|
||||
MEDIUM (18 проблем)
|
||||
========================================================================
|
||||
|
||||
20. u_async.c:591-639 use-after-free
|
||||
Stale handle в uasync_cancel_timeout/uasync_call_soon_cancel.
|
||||
Нет инвалидации (generation counter), пул может переиспользовать память.
|
||||
|
||||
21. memory_pool.c:8,38,61,95 race condition
|
||||
Глобальная g_total_free изменяется без блокировок, общая для всех пулов.
|
||||
|
||||
22. memory_pool.c:51-69 потенциальный OOB write
|
||||
Нет проверки принадлежности блока пулу. Free в чужой пул вызывает
|
||||
memset с чужим object_size — частичная очистка или запись за границу.
|
||||
|
||||
23. timeout_heap.c:43-45 утечка / несоответствие документации
|
||||
Документация обещает free() при free_callback==NULL, код этого не делает.
|
||||
|
||||
24. etcp.c:718-763 reentrancy
|
||||
Вложенный ack_timeout_check (через input_send_q_cb→etcp_request_pkt→
|
||||
wait_ack_cb) обрабатывает не тот entry, т.к. outer loop использует
|
||||
current, который мог быть перемещён/удалён внутренним вызовом.
|
||||
|
||||
25. etcp.c:747,944 утечка / дрейф счётчиков
|
||||
Непроверенный queue_data_put_with_index: при провале счётчики
|
||||
(retransmissions_count, inflight_bytes) не откатываются.
|
||||
|
||||
26. etcp_connections.c:1731,1763 out-of-bounds read
|
||||
INIT_RESPONSE парсинг: нужно >=19 байт, проверяется только >=3.
|
||||
|
||||
27. etcp_connections.c:748-753 use-after-free + бесконечный цикл
|
||||
etcp_socket_remove: при link->conn==NULL remove_link пропускается,
|
||||
num_channels не уменьшается, цикл перечитывает освобождённый conn->links[0].
|
||||
|
||||
28. etcp_connections.c:1736-1738 логическая ошибка
|
||||
link_status перезаписывается значением remote_keepalive, игнорируя
|
||||
локальный recv_keepalive. Временно помечает link UP даже когда
|
||||
локальный приём мёртв.
|
||||
|
||||
29. pkt_normalizer.c:43 integer overflow/underflow
|
||||
frag_size = mtu - ACK_REZERV - UDP_HDR_SIZE - UDP_SC_HDR_SIZE.
|
||||
При mtu < 158 беззнаковое вычитание даёт ~65379 → огромные буферы.
|
||||
|
||||
30. pkt_normalizer.c:240 утечка памяти
|
||||
При провале queue_data_put фрагмент и dgram не освобождаются.
|
||||
|
||||
31. eim_nat.c:97,173 null deref
|
||||
Нет проверки ctx->initialized/ctx->table в egress/ingress.
|
||||
|
||||
32. eim_nat.c:268,293 логическая ошибка
|
||||
Статические пробросы (port forward) вне port range недоступны.
|
||||
|
||||
33. secure_channel.c:66 key hygiene
|
||||
Нет sc_free_ctx — сессионный ключ не зануляется при уничтожении контекста.
|
||||
|
||||
34. secure_channel.c:322,620 key hygiene
|
||||
ECDH shared secret остаётся на стеке незанулённым после
|
||||
sc_derive_session_key.
|
||||
|
||||
35. secure_channel.c:352,680 nonce entropy
|
||||
При постоянном провале random_bytes seed остаётся 0 (static init).
|
||||
Nonce становится полностью предсказуемым (counter+timestamp).
|
||||
|
||||
36. route_bgp.c:1122 утечка памяти
|
||||
route_bgp_send_nat_info: etcp_send без проверки → pkt/e утекают при провале.
|
||||
|
||||
37. route_bgp.c:1157 утечка памяти (аналогично #36)
|
||||
route_bgp_send_nat_check_req: etcp_send без проверки.
|
||||
|
||||
38. config_parser.c:1055-1066 логическая ошибка
|
||||
update_config_keys дописывает [global] секцию в конец файла.
|
||||
При повторном вызове получается дубликат секции, парсер может сбойнуть.
|
||||
|
||||
|
||||
========================================================================
|
||||
LOW (13 проблем)
|
||||
========================================================================
|
||||
|
||||
39. u_async.c:1594-1617 утечка (abort)
|
||||
abort() в destroy до освобождения posted_tasks и мьютекса.
|
||||
|
||||
40. u_async.c:1608 race condition
|
||||
posted_tasks_head читается без posted_lock в destroy.
|
||||
|
||||
41. memory_pool.c:47 truncation
|
||||
size_t → uint32_t в u_calloc_impl (object_size > UINT32_MAX).
|
||||
|
||||
42. memory_pool.h:16 signedness
|
||||
int free_count может обернуться при двойном освобождении.
|
||||
|
||||
43. timeout_heap.c:31 dead code
|
||||
freed_count никогда не обновляется, timeout_heap_get_freed_count не реализован.
|
||||
|
||||
44. timeout_heap.c:66-67 integer overflow
|
||||
capacity*2 и sizeof(TimeoutEntry)*new_cap могут переполниться.
|
||||
|
||||
45. ll_queue.c:106-127 dangling pointer
|
||||
queue_free освобождает hash_table, но не чистит entry->hash_next.
|
||||
|
||||
46. pkt_normalizer.c:374 десинхронизация потока
|
||||
При провале ll_alloc_lldgram 2 байта заголовка уже consumed,
|
||||
но фрагмент сброшен → потеря данных.
|
||||
|
||||
47. eim_nat.c:105,181 unaligned access (ARM)
|
||||
*(uint16_t*)(ip_data + 6) — невыровненный доступ на ARM.
|
||||
|
||||
48. secure_channel.c:108-131 избыточность
|
||||
CRC32 поверх CCM (аутентифицированное шифрование) избыточен.
|
||||
|
||||
49. secure_channel.h:51-52 dead code
|
||||
Неиспользуемые поля send_nonce[13]/recv_nonce[13].
|
||||
|
||||
50. config_parser.c:312 undefined behavior
|
||||
~0U << (32 - cidr) при cidr==0 — сдвиг на ширину типа.
|
||||
|
||||
51. route_ping.c:227 integer overflow
|
||||
uint16_t avg_rtt = timeout_ms * 10 — переполнение при timeout_ms > 6553.
|
||||
|
||||
|
||||
========================================================================
|
||||
Файлы без ошибок
|
||||
========================================================================
|
||||
|
||||
- route_node.c — чисто
|
||||
- tun_if.c — чисто
|
||||
- etcp_api.c — только порядок queue_entry_free/dgram_free (LOW, см. etcp.c Issue #7 старого аудита)
|
||||
- etcp_loadbalancer.c — не аудирован (250 строк)
|
||||
|
||||
|
||||
========================================================================
|
||||
Рекомендуемый порядок исправления
|
||||
========================================================================
|
||||
|
||||
1. КРИТИЧЕСКИ ФУНКЦИОНАЛЬНЫЕ:
|
||||
u_async.c #2 (posted_tasks не работают на Linux)
|
||||
u_async.c #3 (утечки при сбое старта)
|
||||
|
||||
2. USE-AFTER-FREE:
|
||||
etcp_connections.c #6,#7,#8,#5 (висячие таймеры)
|
||||
u_async.c #1 (частичный realloc)
|
||||
ll_queue.c #17 (reentrancy)
|
||||
|
||||
3. УТЕЧКИ ПАМЯТИ:
|
||||
etcp.c #5 (dgram из data_pool)
|
||||
route_bgp.c #14,#15,#36,#37
|
||||
route_ping.c #16
|
||||
|
||||
4. OOB / NULL DEREF:
|
||||
etcp_connections.c #9,#26 (INIT bounds)
|
||||
pkt_normalizer.c #10,#11 (null deref)
|
||||
|
||||
5. ЛОГИКА:
|
||||
eim_nat.c #12,#13 (порты + expiry)
|
||||
memory_pool.c #4 (double-free)
|
||||
config_parser.c #38 (дубликат секции)
|
||||
@@ -28,4 +28,4 @@ if not exist "%MSYS2_PATH%\msys2_shell.cmd" (
|
||||
REM Launch MSYS2 UCRT64 shell and run build script
|
||||
echo Starting MSYS2 UCRT64 environment...
|
||||
rem call "%MSYS2_PATH%\msys2_shell.cmd" -ucrt64 -here -c "bash build.sh > build_win.log 2>&1"
|
||||
call "%MSYS2_PATH%\msys2_shell.cmd" -ucrt64 -here -defterm -no-start -c "bash build.sh 2>&1 | tee build_win.log"
|
||||
call "%MSYS2_PATH%\msys2_shell.cmd" -ucrt64 -here -defterm -no-start -c "set -o pipefail; bash build.sh 2>&1 | tee build_win.log"
|
||||
@@ -5,6 +5,8 @@
|
||||
# -h, --help Show this help
|
||||
# --clean Clean before build
|
||||
# --full Full rebuild (autoreconf + configure + make)
|
||||
# --asan Build with AddressSanitizer (debug)
|
||||
# --hardened Build with compiler security hardening flags
|
||||
# -j[N] Build with N parallel jobs (default: 4)
|
||||
|
||||
set -e # Exit on error
|
||||
@@ -21,8 +23,11 @@ fi
|
||||
# Parse arguments
|
||||
CLEAN=0
|
||||
FULL_REBUILD=0
|
||||
USE_ASAN=0
|
||||
USE_HARDENED=0
|
||||
JOBS="-j4"
|
||||
MAKE_ARGS=""
|
||||
HARDEN_FLAGS=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
@@ -32,6 +37,8 @@ while [[ $# -gt 0 ]]; do
|
||||
echo " -h, --help Show this help"
|
||||
echo " --clean Clean before build"
|
||||
echo " --full Full rebuild (autoreconf + configure + make)"
|
||||
echo " --asan Build with AddressSanitizer"
|
||||
echo " --hardened Build with compiler security hardening flags"
|
||||
echo " -j[N] Build with N parallel jobs (default: 4)"
|
||||
exit 0
|
||||
;;
|
||||
@@ -43,6 +50,14 @@ while [[ $# -gt 0 ]]; do
|
||||
FULL_REBUILD=1
|
||||
shift
|
||||
;;
|
||||
--asan)
|
||||
USE_ASAN=1
|
||||
shift
|
||||
;;
|
||||
--hardened)
|
||||
USE_HARDENED=1
|
||||
shift
|
||||
;;
|
||||
-j*)
|
||||
JOBS="$1"
|
||||
shift
|
||||
@@ -122,13 +137,43 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
# Generate Makefiles if needed
|
||||
if [ ! -f Makefile ]; then
|
||||
echo ""
|
||||
echo "Running ./configure..."
|
||||
if [ "$USE_ASAN" -eq 1 ]; then
|
||||
./configure CFLAGS="-g -Og -fsanitize=address -fno-omit-frame-pointer -fno-pie" LDFLAGS="-no-pie"
|
||||
elif [ "$USE_HARDENED" -eq 1 ]; then
|
||||
./configure --enable-hardening
|
||||
else
|
||||
./configure
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build (all .o files go to build/ directory, binary copied to project root)
|
||||
echo ""
|
||||
echo "Compiling with $JOBS..."
|
||||
BUILD_SUCCESS=0
|
||||
make $JOBS $MAKE_ARGS 2>&1 | tee build.log
|
||||
if [ ${PIPESTATUS[0]} -eq 0 ]; then
|
||||
if [ "$USE_ASAN" -eq 1 ]; then
|
||||
echo "(AddressSanitizer enabled — lib/ and src/ only, skipping tests)"
|
||||
AF="-fsanitize=address -fno-omit-frame-pointer -g -Og"
|
||||
AL="-fsanitize=address"
|
||||
(cd lib && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$AF" "LDFLAGS+=$AL" $JOBS $MAKE_ARGS 2>&1) || { echo "lib build failed"; exit 1; }
|
||||
(cd src && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$AF" "LDFLAGS+=$AL" $JOBS $MAKE_ARGS 2>&1) || { echo "src build failed"; exit 1; }
|
||||
BUILD_SUCCESS=1
|
||||
elif [ "$USE_HARDENED" -eq 1 ]; then
|
||||
echo "(Hardening flags enabled)"
|
||||
HF="-D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection -Wformat=2 -Wformat-overflow=2 -fPIE"
|
||||
HL="-Wl,-z,relro -Wl,-z,now -pie"
|
||||
(cd lib && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$HF" "LDFLAGS+=$HL" $JOBS $MAKE_ARGS 2>&1) || { echo "lib build failed"; exit 1; }
|
||||
(cd src && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$HF" "LDFLAGS+=$HL" $JOBS $MAKE_ARGS 2>&1) || { echo "src build failed"; exit 1; }
|
||||
(cd tests && make clean $JOBS $MAKE_ARGS 2>&1 && make "CFLAGS+=$HF" "LDFLAGS+=$HL" $JOBS $MAKE_ARGS 2>&1) || { echo "tests build failed"; exit 1; }
|
||||
BUILD_SUCCESS=1
|
||||
else
|
||||
make $JOBS $MAKE_ARGS 2>&1 | tee build.log
|
||||
if [ ${PIPESTATUS[0]} -eq 0 ]; then
|
||||
BUILD_SUCCESS=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $BUILD_SUCCESS -eq 1 ]; then
|
||||
|
||||
+37
-6
@@ -1,8 +1,24 @@
|
||||
#!/bin/sh
|
||||
#pkg install -y autoconf openssl
|
||||
|
||||
set -e
|
||||
|
||||
# Проверка и установка зависимостей
|
||||
need_install=""
|
||||
|
||||
for cmd in autoreconf aclocal automake libtoolize; do
|
||||
if ! command -v "$cmd" >/dev/null 2>&1; then
|
||||
need_install=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$need_install" ]; then
|
||||
echo "Устанавливаются необходимые пакеты..."
|
||||
pkg update
|
||||
pkg install -y autoconf automake libtool pkgconf openssl
|
||||
fi
|
||||
|
||||
mkdir -p m4
|
||||
set -e
|
||||
|
||||
echo "=== Полная пересборка uTun ==="
|
||||
|
||||
@@ -12,18 +28,33 @@ make distclean 2>/dev/null || true
|
||||
rm -f config.h config.status config.log
|
||||
rm -f configure.log autoreconf.log build.log
|
||||
|
||||
# 2. Автогенерация (autoreconf)
|
||||
echo "[2/4] Автогенерация (autoreconf -fi)..."
|
||||
# 2. Генерация configure
|
||||
echo "[2/4] Автогенерация..."
|
||||
autoreconf -fi 2>&1 | tee autoreconf.log
|
||||
|
||||
# Проверяем, появился ли configure
|
||||
if [ ! -f configure ]; then
|
||||
echo "Ошибка: autoreconf не создал configure."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chmod +x configure
|
||||
|
||||
# 3. Конфигурация
|
||||
echo "[3/4] Конфигурация..."
|
||||
./configure --prefix=/usr/local 2>&1 | tee configure.log
|
||||
|
||||
# Определяем количество ядер
|
||||
if command -v nproc >/dev/null 2>&1; then
|
||||
JOBS=$(nproc)
|
||||
else
|
||||
JOBS=$(sysctl -n hw.ncpu)
|
||||
fi
|
||||
|
||||
# 4. Сборка
|
||||
echo "[4/4] Сборка..."
|
||||
make -j$(nproc) 2>&1 | tee build.log
|
||||
make -j"$JOBS" 2>&1 | tee build.log
|
||||
|
||||
echo ""
|
||||
echo
|
||||
echo "=== Готово ==="
|
||||
echo "Бинарный файл: src/utun"
|
||||
+1
-1
@@ -70,7 +70,7 @@ echo "Building utun.exe..."
|
||||
cd src
|
||||
EXE_NEEDS_REBUILD=0
|
||||
|
||||
CORE_SOURCES="utun.c utun_instance.c config_parser.c config_updater.c route_lib.c route_bgp.c routing.c tun_if.c tun_route.c etcp.c etcp_connections.c etcp_loadbalancer.c secure_channel.c crc32.c pkt_normalizer.c etcp_api.c tun_windows.c"
|
||||
CORE_SOURCES="utun.c utun_instance.c config_parser.c config_updater.c route_lib.c topo_bgp.c routing.c tun_if.c tun_route.c etcp.c etcp_connections.c etcp_loadbalancer.c secure_channel.c crc32.c pkt_normalizer.c etcp_api.c tun_windows.c"
|
||||
|
||||
for src in $CORE_SOURCES; do
|
||||
if [ -f "$src" ]; then
|
||||
|
||||
+1
-1
@@ -30,4 +30,4 @@ REM Launch MSYS2 UCRT64 shell and run build script with --full flag
|
||||
echo Starting MSYS2 UCRT64 environment for FULL REBUILD...
|
||||
echo Running: bash build.sh --full
|
||||
echo.
|
||||
call "%MSYS2_PATH%\msys2_shell.cmd" -ucrt64 -here -defterm -no-start -c "bash build.sh --full 2>&1 | tee build_full.log"
|
||||
call "%MSYS2_PATH%\msys2_shell.cmd" -ucrt64 -here -defterm -no-start -c "set -o pipefail; bash build.sh --full 2>&1 | tee build_full.log"
|
||||
File diff suppressed because it is too large.
Load diff
@@ -55,6 +55,20 @@ fi
|
||||
|
||||
AM_CONDITIONAL([USE_OPENSSL], [test "x$with_openssl" = "xyes"])
|
||||
|
||||
# ==================== HARDENING ====================
|
||||
AC_ARG_ENABLE([hardening],
|
||||
AS_HELP_STRING([--enable-hardening], [Enable compiler security hardening (FORTIFY, stack protector, PIE, RELRO)]),
|
||||
[enable_hardening=$enableval],
|
||||
[enable_hardening=no])
|
||||
|
||||
if test "x$enable_hardening" = "xyes"; then
|
||||
HARDEN_CFLAGS="-D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection -Wformat=2 -Wformat-overflow=2 -fPIE"
|
||||
HARDEN_LDFLAGS="-Wl,-z,relro -Wl,-z,now -pie"
|
||||
CFLAGS="$CFLAGS $HARDEN_CFLAGS"
|
||||
LDFLAGS="$LDFLAGS $HARDEN_LDFLAGS"
|
||||
AC_MSG_NOTICE([Hardening flags enabled: $HARDEN_CFLAGS])
|
||||
fi
|
||||
|
||||
# ==================== НАДЁЖНЫЙ ДЕТЕКТ WINDOWS ====================
|
||||
AC_MSG_CHECKING([for Windows])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
|
||||
Executable
+284
@@ -0,0 +1,284 @@
|
||||
#!/bin/bash
|
||||
# cross-build-win.sh — кросс-компиляция uTun для Windows 10 из Linux
|
||||
# Использование: ./cross-build-win.sh [--clean]
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
step() { echo -e "${CYAN}===${NC} $*"; }
|
||||
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARNING]${NC} $*"; }
|
||||
fatal() { echo -e "${RED}FATAL:${NC} $*" >&2; exit 1; }
|
||||
|
||||
trap 'fatal "script failed at line $LINENO (exit code: $?)"' ERR
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
BUILD_DIR="$SCRIPT_DIR/build-win"
|
||||
PREFIX="$BUILD_DIR/prefix"
|
||||
|
||||
MINGW_CC="x86_64-w64-mingw32-gcc"
|
||||
MINGW_AR="x86_64-w64-mingw32-ar"
|
||||
MINGW_RANLIB="x86_64-w64-mingw32-ranlib"
|
||||
|
||||
MINGW_PKGS="gcc-mingw-w64-x86-64-posix g++-mingw-w64-x86-64-posix"
|
||||
OPENSSL_VER="3.0.16"
|
||||
OPENSSL_URL="https://github.com/openssl/openssl/releases/download/openssl-${OPENSSL_VER}/openssl-${OPENSSL_VER}.tar.gz"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 0. Аргументы
|
||||
# -------------------------------------------------------------------
|
||||
CLEAN=0
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--clean) CLEAN=1; shift ;;
|
||||
*) fatal "unknown option: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$CLEAN" -eq 1 ]; then
|
||||
echo -e "${YELLOW}Cleaning build directory: $BUILD_DIR${NC}"
|
||||
rm -rf "$BUILD_DIR"
|
||||
fi
|
||||
|
||||
echo -e "${CYAN}==============================================${NC}"
|
||||
echo -e "${CYAN} uTun Cross-Compilation for Windows 10${NC}"
|
||||
echo -e "${CYAN}==============================================${NC}"
|
||||
echo ""
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 1. Проверка системы
|
||||
# -------------------------------------------------------------------
|
||||
step "Checking system..."
|
||||
if [ "$(uname -s)" != "Linux" ]; then
|
||||
fatal "This script only runs on Linux. Detected: $(uname -s)"
|
||||
fi
|
||||
if [ "$(uname -m)" != "x86_64" ]; then
|
||||
fatal "This script requires x86_64 architecture. Detected: $(uname -m)"
|
||||
fi
|
||||
|
||||
if ! command -v apt-get &>/dev/null; then
|
||||
fatal "apt-get not found. Only Debian/Ubuntu are supported."
|
||||
fi
|
||||
ok "System: Linux x86_64 with apt"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 2. Установка пакетов
|
||||
# -------------------------------------------------------------------
|
||||
step "Installing required packages..."
|
||||
|
||||
need_install=()
|
||||
for pkg in $MINGW_PKGS; do
|
||||
if ! dpkg -s "$pkg" &>/dev/null 2>&1; then
|
||||
need_install+=("$pkg")
|
||||
fi
|
||||
done
|
||||
if ! command -v wget &>/dev/null && ! command -v curl &>/dev/null; then
|
||||
need_install+=("wget")
|
||||
fi
|
||||
|
||||
if [ ${#need_install[@]} -gt 0 ]; then
|
||||
echo " Installing: ${need_install[*]}"
|
||||
sudo apt-get update -qq || fatal "apt-get update failed"
|
||||
sudo apt-get install -y -qq "${need_install[@]}" || fatal "apt-get install failed"
|
||||
ok "Packages installed: ${need_install[*]}"
|
||||
else
|
||||
ok "All packages already installed"
|
||||
fi
|
||||
|
||||
# Проверка компилятора
|
||||
for tool in "$MINGW_CC" "$MINGW_AR" "$MINGW_RANLIB"; do
|
||||
if ! command -v "$tool" &>/dev/null; then
|
||||
fatal "$tool not found after package installation"
|
||||
fi
|
||||
done
|
||||
ok "Cross-compiler: $($MINGW_CC --version | head -1)"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 3. Кросс-компиляция OpenSSL
|
||||
# -------------------------------------------------------------------
|
||||
OPENSSL_SRC="$BUILD_DIR/openssl-${OPENSSL_VER}"
|
||||
LIBCRYPTO="$PREFIX/lib/libcrypto.a"
|
||||
|
||||
if [ -f "$LIBCRYPTO" ]; then
|
||||
ok "OpenSSL already built: $LIBCRYPTO"
|
||||
else
|
||||
step "Building OpenSSL ${OPENSSL_VER} for Windows..."
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
TARBALL="$BUILD_DIR/openssl-${OPENSSL_VER}.tar.gz"
|
||||
if [ ! -f "$TARBALL" ]; then
|
||||
echo " Downloading OpenSSL ${OPENSSL_VER}..."
|
||||
if command -v wget &>/dev/null; then
|
||||
wget -q --show-progress -O "$TARBALL" "$OPENSSL_URL" || fatal "wget download failed for $OPENSSL_URL"
|
||||
else
|
||||
curl -L --progress-bar -o "$TARBALL" "$OPENSSL_URL" || fatal "curl download failed for $OPENSSL_URL"
|
||||
fi
|
||||
ok "Downloaded: $TARBALL"
|
||||
fi
|
||||
|
||||
if [ ! -d "$OPENSSL_SRC" ]; then
|
||||
echo " Extracting..."
|
||||
tar xzf "$TARBALL" -C "$BUILD_DIR" || fatal "tar extract failed"
|
||||
fi
|
||||
|
||||
echo " Configuring OpenSSL for mingw64..."
|
||||
cd "$OPENSSL_SRC"
|
||||
./Configure --cross-compile-prefix=x86_64-w64-mingw32- \
|
||||
mingw64 no-shared no-tests \
|
||||
--prefix="$PREFIX" --libdir="$PREFIX/lib" --openssldir="$PREFIX/ssl" \
|
||||
|| fatal "OpenSSL configure failed (see output above)"
|
||||
ok "OpenSSL configured"
|
||||
|
||||
echo " Compiling OpenSSL..."
|
||||
make -j"$(nproc)" || fatal "OpenSSL make failed"
|
||||
echo " Installing OpenSSL..."
|
||||
make install_sw >/dev/null || fatal "OpenSSL make install_sw failed"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
# OpenSSL может установить в lib64/ вместо lib/ — поправим
|
||||
if [ ! -f "$LIBCRYPTO" ] && [ -f "$PREFIX/lib64/libcrypto.a" ]; then
|
||||
ln -sf lib64 "$PREFIX/lib" 2>/dev/null || cp -r "$PREFIX/lib64" "$PREFIX/lib" 2>/dev/null || true
|
||||
fi
|
||||
if [ ! -f "$LIBCRYPTO" ]; then
|
||||
fatal "libcrypto.a was not found at $PREFIX/lib/ or $PREFIX/lib64/"
|
||||
fi
|
||||
ok "OpenSSL built: $LIBCRYPTO"
|
||||
fi
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 4. Сборка libuasync.a
|
||||
# -------------------------------------------------------------------
|
||||
step "Building libuasync.a..."
|
||||
|
||||
LIB_OBJDIR="$BUILD_DIR/lib-objs"
|
||||
mkdir -p "$LIB_OBJDIR"
|
||||
|
||||
# Все .c файлы из lib/Makefile.am + myip.c (реализация get_my_ip)
|
||||
LIB_SOURCES=(
|
||||
u_async.c ll_queue.c memory_pool.c debug_config.c timeout_heap.c
|
||||
sha256.c mem.c serialize.c swm_min.c getmyip.c radix.c tcp_io.c
|
||||
myip.c platform_compat.c socket_compat.c
|
||||
liblmdb/mdb.c liblmdb/midl.c
|
||||
)
|
||||
|
||||
LIB_CFLAGS="-D_ISOC99_SOURCE -DDEBUG_OUTPUT_STDERR -DUSE_OPENSSL"
|
||||
LIB_CFLAGS+=" -I$SCRIPT_DIR/lib -I$SCRIPT_DIR/src -I$SCRIPT_DIR/lib/liblmdb"
|
||||
LIB_CFLAGS+=" -I$PREFIX/include"
|
||||
LIB_CFLAGS+=" -g -O2 -Wno-deprecated-declarations"
|
||||
|
||||
for src in "${LIB_SOURCES[@]}"; do
|
||||
obj="$LIB_OBJDIR/$(basename "${src%.c}.o")"
|
||||
echo " CC lib/$src"
|
||||
"$MINGW_CC" $LIB_CFLAGS -c "$SCRIPT_DIR/lib/$src" -o "$obj" \
|
||||
|| fatal "compilation failed: lib/$src"
|
||||
done
|
||||
|
||||
"$MINGW_AR" crs "$BUILD_DIR/libuasync.a" "$LIB_OBJDIR"/*.o \
|
||||
|| fatal "ar failed for libuasync.a"
|
||||
"$MINGW_RANLIB" "$BUILD_DIR/libuasync.a" 2>/dev/null || true
|
||||
ok "libuasync.a ($(wc -c < "$BUILD_DIR/libuasync.a") bytes)"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 5. Сборка объектных файлов для utun.exe
|
||||
# -------------------------------------------------------------------
|
||||
step "Compiling utun source files..."
|
||||
|
||||
SRC_OBJDIR="$BUILD_DIR/src-objs"
|
||||
mkdir -p "$SRC_OBJDIR"
|
||||
|
||||
# Все .c файлы из src/Makefile.am (utun_CORE_SOURCES)
|
||||
SRC_SOURCES=(
|
||||
utun.c utun_instance.c config_parser.c config_updater.c
|
||||
route_lib.c route6_lib.c topo_bgp.c route_ping.c
|
||||
topo_node.c topo_node_lmdb.c route_connectivity.c conn_mgr.c
|
||||
routing.c tun_if.c tun_route.c tun_linux.c tun_freebsd.c tun_windows.c
|
||||
etcp.c etcp_connections.c etcp_bbr.c etcp_loadbalancer.c
|
||||
etcp_debug.c etcp_dump.c secure_channel.c crc32.c
|
||||
stcp_link.c stcp.c stcp_server.c stcp_client.c
|
||||
pkt_normalizer.c packet_dump.c etcp_api.c
|
||||
control_server.c msg_transport.c
|
||||
firewall.c eim_nat.c nat_transport.c dummynet.c
|
||||
etcp_router.c
|
||||
proxy/udp_proxy.c proxy/socks_proxy.c
|
||||
lwip_tcp/lwip_pbuf.c
|
||||
)
|
||||
|
||||
SRC_CFLAGS="-DUSE_OPENSSL"
|
||||
SRC_CFLAGS+=" -I$SCRIPT_DIR/src -I$SCRIPT_DIR/lib -I$SCRIPT_DIR/lib/liblmdb"
|
||||
SRC_CFLAGS+=" -I$SCRIPT_DIR/tinycrypt/lib/include -I$SCRIPT_DIR/tinycrypt/lib/source"
|
||||
SRC_CFLAGS+=" -I$PREFIX/include"
|
||||
SRC_CFLAGS+=" -g -O2 -Wno-deprecated-declarations"
|
||||
|
||||
TOTAL=${#SRC_SOURCES[@]}
|
||||
CURRENT=0
|
||||
FAILED=()
|
||||
|
||||
for src in "${SRC_SOURCES[@]}"; do
|
||||
CURRENT=$((CURRENT + 1))
|
||||
# Имя объектного файла: заменяем / на _ (для proxy/ и lwip_tcp/)
|
||||
obj_name="$(echo "$src" | tr '/' '_')"
|
||||
obj="$SRC_OBJDIR/${obj_name%.c}.o"
|
||||
printf " [%3d/%3d] CC src/%s\n" "$CURRENT" "$TOTAL" "$src"
|
||||
if ! "$MINGW_CC" $SRC_CFLAGS -c "$SCRIPT_DIR/src/$src" -o "$obj" 2>&1; then
|
||||
FAILED+=("src/$src")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#FAILED[@]} -gt 0 ]; then
|
||||
echo ""
|
||||
fatal "compilation failed for: ${FAILED[*]}"
|
||||
fi
|
||||
ok "All $TOTAL source files compiled"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 6. Линковка utun.exe
|
||||
# -------------------------------------------------------------------
|
||||
step "Linking utun.exe..."
|
||||
|
||||
"$MINGW_CC" -o "$SCRIPT_DIR/utun.exe" \
|
||||
"$SRC_OBJDIR"/*.o \
|
||||
"$BUILD_DIR/libuasync.a" \
|
||||
"$LIBCRYPTO" \
|
||||
-lws2_32 -liphlpapi -ladvapi32 -lbcrypt -lcrypt32 -lpthread -lm \
|
||||
-static \
|
||||
|| fatal "linking failed (see undefined references above)"
|
||||
|
||||
ok "utun.exe ($(wc -c < "$SCRIPT_DIR/utun.exe") bytes)"
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# 7. Копирование wintun.dll и финал
|
||||
# -------------------------------------------------------------------
|
||||
echo ""
|
||||
if [ -f "$SCRIPT_DIR/lib/wintun.dll" ]; then
|
||||
cp "$SCRIPT_DIR/lib/wintun.dll" "$SCRIPT_DIR/"
|
||||
ok "wintun.dll copied to project root"
|
||||
else
|
||||
warn "wintun.dll not found in lib/ — place it manually alongside utun.exe"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}==============================================${NC}"
|
||||
echo -e "${GREEN} Build successful!${NC}"
|
||||
echo -e "${GREEN} Binary: utun.exe${NC}"
|
||||
echo -e "${GREEN}==============================================${NC}"
|
||||
echo ""
|
||||
|
||||
if [ -f "$SCRIPT_DIR/lib/wintun.dll" ]; then
|
||||
echo "Run on Windows:"
|
||||
echo " 1. Install Wintun driver (https://www.wintun.net)"
|
||||
echo " 2. Copy utun.exe + wintun.dll to the same directory"
|
||||
echo " 3. Run: utun.exe -f -p utun.pid -l utun.log"
|
||||
else
|
||||
echo "Run on Windows:"
|
||||
echo " 1. Install Wintun driver (https://www.wintun.net)"
|
||||
echo " 2. Copy utun.exe + wintun.dll to the same directory"
|
||||
echo " 3. Run: utun.exe -f -p utun.pid -l utun.log"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,35 @@
|
||||
План доработок
|
||||
[ ] продумать архитектуру репликации таблицы узлов группы
|
||||
[ ] добавить узлы хелперы (для проксирования трафика)
|
||||
|
||||
|
||||
Категории узлов:
|
||||
- узлы без нат []
|
||||
- узлы с nat [restricted]
|
||||
|
||||
Узлы за nat сами подключаются к прямым
|
||||
|
||||
Узлы за eim nat сами подключаются и сообщают обновление адреса.
|
||||
|
||||
Прямые узлы должны сами должны объединиться в сеть следующим образом:
|
||||
|
||||
Инит: база содержит RTT по узлам.
|
||||
выбираем n=16 узлов с минимальным rtt. запускаем подключение к ним.
|
||||
синхронизируем роутинг таблицу с ними (обмен маршрутами должен инициироваться сразу после подключения).
|
||||
при потере связи узлы через nat должны найти альтернативы.
|
||||
|
||||
в таблице обновляется
|
||||
|
||||
как обмен завершен
|
||||
|
||||
Нужно собирать и обновлять информацию о том
|
||||
- к каким публичным нодам подключены nat узлы.
|
||||
|
||||
- метрика доступности узла - как давно не менялся ip:port - собираем с клиентов. вместе с рейтигном (каждый клиент по узлу фолрмирует свой рейтинг кандидата в суперузлы и отправляет подписанный рейтинг узлу)
|
||||
рейтинг включает:
|
||||
- число разных часов фигурирует в замерах
|
||||
- число разных дат фигурирует в замерах
|
||||
- гистограмма rtt: 0-2ms, 2-4ms, 4-6ms, 6-10ms, 10-13ms, 14-18ms, 18-25ms, 25-35ms, 35-50ms, 50-75ms, 75-100ms, 100-130ms, 130-160ms, 160+ms
|
||||
- гистограмма потерь: 0-1% 1-2% 2-4% 4-8% >8% (обновляется каждый час)
|
||||
|
||||
Сделать подключение к группе
|
||||
+32
-8
@@ -22,12 +22,9 @@ utun поддерживает встроенный ipv4 роутинг, може
|
||||
Есть статические подключения (которые прописаны в конфиге) и есть динамические - которые устанавливаются в процессе по необходимости.
|
||||
Обмен маршрутами происходит только через статические подключения.
|
||||
|
||||
toto:
|
||||
- задать ограничение bandwidth для каждого сокета (общее).
|
||||
|
||||
|
||||
## etcp_connections:
|
||||
- обслуживает encrypt/decrypt и установку защищенного подключения.
|
||||
- обслуживает encrypt/decrypt и установку защищенного подключения per-link.
|
||||
- для одного etcp соединения может испольоваться несколько подключений одновременно (load balancing / filover)
|
||||
|
||||
## etcp_loadbalancer:
|
||||
@@ -50,12 +47,39 @@ toto:
|
||||
- обеспечивает ретрансмиссии при передаче и сборку в правильной последовательности при приёме
|
||||
- взаимодействует с pkt_normalizer и с udp сокетами для отправки-получения пакетов
|
||||
|
||||
## etcp_metric:
|
||||
- обеспечивает обновление метрик каналов etcp_connections
|
||||
- вызывается при получении ack
|
||||
## stcp_* :
|
||||
- аналогично etcp только упрощенный вариант, используя tcp в качестве транспорта.
|
||||
|
||||
## etcp_router:
|
||||
- устанавливает связь между узлами, маршрутизирует трафик через цепочку узлов
|
||||
|
||||
## route_lib, route6_lib :
|
||||
- управляет таблицами маршрутизации (ipv4/ipv6 lookup)
|
||||
|
||||
## route_node:
|
||||
- хранит список доступных узлов (полученные от соседей) и их адреса/pubkeys для подключения.
|
||||
- содержит апи для поиска-чтения-обновления узла
|
||||
|
||||
## route_bgp:
|
||||
- составляет карту маршрутизации между узлами и обновляет ее при подключении-отключении узлов
|
||||
|
||||
## route_ping:
|
||||
- модуль для определения типа nat (strict или eim) используя вспомогательный узел
|
||||
|
||||
Библиотеки:
|
||||
## uasync
|
||||
- модуль для асинхронной работы.
|
||||
реализует сокеты, таймауты и сигналы из других потоков
|
||||
- быстро работает с большим числом таймаутов (timeout_heap)
|
||||
|
||||
## ll_queue
|
||||
- модуль работы с очередями, использует uasync.
|
||||
- реализует fifo/lifo очереди, быстрый поиск по индексу, различные callback - сигнализация о состояниях очереди,
|
||||
- congestion control: если много желающих записать -> очередь round-robin
|
||||
|
||||
|
||||
Тесты:
|
||||
# Правила работы с очередями в тестах:
|
||||
- для добавления в очередь надо использовать queue_wait_threshold(q,0,0,arg) и добавлять по одному пакету, каждый раз дожидаясь когда очередь станет пустой.
|
||||
- для добавления в очередь надо использовать queue_waiter_wait и queue_set_threshold, добавлять по одному пакету, каждый раз дожидаясь когда очередь станет пустой.
|
||||
- для получения надо использовать queue_set_callback + queue_resume_callback
|
||||
|
||||
@@ -136,7 +136,7 @@ link_state: 0=just init, 1=handshake, 2=reconnect, 3=connected.
|
||||
- local_link_id: uint8_t (0-255), назначается отправителем
|
||||
- sock_id: uint8_t, уникальный ID сокета отправителя
|
||||
- only_local: uint8_t, флаг "только локальные подключения"
|
||||
- type: uint8_t, тип сокета (CFG_SERVER_TYPE_PUBLIC/NAT/PRIVATE)
|
||||
- type: uint8_t, тип сокета (CFG_SERVER_TYPE_UNKNOWN=0/PUBLIC=1/NAT=2/PRIVATE=3/LOCAL=4)
|
||||
- random_padding: случайные байты до размера handshake (защита от fingerprinting)
|
||||
- salt + obfuscated_pubkey: публичный ключ с обфускацией (не шифруется AES,
|
||||
но скрывается XOR с salt и собственным публичным ключом). Передаётся в конце пакета
|
||||
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
config.h
|
||||
lib\debug_config.c
|
||||
lib\debug_config.h
|
||||
lib\ll_queue.c
|
||||
lib\ll_queue.h
|
||||
lib\mem.c
|
||||
lib\mem.h
|
||||
lib\memory_pool.c
|
||||
lib\memory_pool.h
|
||||
lib\platform_compat.c
|
||||
lib\platform_compat.h
|
||||
lib\sha256.c
|
||||
lib\sha256.h
|
||||
lib\socket_compat.c
|
||||
lib\socket_compat.h
|
||||
lib\timeout_heap.c
|
||||
lib\timeout_heap.h
|
||||
lib\u_async.c
|
||||
lib\u_async.h
|
||||
lib\wintun.h
|
||||
net_emulator\net_emulator.c
|
||||
net_emulator\net_emulator.h
|
||||
src\config_parser.c
|
||||
src\config_parser.h
|
||||
src\config_updater.c
|
||||
src\config_updater.h
|
||||
src\control_server.c
|
||||
src\control_server.h
|
||||
src\crc32.c
|
||||
src\crc32.h
|
||||
src\dummynet.c
|
||||
src\dummynet.h
|
||||
src\etcp.c
|
||||
src\etcp.h
|
||||
src\etcp_api.c
|
||||
src\etcp_api.h
|
||||
src\etcp_connections.c
|
||||
src\etcp_connections.h
|
||||
src\etcp_debug.c
|
||||
src\etcp_debug.h
|
||||
src\etcp_loadbalancer.c
|
||||
src\etcp_loadbalancer.h
|
||||
src\packet_dump.c
|
||||
src\packet_dump.h
|
||||
src\pkt_normalizer.c
|
||||
src\pkt_normalizer.h
|
||||
src\route_bgp.c
|
||||
src\route_bgp.h
|
||||
src\route_lib.c
|
||||
src\route_lib.h
|
||||
src\routing.c
|
||||
src\routing.h
|
||||
src\secure_channel.c
|
||||
src\secure_channel.h
|
||||
src\tun_if.c
|
||||
src\tun_if.h
|
||||
src\tun_linux.c
|
||||
src\tun_route.c
|
||||
src\tun_route.h
|
||||
src\tun_windows.c
|
||||
src\utun.c
|
||||
src\utun_instance.c
|
||||
src\utun_instance.h
|
||||
tests\bench_timeout_heap.c
|
||||
tests\bench_uasync_timeouts.c
|
||||
tests\debug_full_test.c
|
||||
tests\debug_performance.c
|
||||
tests\debug_simple.c
|
||||
tests\detailed_test.c
|
||||
tests\simple_test.c
|
||||
tests\test_bgp_route_exchange.c
|
||||
tests\test_config_debug.c
|
||||
tests\test_control_server.c
|
||||
tests\test_control_simple.c
|
||||
tests\test_crash_debug.c
|
||||
tests\test_crypto.c
|
||||
tests\test_debug_categories.c
|
||||
tests\test_dummynet.c
|
||||
tests\test_ecc_encrypt.c
|
||||
tests\test_etcp_100_packets.c
|
||||
tests\test_etcp_api.c
|
||||
tests\test_etcp_crypto.c
|
||||
tests\test_etcp_dummynet.c
|
||||
tests\test_etcp_exit.c
|
||||
tests\test_etcp_link_id.c
|
||||
tests\test_etcp_minimal.c
|
||||
tests\test_etcp_simple_traffic.c
|
||||
tests\test_etcp_two_instances.c
|
||||
tests\test_intensive_memory_pool.c
|
||||
tests\test_intensive_memory_pool_new.c
|
||||
tests\test_ll_queue.c
|
||||
tests\test_memory_pool_and_config.c
|
||||
tests\test_minimal.c
|
||||
tests\test_minimal_exit.c
|
||||
tests\test_offset.c
|
||||
tests\test_packet_dump.c
|
||||
tests\test_pkt_normalizer_etcp.c
|
||||
tests\test_pkt_normalizer_standalone.c
|
||||
tests\test_poll_exact.c
|
||||
tests\test_poll_multi.c
|
||||
tests\test_route_lib.c
|
||||
tests\test_routing_mesh.c
|
||||
tests\test_simple.c
|
||||
tests\test_simple2.c
|
||||
tests\test_socket.c
|
||||
tests\test_utils.h
|
||||
tests\test_u_async_comprehensive.c
|
||||
tests\test_u_async_performance.c
|
||||
tests\track_test.c
|
||||
tests\working_crypto_test.c
|
||||
tinycrypt\lib\include\tinycrypt\aes.h
|
||||
tinycrypt\lib\include\tinycrypt\cbc_mode.h
|
||||
tinycrypt\lib\include\tinycrypt\ccm_mode.h
|
||||
tinycrypt\lib\include\tinycrypt\cmac_mode.h
|
||||
tinycrypt\lib\include\tinycrypt\constants.h
|
||||
tinycrypt\lib\include\tinycrypt\ctr_mode.h
|
||||
tinycrypt\lib\include\tinycrypt\ctr_prng.h
|
||||
tinycrypt\lib\include\tinycrypt\ecc.h
|
||||
tinycrypt\lib\include\tinycrypt\ecc_dh.h
|
||||
tinycrypt\lib\include\tinycrypt\ecc_dsa.h
|
||||
tinycrypt\lib\include\tinycrypt\ecc_platform_specific.h
|
||||
tinycrypt\lib\include\tinycrypt\hmac.h
|
||||
tinycrypt\lib\include\tinycrypt\hmac_prng.h
|
||||
tinycrypt\lib\include\tinycrypt\sha256.h
|
||||
tinycrypt\lib\include\tinycrypt\utils.h
|
||||
tinycrypt\lib\source\aes_decrypt.c
|
||||
tinycrypt\lib\source\aes_encrypt.c
|
||||
tinycrypt\lib\source\cbc_mode.c
|
||||
tinycrypt\lib\source\ccm_mode.c
|
||||
tinycrypt\lib\source\cmac_mode.c
|
||||
tinycrypt\lib\source\ctr_mode.c
|
||||
tinycrypt\lib\source\ctr_prng.c
|
||||
tinycrypt\lib\source\ecc.c
|
||||
tinycrypt\lib\source\ecc_dh.c
|
||||
tinycrypt\lib\source\ecc_dsa.c
|
||||
tinycrypt\lib\source\ecc_platform_specific.c
|
||||
tinycrypt\lib\source\hmac.c
|
||||
tinycrypt\lib\source\hmac_prng.c
|
||||
tinycrypt\lib\source\sha256.c
|
||||
tinycrypt\lib\source\utils.c
|
||||
tinycrypt\tests\test_aes.c
|
||||
tinycrypt\tests\test_cbc_mode.c
|
||||
tinycrypt\tests\test_ccm_mode.c
|
||||
tinycrypt\tests\test_client_server.c
|
||||
tinycrypt\tests\test_cmac_mode.c
|
||||
tinycrypt\tests\test_ctr_mode.c
|
||||
tinycrypt\tests\test_ctr_prng.c
|
||||
tinycrypt\tests\test_ecc_dh.c
|
||||
tinycrypt\tests\test_ecc_dsa.c
|
||||
tinycrypt\tests\test_ecc_utils.c
|
||||
tinycrypt\tests\test_hmac.c
|
||||
tinycrypt\tests\test_hmac_prng.c
|
||||
tinycrypt\tests\test_sha256.c
|
||||
tinycrypt\tests\include\test_ecc_utils.h
|
||||
tinycrypt\tests\include\test_utils.h
|
||||
tools\bping\bping.c
|
||||
tools\etcpmon\etcpmon_client.c
|
||||
tools\etcpmon\etcpmon_client.h
|
||||
tools\etcpmon\etcpmon_graph.c
|
||||
tools\etcpmon\etcpmon_graph.h
|
||||
tools\etcpmon\etcpmon_gui.c
|
||||
tools\etcpmon\etcpmon_gui.h
|
||||
tools\etcpmon\etcpmon_main.c
|
||||
tools\etcpmon\etcpmon_protocol.h
|
||||
+25
-28
@@ -128,6 +128,8 @@ static const struct {
|
||||
{"keepalive", DEBUG_CATEGORY_KEEPALIVE},
|
||||
{"etcp_route", DEBUG_CATEGORY_ETCPROUTE},
|
||||
{"bbr", DEBUG_CATEGORY_BBR},
|
||||
{"etcp_dump", DEBUG_CATEGORY_ETCP_DUMP},
|
||||
{"connectivity", DEBUG_CATEGORY_CONNECTIVITY},
|
||||
{"all", DEBUG_CATEGORY_ALL},
|
||||
{NULL, DEBUG_CATEGORY_NONE}
|
||||
};
|
||||
@@ -136,13 +138,14 @@ static const struct {
|
||||
const char* name;
|
||||
debug_level_t level;
|
||||
} g_level_table[] = {
|
||||
{"none", DEBUG_LEVEL_NONE},
|
||||
{"error", DEBUG_LEVEL_ERROR},
|
||||
{"warn", DEBUG_LEVEL_WARN},
|
||||
{"info", DEBUG_LEVEL_INFO},
|
||||
{"debug", DEBUG_LEVEL_DEBUG},
|
||||
{"trace", DEBUG_LEVEL_TRACE},
|
||||
{NULL, DEBUG_LEVEL_NONE}
|
||||
{"disabled", DEBUG_LEVEL_DISABLED},
|
||||
{"none", DEBUG_LEVEL_NONE},
|
||||
{"error", DEBUG_LEVEL_ERROR},
|
||||
{"warn", DEBUG_LEVEL_WARN},
|
||||
{"info", DEBUG_LEVEL_INFO},
|
||||
{"debug", DEBUG_LEVEL_DEBUG},
|
||||
{"trace", DEBUG_LEVEL_TRACE},
|
||||
{NULL, DEBUG_LEVEL_NONE}
|
||||
};
|
||||
|
||||
debug_category_t get_category_by_name(const char* name) {
|
||||
@@ -162,7 +165,7 @@ debug_level_t debug_level_from_name(const char* name) {
|
||||
return g_level_table[i].level;
|
||||
}
|
||||
}
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid debug level '%s' (valid: none,error,warn,info,debug,trace)", name);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Invalid debug level '%s' (valid: disabled,none,error,warn,info,debug,trace)", name);
|
||||
return DEBUG_LEVEL_INFO; /* fallback */
|
||||
}
|
||||
|
||||
@@ -220,15 +223,13 @@ void debug_config_init(void) {
|
||||
|
||||
// Dual output defaults
|
||||
g_debug_config.file_output = NULL; // No file by default
|
||||
g_debug_config.file_level = DEBUG_LEVEL_TRACE; // File gets everything
|
||||
g_debug_config.console_level = DEBUG_LEVEL_INFO; // Console gets INFO+
|
||||
g_debug_config.console_enabled = 1; // Console enabled
|
||||
g_debug_config.thread_marker = 0; // No thread marker by default
|
||||
}
|
||||
|
||||
/* Set debug level */
|
||||
void debug_set_level(debug_level_t level) {
|
||||
g_debug_config.level = level;
|
||||
g_debug_config.console_level = level;
|
||||
}
|
||||
|
||||
/* Set debug level for specific category (0 = disabled, otherwise uses that level) */
|
||||
@@ -280,16 +281,6 @@ void debug_set_output_file(const char* file_path) {
|
||||
}
|
||||
|
||||
/* Set console log level */
|
||||
void debug_set_console_level(debug_level_t level) {
|
||||
g_debug_config.console_level = level;
|
||||
}
|
||||
|
||||
/* Set file log level */
|
||||
void debug_set_file_level(debug_level_t level) {
|
||||
g_debug_config.file_level = level;
|
||||
}
|
||||
|
||||
/* Enable/disable console output */
|
||||
void debug_enable_console(int enable) {
|
||||
g_debug_config.console_enabled = enable;
|
||||
}
|
||||
@@ -343,6 +334,9 @@ int debug_should_output(debug_level_t level, debug_category_t category_idx) {
|
||||
}
|
||||
|
||||
debug_level_t cat_level = g_debug_config.category_levels[category_idx];
|
||||
if (cat_level == DEBUG_LEVEL_DISABLED) {
|
||||
return 0;
|
||||
}
|
||||
if (cat_level == DEBUG_LEVEL_NONE) {
|
||||
cat_level = g_debug_config.level;
|
||||
}
|
||||
@@ -395,6 +389,12 @@ void debug_output(debug_level_t level, debug_category_t category_idx,
|
||||
offset += snprintf(buffer + offset, remaining, "[%s-%03ld.%03ld] ", time_str, tv.tv_usec / 1000, tv.tv_usec % 1000);
|
||||
remaining = BUFFER_SIZE - offset;
|
||||
|
||||
/* Add thread marker if set */
|
||||
if (g_debug_config.thread_marker > 0) {
|
||||
offset += snprintf(buffer + offset, remaining, "[%d] ", g_debug_config.thread_marker);
|
||||
remaining = BUFFER_SIZE - offset;
|
||||
}
|
||||
|
||||
/* Add level */
|
||||
const char* level_name = get_level_name(level);
|
||||
offset += snprintf(buffer + offset, remaining, "[%s] ", level_name);
|
||||
@@ -433,15 +433,12 @@ void debug_output(debug_level_t level, debug_category_t category_idx,
|
||||
va_end(args);
|
||||
|
||||
/* Output to console if enabled and level is sufficient */
|
||||
if (g_debug_config.console_enabled && level <= g_debug_config.console_level) {
|
||||
fprintf(stdout, "%s", buffer);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/* Output to file if open and level is sufficient */
|
||||
if (g_debug_config.file_output && level <= g_debug_config.file_level) {
|
||||
if (g_debug_config.file_output) {
|
||||
fprintf(g_debug_config.file_output, "%s", buffer);
|
||||
fflush(g_debug_config.file_output);
|
||||
} else if (g_debug_config.console_enabled) {
|
||||
fprintf(stdout, "%s", buffer);
|
||||
fflush(stdout);
|
||||
}
|
||||
#undef BUFFER_SIZE
|
||||
}
|
||||
|
||||
+14
-13
@@ -26,12 +26,13 @@ extern "C" {
|
||||
|
||||
/* Debug levels */
|
||||
typedef enum {
|
||||
DEBUG_LEVEL_NONE = 0, // No debug output
|
||||
DEBUG_LEVEL_ERROR = 1, // Errors only
|
||||
DEBUG_LEVEL_WARN = 2, // Warnings and errors
|
||||
DEBUG_LEVEL_INFO = 3, // Info, warnings, errors
|
||||
DEBUG_LEVEL_DEBUG = 4, // Full debug output
|
||||
DEBUG_LEVEL_TRACE = 5 // Trace everything
|
||||
DEBUG_LEVEL_DISABLED = -1, // Category completely disabled, never outputs
|
||||
DEBUG_LEVEL_NONE = 0, // Fallback to global level
|
||||
DEBUG_LEVEL_ERROR = 1, // Errors only
|
||||
DEBUG_LEVEL_WARN = 2, // Warnings and errors
|
||||
DEBUG_LEVEL_INFO = 3, // Info, warnings, errors
|
||||
DEBUG_LEVEL_DEBUG = 4, // Full debug output
|
||||
DEBUG_LEVEL_TRACE = 5 // Trace everything
|
||||
} debug_level_t;
|
||||
|
||||
/* Debug categories as indices (0-based) */
|
||||
@@ -61,7 +62,9 @@ typedef int debug_category_t;
|
||||
#define DEBUG_CATEGORY_KEEPALIVE 21 // Link keepalive logic
|
||||
#define DEBUG_CATEGORY_ETCPROUTE 22 // ETCP routing
|
||||
#define DEBUG_CATEGORY_BBR 23 // BBR congestion control
|
||||
#define DEBUG_CATEGORY_COUNT 24 // Total number of categories
|
||||
#define DEBUG_CATEGORY_ETCP_DUMP 24 // ETCP packet dump
|
||||
#define DEBUG_CATEGORY_CONNECTIVITY 25 // Connection/handshake lifecycle
|
||||
#define DEBUG_CATEGORY_COUNT 26 // Total number of categories
|
||||
#define DEBUG_CATEGORY_ALL (-1) // special value for all categories
|
||||
|
||||
/* Debug configuration structure */
|
||||
@@ -75,9 +78,8 @@ typedef struct {
|
||||
|
||||
// Dual output support
|
||||
FILE* file_output; // File handle for log file (NULL if not open)
|
||||
debug_level_t file_level; // Log level for file output
|
||||
debug_level_t console_level; // Log level for console output
|
||||
int console_enabled; // Enable console output
|
||||
int thread_marker; // Thread identification (0=none, e.g. 2 for uasync)
|
||||
} debug_config_t;
|
||||
|
||||
/* Global debug configuration */
|
||||
@@ -87,6 +89,7 @@ extern debug_config_t g_debug_config;
|
||||
void debug_config_init(void);
|
||||
|
||||
/* Set debug level */
|
||||
// итоговый level = max (global level - здесь задается, category level)
|
||||
void debug_set_level(debug_level_t level);
|
||||
|
||||
/* Set debug level for specific category (0 = disabled, otherwise uses that level) */
|
||||
@@ -106,10 +109,8 @@ void debug_enable_function_name(int enable);
|
||||
void debug_enable_file_line(int enable);
|
||||
void debug_set_output_file(const char* file_path);
|
||||
|
||||
/* Dual output configuration */
|
||||
void debug_set_console_level(debug_level_t level);
|
||||
void debug_set_file_level(debug_level_t level);
|
||||
void debug_enable_file_output(const char* file_path, int truncate);
|
||||
/* Dual output configuration */
|
||||
void debug_enable_file_output(const char* file_path, int truncate);
|
||||
void debug_disable_file_output(void);
|
||||
int debug_reopen_log(void);
|
||||
void debug_enable_console(int enable);
|
||||
|
||||
+8
-4
@@ -130,11 +130,11 @@ void queue_free(struct ll_queue* q) {
|
||||
// ВАЖНО: Не освобождаем элементы в очереди - они должны быть извлечены отдельно
|
||||
// Это упрощает архитектуру и предотвращает double-u_free
|
||||
|
||||
// Освободить список waiters (сбросить handle->internal)
|
||||
// Освободить список waiters (сбросить handle->internal, отменить отложенные вызовы)
|
||||
struct queue_waiter* w = q->waiter_head;
|
||||
while (w) {
|
||||
struct queue_waiter* next = w->next;
|
||||
if (w->handle) w->handle->internal = NULL;
|
||||
if (w->handle) { w->handle->internal = NULL; if (w->handle->call_soon_id) { uasync_call_soon_cancel(q->ua, w->handle->call_soon_id); w->handle->call_soon_id = NULL; } }
|
||||
u_free(w);
|
||||
w = next;
|
||||
}
|
||||
@@ -305,14 +305,17 @@ static uint32_t make_hash(const void* data, uint16_t len) {// алгоритм F
|
||||
|
||||
static void waiter_defer_cb(void* arg) {
|
||||
struct queue_waiter_handle* h = (struct queue_waiter_handle*)arg;
|
||||
h->defer_cb(h->defer_q, h->defer_arg);
|
||||
h->call_soon_id = NULL;
|
||||
if (h->defer_cb) h->defer_cb(h->defer_q, h->defer_arg);
|
||||
else DEBUG_ERROR(DEBUG_CATEGORY_LL_QUEUE, "waiter_defer_cb: NULL callback (stale?) q=%p", (void*)h->defer_q);
|
||||
}
|
||||
|
||||
static inline void queue_waiter_call(struct ll_queue* q, queue_threshold_callback_fn cb, void* arg,
|
||||
struct queue_waiter_handle* h) {
|
||||
if (q->waiter_defer) {
|
||||
if (h->call_soon_id) { DEBUG_WARN(DEBUG_CATEGORY_LL_QUEUE, "queue_waiter_call: overwriting existing call_soon_id=%p", h->call_soon_id); uasync_call_soon_cancel(q->ua, h->call_soon_id); }
|
||||
h->defer_q = q; h->defer_cb = cb; h->defer_arg = arg;
|
||||
uasync_call_soon(q->ua, h, waiter_defer_cb);
|
||||
h->call_soon_id = uasync_call_soon(q->ua, h, waiter_defer_cb);
|
||||
} else {
|
||||
cb(q, arg);
|
||||
}
|
||||
@@ -692,6 +695,7 @@ int queue_waiter_wait(struct ll_queue* q, struct queue_waiter_handle* h,
|
||||
|
||||
void queue_waiter_cancel(struct ll_queue* q, struct queue_waiter_handle* h) {
|
||||
if (!q || !h || !h->internal) return;
|
||||
if (h->call_soon_id) { uasync_call_soon_cancel(q->ua, h->call_soon_id); h->call_soon_id = NULL; }
|
||||
|
||||
struct queue_waiter* waiter = h->internal;
|
||||
struct queue_waiter* prev = NULL;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#ifndef LL_QUEUE_H
|
||||
#define LL_QUEUE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stddef.h> // для size_t
|
||||
#include <stdint.h> // для uint64_t
|
||||
#include "memory_pool.h" // для struct memory_pool
|
||||
@@ -117,6 +122,7 @@ struct queue_waiter_handle {
|
||||
struct ll_queue* defer_q;
|
||||
queue_threshold_callback_fn defer_cb;
|
||||
void* defer_arg;
|
||||
void* call_soon_id; ///< хендл отложенного вызова (для отмены)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -411,4 +417,8 @@ static inline size_t queue_total_bytes(struct ll_queue* q) {
|
||||
return q->total_bytes;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // LL_QUEUE_H
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef MEMORY_POOL_H
|
||||
#define MEMORY_POOL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include "debug_config.h"
|
||||
|
||||
@@ -37,4 +42,8 @@ size_t memory_pool_get_total_free_blocks(void);
|
||||
|
||||
int memory_pool_is_freed(struct memory_pool* pool, void* obj);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // MEMORY_POOL_H
|
||||
@@ -5,6 +5,11 @@
|
||||
#ifndef PLATFORM_COMPAT_H
|
||||
#define PLATFORM_COMPAT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
@@ -30,6 +35,18 @@
|
||||
// POSIX functions missing on Windows
|
||||
#define strcasecmp _stricmp
|
||||
#define strncasecmp _strnicmp
|
||||
|
||||
// memmem — not available on Windows
|
||||
#define memmem compat_memmem
|
||||
static inline void* compat_memmem(const void* h, size_t hl, const void* n, size_t nl) {
|
||||
if (nl == 0) return (void*)h;
|
||||
if (hl < nl) return NULL;
|
||||
const unsigned char* hay = (const unsigned char*)h;
|
||||
const unsigned char* ndl = (const unsigned char*)n;
|
||||
for (size_t i = 0; i <= hl - nl; i++)
|
||||
if (hay[i] == ndl[0] && memcmp(hay + i, ndl, nl) == 0) return (void*)(hay + i);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// poll() and pollfd are already defined in winsock2.h on MSYS2 UCRT64
|
||||
// Only define if not already present
|
||||
@@ -174,4 +191,17 @@ int get_interface_ipv6_by_index(uint32_t netif_index, int temporary, uint8_t* ad
|
||||
// family: AF_INET or AF_INET6, returns ifindex (0 on failure)
|
||||
uint32_t get_default_route_netif_index(int family);
|
||||
|
||||
|
||||
// Cross-platform mkdir
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#define utun_mkdir(path, mode) _mkdir(path)
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#define utun_mkdir(path, mode) mkdir(path, mode)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // PLATFORM_COMPAT_H
|
||||
@@ -32,6 +32,15 @@
|
||||
#ifndef _RADIX_H_
|
||||
#define _RADIX_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "mem.h"
|
||||
|
||||
|
||||
@@ -76,6 +76,11 @@
|
||||
*/
|
||||
#ifndef SERIALIZE_H
|
||||
#define SERIALIZE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../lib/mem.h"
|
||||
@@ -127,4 +132,8 @@ int serialize_decode(const uint8_t* in_buf, size_t in_len,
|
||||
|
||||
void serialize_free(const struct SerializeSchema* schema, void** structure);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // SERIALIZE_H
|
||||
@@ -9,6 +9,11 @@
|
||||
#ifndef SC_SHA256_H
|
||||
#define SC_SHA256_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*************************** HEADER FILES ***************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -29,4 +34,8 @@ void sc_sha256_init(SC_SHA256_CTX *ctx);
|
||||
void sc_sha256_update(SC_SHA256_CTX *ctx, const uint8_t data[], size_t len);
|
||||
void sc_sha256_final(SC_SHA256_CTX *ctx, uint8_t hash[]);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // SC_SHA256_H
|
||||
@@ -89,6 +89,7 @@ int socket_set_nonblocking(socket_t sock) {
|
||||
}
|
||||
|
||||
int socket_close_wrapper(socket_t sock) {
|
||||
if (sock == 0) return 0; // never close fd 0
|
||||
return close(sock);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
|
||||
#ifndef SOCKET_COMPAT_H
|
||||
#define SOCKET_COMPAT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -73,4 +78,8 @@ const char* socket_strerror(int err);
|
||||
// взять порт из sockaddr_storage
|
||||
uint16_t ss_get_port(const struct sockaddr_storage *addr);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // SOCKET_COMPAT_H
|
||||
+34
-16
@@ -13,6 +13,9 @@
|
||||
#ifndef MSG_NOSIGNAL
|
||||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#define SHUT_WR SD_SEND
|
||||
#endif
|
||||
|
||||
static void read_cb(socket_t sock, void* arg);
|
||||
static void write_cb(socket_t sock, void* arg);
|
||||
@@ -32,7 +35,7 @@ struct tcp_conn* tcp_conn_create(
|
||||
void (*on_error)(struct tcp_conn* tc, int err, void* arg),
|
||||
void* arg)
|
||||
{
|
||||
if (!ua || sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_conn_create: invalid args ua=%p", ua); return NULL; }
|
||||
if (!ua || sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_conn_create: invalid args ua=%p", (void*)ua); return NULL; }
|
||||
|
||||
struct tcp_conn* tc = u_calloc(1, sizeof(struct tcp_conn));
|
||||
if (!tc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_conn_create: u_calloc failed"); return NULL; }
|
||||
@@ -104,8 +107,17 @@ struct tcp_conn* tcp_conn_create(
|
||||
return tc;
|
||||
}
|
||||
|
||||
static void tcp_conn_deferred_free(void* arg) {
|
||||
struct tcp_conn* tc = (struct tcp_conn*)arg;
|
||||
if (tc->entry_pool) memory_pool_destroy(tc->entry_pool);
|
||||
if (tc->data_pool) memory_pool_destroy(tc->data_pool);
|
||||
u_free(tc);
|
||||
}
|
||||
|
||||
void tcp_conn_destroy(struct tcp_conn* tc) {
|
||||
if (!tc) return;
|
||||
if (tc->destroyed) return;
|
||||
tc->destroyed = 1;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "tcp_conn_destroy: fd=%d connected=%d error=%d fin_remote=%d fin_local=%d closed=%d",
|
||||
(int)tc->sock, tc->connected, tc->error, tc->fin_remote, tc->fin_local, tc->closed);
|
||||
|
||||
@@ -127,14 +139,12 @@ void tcp_conn_destroy(struct tcp_conn* tc) {
|
||||
if (e->dgram && e->dgram != &tcp_fin_sentinel) memory_pool_free(tc->data_pool, e->dgram);
|
||||
queue_entry_free(e); queue_resume_callback(tc->write_queue);
|
||||
}
|
||||
queue_free(tc->read_queue);
|
||||
queue_free(tc->write_queue);
|
||||
queue_free(tc->read_queue); tc->read_queue = NULL;
|
||||
queue_free(tc->write_queue); tc->write_queue = NULL;
|
||||
|
||||
if (tc->write_buf) memory_pool_free(tc->data_pool, tc->write_buf);
|
||||
if (tc->sock != SOCKET_INVALID) socket_close_wrapper(tc->sock);
|
||||
memory_pool_destroy(tc->entry_pool);
|
||||
memory_pool_destroy(tc->data_pool);
|
||||
u_free(tc);
|
||||
if (tc->sock != SOCKET_INVALID) { socket_close_wrapper(tc->sock); tc->sock = SOCKET_INVALID; }
|
||||
uasync_call_soon(tc->ua, tc, tcp_conn_deferred_free);
|
||||
}
|
||||
|
||||
static void tcp_conn_handle_error(struct tcp_conn* tc, int err)
|
||||
@@ -157,13 +167,15 @@ static void tcp_conn_handle_error(struct tcp_conn* tc, int err)
|
||||
}
|
||||
}
|
||||
|
||||
if (tc->socket_id) {
|
||||
if (tc->sock != SOCKET_INVALID) {
|
||||
uasync_remove_socket_t(tc->ua, tc->sock);
|
||||
tc->socket_id = NULL;
|
||||
}
|
||||
if (tc->sock != SOCKET_INVALID) {
|
||||
#if HAS_EPOLL
|
||||
if (tc->ua && tc->ua->use_epoll && tc->ua->epoll_fd >= 0)
|
||||
epoll_ctl(tc->ua->epoll_fd, EPOLL_CTL_DEL, (int)tc->sock, NULL);
|
||||
#endif
|
||||
socket_close_wrapper(tc->sock);
|
||||
tc->sock = SOCKET_INVALID;
|
||||
tc->sock = SOCKET_INVALID; // ДО on_error: read/write в том же epoll event увидят INVALID
|
||||
}
|
||||
|
||||
queue_set_callback(tc->write_queue, NULL, NULL);
|
||||
@@ -183,7 +195,7 @@ static void tcp_conn_handle_error(struct tcp_conn* tc, int err)
|
||||
static void read_cb(socket_t sock, void* arg) {
|
||||
(void)sock;
|
||||
struct tcp_conn* tc = (struct tcp_conn*)arg;
|
||||
if (!tc || tc->sock == SOCKET_INVALID) return;
|
||||
if (!tc || tc->sock == SOCKET_INVALID || !tc->read_queue) return;
|
||||
|
||||
struct ll_entry* e = queue_entry_new_from_pool(tc->entry_pool);
|
||||
uint8_t* buf = memory_pool_alloc(tc->data_pool);
|
||||
@@ -200,8 +212,10 @@ static void read_cb(socket_t sock, void* arg) {
|
||||
if (n > 0) {
|
||||
e->dgram = buf;
|
||||
e->len = (uint16_t)n;
|
||||
queue_data_put(tc->read_queue, e);
|
||||
if (tc->read_queue->count >= tc->read_high_water && !tc->read_paused) {
|
||||
struct ll_queue* rq = tc->read_queue;
|
||||
queue_data_put(rq, e);
|
||||
// tc мог быть уничтожен через callback chain — read_queue обнулён
|
||||
if (rq && tc->read_queue && tc->read_queue->count >= tc->read_high_water && !tc->read_paused) {
|
||||
uasync_set_socket_read(tc->ua, tc->socket_id, 0);
|
||||
tc->read_paused = 1;
|
||||
queue_waiter_wait(tc->read_queue, &tc->read_waiter, resume_read_cb, tc);
|
||||
@@ -366,12 +380,12 @@ static void write_queue_fetch_cb(struct ll_queue* q, void* arg) {
|
||||
static void write_cb(socket_t sock, void* arg) {
|
||||
(void)sock;
|
||||
struct tcp_conn* tc = (struct tcp_conn*)arg;
|
||||
if (!tc || tc->sock == SOCKET_INVALID) return;
|
||||
if (!tc || tc->sock == SOCKET_INVALID || !tc->write_queue) return;
|
||||
|
||||
if (!tc->connected) {
|
||||
int err = 0;
|
||||
socklen_t len = sizeof(err);
|
||||
if (getsockopt(tc->sock, SOL_SOCKET, SO_ERROR, &err, &len) == 0 && err == 0) {
|
||||
if (getsockopt(tc->sock, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0 && err == 0) {
|
||||
tc->connected = 1;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "tcp_io: connect ok fd=%d", (int)tc->sock);
|
||||
} else {
|
||||
@@ -384,6 +398,10 @@ static void write_cb(socket_t sock, void* arg) {
|
||||
if (flush_write_buf(tc)) return;
|
||||
if (!tc->write_buf) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_SOCKET, "tcp_io: write_cb flushed fd=%d wmon=%d (resume WQ cb)", (int)tc->sock, tc->write_monitor);
|
||||
if (tc->write_queue->waiter_defer) {
|
||||
uasync_set_socket_write(tc->ua, tc->socket_id, 0);
|
||||
tc->write_monitor = 0;
|
||||
}
|
||||
queue_resume_callback(tc->write_queue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@
|
||||
#ifndef TCP_IO_H
|
||||
#define TCP_IO_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include "u_async.h"
|
||||
#include "socket_compat.h"
|
||||
#include "ll_queue.h"
|
||||
@@ -46,6 +51,7 @@ struct tcp_conn {
|
||||
uint8_t fin_remote; // FIN получен от удалённой стороны (recv == 0)
|
||||
uint8_t fin_local; // FIN отправлен удалённой стороне (shutdown SHUT_WR)
|
||||
uint8_t closed; // сокет полностью закрыт (close)
|
||||
uint8_t destroyed; // 1 = tcp_conn_destroy вызван, tc ожидает отложенного free
|
||||
|
||||
// Частичная отправка (из data_pool, не в очереди — досылается первой)
|
||||
uint8_t* write_buf;
|
||||
@@ -107,4 +113,8 @@ void tcp_conn_set_flushed(struct tcp_conn* tc, void (*on_flushed)(struct tcp_con
|
||||
// Принудительная остановка чтения: убирает EPOLLIN и отменяет read_waiter.
|
||||
void tcp_conn_pause_read(struct tcp_conn* tc);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -3,6 +3,11 @@
|
||||
#ifndef TIMEOUT_HEAP_H
|
||||
#define TIMEOUT_HEAP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h> // For uint64_t
|
||||
#include <stddef.h> // For size_t
|
||||
|
||||
@@ -94,4 +99,8 @@ size_t timeout_heap_get_freed_count(TimeoutHeap *h);
|
||||
|
||||
size_t timeout_heap_get_size(TimeoutHeap *h);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // TIMEOUT_HEAP_H
|
||||
+612
-603
File diff suppressed because it is too large.
Load diff
+79
-70
@@ -5,18 +5,23 @@
|
||||
#ifndef UASYNC_H
|
||||
#define UASYNC_H
|
||||
|
||||
#include "platform_compat.h"
|
||||
#include <stddef.h>
|
||||
#include <signal.h>
|
||||
#include "timeout_heap.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include "platform_compat.h"
|
||||
#include <stddef.h>
|
||||
#include <signal.h>
|
||||
#include "timeout_heap.h"
|
||||
#include "socket_compat.h"
|
||||
|
||||
typedef void (*timeout_callback_t)(void* user_arg);// передаёт user_arg из uasync_set_timeout
|
||||
typedef void (*socket_callback_t)(int fd, void* user_arg);// передаёт user_arg из uasync_add_socket (for pipe/file)
|
||||
typedef void (*socket_t_callback_t)(socket_t sock, void* user_arg);// передаёт user_arg из uasync_add_socket_t (for sockets)
|
||||
// user_arg полезен если нужно передать управляющую структуру. Ее можно выделить в памяти и в ней хранить всё что надо. т.е. при set_timeout передаём и получаем ее в callback-е
|
||||
|
||||
|
||||
|
||||
|
||||
// Error type
|
||||
typedef int err_t;
|
||||
#define ERR_OK 0
|
||||
@@ -28,21 +33,21 @@ typedef int err_t;
|
||||
|
||||
typedef void (*uasync_post_callback_t)(void* user_arg);
|
||||
|
||||
#include "memory_pool.h"
|
||||
|
||||
struct timeout_node; // Forward declaration
|
||||
|
||||
#include "memory_pool.h"
|
||||
|
||||
struct timeout_node; // Forward declaration
|
||||
|
||||
struct posted_task {
|
||||
uasync_post_callback_t callback;
|
||||
void* arg;
|
||||
struct posted_task* next;
|
||||
};
|
||||
|
||||
// Uasync instance structure
|
||||
struct UASYNC {
|
||||
struct memory_pool* timeout_pool; // Pool for timeout_node allocation
|
||||
struct timeout_node* immediate_queue_head; // FIFO queue for immediate execution
|
||||
struct timeout_node* immediate_queue_tail;
|
||||
// Uasync instance structure
|
||||
struct UASYNC {
|
||||
struct memory_pool* timeout_pool; // Pool for timeout_node allocation
|
||||
struct timeout_node* immediate_queue_head; // FIFO queue for immediate execution
|
||||
struct timeout_node* immediate_queue_tail;
|
||||
TimeoutHeap* timeout_heap; // Heap for timeout management
|
||||
struct socket_array* sockets; // Array-based socket management
|
||||
// Debug counters for memory allocation tracking
|
||||
@@ -67,55 +72,55 @@ struct UASYNC {
|
||||
#ifdef _WIN32
|
||||
CRITICAL_SECTION posted_lock;
|
||||
#else
|
||||
pthread_mutex_t posted_lock;
|
||||
#endif
|
||||
volatile sig_atomic_t stop;
|
||||
pthread_mutex_t posted_lock;
|
||||
#endif
|
||||
volatile sig_atomic_t stop;
|
||||
};
|
||||
|
||||
// Type definitions
|
||||
typedef struct UASYNC uasync_t;
|
||||
typedef struct UASYNC UASYNC_t;
|
||||
|
||||
// Instance API - основной API для работы с uasync
|
||||
struct UASYNC* uasync_create(void);
|
||||
void uasync_destroy(struct UASYNC* ua, int close_fds);
|
||||
|
||||
// текущее время (timebase 0.1ms)
|
||||
uint64_t get_time_tb(void);
|
||||
// текущее время в микросекундах (для burst-измерений)
|
||||
uint64_t get_time_us(void);
|
||||
|
||||
// Timeouts, timebase = 0.1 mS
|
||||
// Как работать с таймаутами если таймаут может быть использован несколько раз:
|
||||
// 1. заводим дескриптор таймаута и обнуляем
|
||||
// 2. перед активацией таймаута проверяем дескриптор (t_id) на null
|
||||
// 3. при cancel или срабатывании дескриптор обнуляем
|
||||
// это обеспечит отсутствие утечек и задвоений таймаутов
|
||||
void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* user_arg, timeout_callback_t callback, const char* name);
|
||||
err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id);
|
||||
|
||||
// Immediate execution in next mainloop (FIFO order)
|
||||
void* uasync_call_soon(struct UASYNC* ua, void* user_arg, timeout_callback_t callback);
|
||||
err_t uasync_call_soon_cancel(struct UASYNC* ua, void* t_id);
|
||||
|
||||
|
||||
// Type definitions
|
||||
typedef struct UASYNC uasync_t;
|
||||
typedef struct UASYNC UASYNC_t;
|
||||
|
||||
// Instance API - основной API для работы с uasync
|
||||
struct UASYNC* uasync_create(void);
|
||||
void uasync_destroy(struct UASYNC* ua, int close_fds);
|
||||
|
||||
// текущее время (timebase 0.1ms)
|
||||
uint64_t get_time_tb(void);
|
||||
// текущее время в микросекундах (для burst-измерений)
|
||||
uint64_t get_time_us(void);
|
||||
|
||||
// Timeouts, timebase = 0.1 mS
|
||||
// Как работать с таймаутами если таймаут может быть использован несколько раз:
|
||||
// 1. заводим дескриптор таймаута и обнуляем
|
||||
// 2. перед активацией таймаута проверяем дескриптор (t_id) на null
|
||||
// 3. при cancel или срабатывании дескриптор обнуляем
|
||||
// это обеспечит отсутствие утечек и задвоений таймаутов
|
||||
void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* user_arg, timeout_callback_t callback, const char* name);
|
||||
err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id);
|
||||
|
||||
// Immediate execution in next mainloop (FIFO order)
|
||||
void* uasync_call_soon(struct UASYNC* ua, void* user_arg, timeout_callback_t callback);
|
||||
err_t uasync_call_soon_cancel(struct UASYNC* ua, void* t_id);
|
||||
|
||||
// Sockets - for regular file descriptors (pipe, file)
|
||||
void* uasync_add_socket(struct UASYNC* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_arg);
|
||||
// Sockets - for socket_t (cross-platform sockets)
|
||||
void* uasync_add_socket_t(struct UASYNC* ua, socket_t sock, socket_t_callback_t read_cbk, socket_t_callback_t write_cbk, socket_t_callback_t except_cbk, void* user_arg);
|
||||
err_t uasync_remove_socket(struct UASYNC* ua, void* s_id);
|
||||
err_t uasync_remove_socket_t(struct UASYNC* ua, socket_t sock);
|
||||
|
||||
// Динамическое управление мониторингом чтения/записи для сокета
|
||||
err_t uasync_set_socket_read(struct UASYNC* ua, void* s_id, int enable);
|
||||
err_t uasync_remove_socket(struct UASYNC* ua, void* s_id);
|
||||
err_t uasync_remove_socket_t(struct UASYNC* ua, socket_t sock);
|
||||
|
||||
// Динамическое управление мониторингом чтения/записи для сокета
|
||||
err_t uasync_set_socket_read(struct UASYNC* ua, void* s_id, int enable);
|
||||
err_t uasync_set_socket_write(struct UASYNC* ua, void* s_id, int enable);
|
||||
|
||||
// Single iteration of event loop with timeout (timebase units)
|
||||
void uasync_poll(struct UASYNC* ua, int timeout_tb);
|
||||
|
||||
// Mainloop (бесконечный цикл, __noreturn)
|
||||
void uasync_mainloop(struct UASYNC* ua);
|
||||
void uasync_stop(struct UASYNC* ua);
|
||||
|
||||
|
||||
// Single iteration of event loop with timeout (timebase units)
|
||||
void uasync_poll(struct UASYNC* ua, int timeout_tb);
|
||||
|
||||
// Mainloop (бесконечный цикл, __noreturn)
|
||||
void uasync_mainloop(struct UASYNC* ua);
|
||||
void uasync_stop(struct UASYNC* ua);
|
||||
|
||||
// Debug statistics
|
||||
void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free);
|
||||
|
||||
@@ -124,15 +129,19 @@ int uasync_lookup_socket(struct UASYNC* ua, int fd, void** socket_id);
|
||||
|
||||
// Print all resources (timers, sockets) for debugging
|
||||
void uasync_print_resources(struct UASYNC* ua, const char* prefix);
|
||||
|
||||
// Wakeup mechanism for interrupting poll
|
||||
int uasync_wakeup(struct UASYNC* ua);
|
||||
int uasync_get_wakeup_fd(struct UASYNC* ua); // returns write fd for wakeup pipe (for signal handlers)
|
||||
|
||||
// сообщить async (из другого thread) чтобы он вызвал callback с аргументом
|
||||
void uasync_post(struct UASYNC* ua, uasync_post_callback_t callback, void* user_arg);
|
||||
|
||||
// синхронизация памяти (для доступности из других потоков)
|
||||
void uasync_memsync(struct UASYNC* ua);
|
||||
|
||||
#endif // UASYNC_H
|
||||
|
||||
// Wakeup mechanism for interrupting poll
|
||||
int uasync_wakeup(struct UASYNC* ua);
|
||||
int uasync_get_wakeup_fd(struct UASYNC* ua); // returns write fd for wakeup pipe (for signal handlers)
|
||||
|
||||
// сообщить async (из другого thread) чтобы он вызвал callback с аргументом
|
||||
void uasync_post(struct UASYNC* ua, uasync_post_callback_t callback, void* user_arg);
|
||||
|
||||
// синхронизация памяти (для доступности из других потоков)
|
||||
void uasync_memsync(struct UASYNC* ua);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // UASYNC_H
|
||||
@@ -1,219 +0,0 @@
|
||||
Диаграмма вызовов функций при определении типа NAT (NAT detection)
|
||||
=====================================================================
|
||||
|
||||
=== ИНИЦИАЛИЗАЦИЯ BGP ===
|
||||
|
||||
route_bgp_init(instance)
|
||||
└── etcp_set_new_conn_cbk(instance, route_bgp_etcp_conn_cbk, NULL)
|
||||
└── При создании нового ETCP_CONN:
|
||||
route_bgp_etcp_conn_cbk(conn, arg)
|
||||
├── etcp_conn_set_up_cbk(conn, route_bgp_on_conn_up, bgp)
|
||||
└── etcp_conn_set_down_cbk(conn, route_bgp_on_conn_down, bgp)
|
||||
|
||||
|
||||
=== УСТАНОВКА СОЕДИНЕНИЯ (сервер получает INIT_REQUEST) ===
|
||||
|
||||
etcp_connections_read_callback_socket(sock, arg)
|
||||
└── Получает ETCP_INIT_REQUEST
|
||||
├── etcp_connection_create(instance, name) [если новый peer]
|
||||
│ └── Вызывает instance->etcp_new_conn_cbk [route_bgp_etcp_conn_cbk]
|
||||
├── etcp_link_new(etcp, e_sock, remote_addr, is_server=1)
|
||||
├── Отправляет ETCP_INIT_RESPONSE (с NAT info)
|
||||
├── link->initialized = 1
|
||||
└── etcp_conn_ready(link->etcp)
|
||||
└── conn->ready_cbk
|
||||
└── etcp_on_up(etcp)
|
||||
└── etcp->up_cbk = route_bgp_on_conn_up(etcp, bgp)
|
||||
|
||||
|
||||
=== УСТАНОВКА СОЕДИНЕНИЯ (клиент получает INIT_RESPONSE) ===
|
||||
|
||||
etcp_connections_read_callback_socket(sock, arg)
|
||||
└── Получает ETCP_INIT_RESPONSE
|
||||
├── Сохраняет NAT address: link->nat_ip, link->nat_port
|
||||
├── link->initialized = 1
|
||||
└── etcp_conn_ready(link->etcp)
|
||||
└── etcp_on_up(etcp)
|
||||
└── etcp->up_cbk = route_bgp_on_conn_up(etcp, bgp)
|
||||
|
||||
|
||||
=== ЗАПУСК ПРОВЕРКИ NAT (при поднятии соединения) ===
|
||||
|
||||
route_bgp_on_conn_up(etcp, arg)
|
||||
└── route_bgp_new_conn(conn)
|
||||
├── route_bgp_add_to_senders(bgp, conn)
|
||||
└── Сканирует ВСЕ линки ВСЕХ соединений:
|
||||
if (link->initialized && link->conn && link->nat_check_status < NAT_CHECK_IN_PROGRESS)
|
||||
└── route_bgp_start_link_nat_check(bgp, link)
|
||||
├── if (nat_check_status == IN_PROGRESS) return
|
||||
├── route_bgp_find_third_node(bgp, exclude=link->etcp)
|
||||
│ └── Ищет другой ETCP_CONN в senders_list
|
||||
├── Определяет target_ip/target_port:
|
||||
│ ├── Если link->nat_ip != 0: использует nat_ip/nat_port
|
||||
│ └── Иначе: использует link->remote_addr
|
||||
├── Проверяет is_local_subnet(target_ip) [если запрещено - skip]
|
||||
├── route_bgp_extract_nat_addr(conn, &nat_ip, &nat_port)
|
||||
│ └── Ищет первый линк с nat_ip != 0
|
||||
├── Создает nat_check_arg { link, nat_ip, nat_port }
|
||||
└── route_ping_send_req_addr(bgp, third_conn, target_ip, target_port,
|
||||
count=3, interval=500ms, timeout=1000ms,
|
||||
wait_timeout=5000ms,
|
||||
cb=nat_link_check_cb, arg=nat_check_arg,
|
||||
pubkey=peer_pubkey)
|
||||
|
||||
|
||||
=== ОТПРАВКА ЗАПРОСА НА ПИНГ (через third node) ===
|
||||
|
||||
route_ping_send_req_addr(bgp, to_conn, target_ip, target_port, ...)
|
||||
├── Создает BGP_PING_REQUEST { cmd, subcmd=PING_REQ, request_id, count, interval, timeout, target_ipv4, target_port, pubkey }
|
||||
├── etcp_send(to_conn, entry) [отправляет запрос third node]
|
||||
├── Создает route_ping_pending { request_id, callback=nat_link_check_cb, arg, timeout_timer }
|
||||
└── Добавляет в bgp->ping_pending
|
||||
|
||||
|
||||
=== ОБРАБОТКА PING_REQ (на third node) ===
|
||||
|
||||
route_bgp_receive_cbk(data, len, from_conn, bgp)
|
||||
└── ROUTE_SUBCMD_PING_REQ
|
||||
└── route_ping_handle_req(bgp, from_conn, data, len)
|
||||
├── Создает route_ping_series_ctx { reply_conn, request_id, target_addr, pubkey, local_sock, count_total }
|
||||
├── Находит первый IPv4 ETCP_SOCKET -> local_sock
|
||||
└── etcp_send_ping_to_socket(instance, local_sock, pubkey, &target_addr,
|
||||
timeout_ms, route_ping_single_cb, ctx, NULL, 0)
|
||||
└── Отправляет ETCP пинг на target_addr
|
||||
|
||||
|
||||
=== ОТВЕТ НА ПИНГ (целевой узел) ===
|
||||
|
||||
etcp_connections_read_callback_socket()
|
||||
└── Получает ETCP пинг -> отправляет pong
|
||||
|
||||
|
||||
=== ПОЛУЧЕНИЕ ОТВЕТА НА ПИНГ (third node) ===
|
||||
|
||||
route_ping_single_cb(success, rtt, arg, nonce, resp_data, resp_data_len)
|
||||
├── Обновляет статистику: count_sent++, count_ok++, sum_rtt += rtt
|
||||
├── Если count_sent < count_total:
|
||||
│ └── etcp_send_ping_to_socket(...) [следующий пинг]
|
||||
└── Иначе:
|
||||
└── route_ping_series_finish(ctx)
|
||||
├── Вычисляет avg_rtt
|
||||
└── Создает BGP_PING_RESPONSE { cmd, subcmd=PING_RESP, request_id, count_sent, count_ok, avg_rtt }
|
||||
└── etcp_send(reply_conn, entry) [отправляет результат инициатору]
|
||||
|
||||
|
||||
=== ПОЛУЧЕНИЕ PING_RESP (на инициаторе) ===
|
||||
|
||||
route_bgp_receive_cbk(data, len, from_conn, bgp)
|
||||
└── ROUTE_SUBCMD_PING_RESP
|
||||
└── route_ping_handle_resp(bgp, from_conn, data, len)
|
||||
├── Ищет route_ping_pending по request_id
|
||||
├── Отменяет timeout_timer
|
||||
└── Вызывает callback: nat_link_check_cb(success, avg_rtt, count_sent, count_ok, arg)
|
||||
|
||||
|
||||
=== CALLBACK ПРОВЕРКИ NAT ===
|
||||
|
||||
nat_link_check_cb(success, avg_rtt, count_sent, count_ok, arg)
|
||||
├── Определяет nat_type = success ? NAT_TYPE_OPEN : NAT_TYPE_RESTRICTED
|
||||
├── link->nat_type = nat_type
|
||||
├── link->nat_check_status = success ? NAT_CHECK_OPEN : NAT_CHECK_RESTRICTED
|
||||
└── route_bgp_send_nat_info(link->etcp, socket_id, nat_ip, nat_port, nat_type)
|
||||
├── Создает BGP_NAT_INFO { cmd, subcmd=NAT_INFO, socket_id, nat_ip[4], nat_port, nat_type }
|
||||
└── etcp_send(conn, entry) [отправляет результат клиенту]
|
||||
|
||||
|
||||
=== ПРИЕМ NAT_INFO (на клиенте) ===
|
||||
|
||||
route_bgp_receive_cbk(data, len, from_conn, bgp)
|
||||
└── ROUTE_SUBCMD_NAT_INFO
|
||||
└── route_bgp_handle_nat_info(bgp, from_conn, data, len)
|
||||
├── Ищет линки по remote_socket_id
|
||||
├── Устанавливает link->nat_type = info->nat_type
|
||||
└── Логирует результат NAT detection
|
||||
|
||||
|
||||
=== ЗАПРОС НА ПРОВЕРКУ NAT (NAT_CHECK_REQ - новый механизм) ===
|
||||
|
||||
route_bgp_send_nat_check_req(conn, socket_id)
|
||||
├── Создает BGP_NAT_CHECK_REQ { cmd, subcmd=NAT_CHECK_REQ, socket_id, interface_ip, interface_port }
|
||||
│ ├── Ищет линк с remote_socket_id == socket_id
|
||||
│ └── Берет interface_addr из линка -> interface_ip/interface_port
|
||||
└── etcp_send(conn, entry)
|
||||
|
||||
route_bgp_handle_nat_check_req(bgp, from_conn, data, len)
|
||||
├── Принимает BGP_NAT_CHECK_REQ
|
||||
├── Сохраняет interface_ip/interface_port в struct sockaddr_storage dummy
|
||||
├── Ищет линк с remote_socket_id == req->socket_id
|
||||
└── Если линк найден и не в процессе проверки:
|
||||
└── route_bgp_start_link_nat_check(bgp, target_link)
|
||||
[далее по стандартному flow]
|
||||
|
||||
|
||||
=== СВЯЗЬ СТРУКТУР ===
|
||||
|
||||
struct ETCP_SOCKET
|
||||
├── local_addr [адрес бинда из конфига]
|
||||
├── interface_addr [IP интерфейса + порт: интерфейс/default route/конфиг]
|
||||
└── ...
|
||||
|
||||
struct ETCP_LINK
|
||||
├── remote_addr [адрес удаленного узла]
|
||||
├── nat_ip [NAT IP, полученный из INIT_RESPONSE]
|
||||
├── nat_port [NAT port, полученный из INIT_RESPONSE]
|
||||
├── nat_type [OPEN/RESTRICTED, результат проверки]
|
||||
├── nat_check_status [NONE/WAITING/IN_PROGRESS/OPEN/RESTRICTED]
|
||||
├── initialized [0/1, флаг завершения handshake]
|
||||
└── ...
|
||||
|
||||
struct ETCP_CONN
|
||||
├── links [список ETCP_LINK]
|
||||
├── up_cbk [route_bgp_on_conn_up]
|
||||
├── down_cbk [route_bgp_on_conn_down]
|
||||
├── ready_cbk [вызывается при etcp_conn_ready]
|
||||
├── initialized [0/1, флаг готовности]
|
||||
├── links_up [0/1, есть ли активные линки]
|
||||
└── ...
|
||||
|
||||
|
||||
=== СТАТУСЫ NAT CHECK ===
|
||||
|
||||
NAT_CHECK_NONE = 0 [не проверялся]
|
||||
NAT_CHECK_WAITING = 1 [ожидает]
|
||||
NAT_CHECK_IN_PROGRESS = 2 [проверка запущена]
|
||||
NAT_CHECK_OPEN = 3 [результат: OPEN]
|
||||
NAT_CHECK_RESTRICTED = 4 [результат: RESTRICTED]
|
||||
|
||||
NAT_TYPE_UNKNOWN = 0
|
||||
NAT_TYPE_OPEN = 1
|
||||
NAT_TYPE_RESTRICTED = 2
|
||||
|
||||
NAT_VERIFIED_UNKNOWN = 4
|
||||
NAT_VERIFIED_OPEN = 5
|
||||
NAT_VERIFIED_RESTRICTED = 6
|
||||
NAT_VERIFIED_DIRECT = 7
|
||||
|
||||
|
||||
=== СООБЩЕНИЯ ПРОТОКОЛА ===
|
||||
|
||||
ETCP_INIT_REQUEST (0x02) [клиент -> сервер]
|
||||
ETCP_INIT_RESPONSE (0x03) [сервер -> клиент, включает NAT info]
|
||||
ETCP_INIT_REQUEST_NOINIT (0x04)
|
||||
ETCP_INIT_RESPONSE_NOINIT (0x05)
|
||||
ETCP_PING (0x06)
|
||||
ETCP_PONG (0x07)
|
||||
|
||||
ROUTE_SUBCMD_NODEINFO (0x04)
|
||||
ROUTE_SUBCMD_REQUEST_TABLE (0x05)
|
||||
ROUTE_SUBCMD_WITHDRAW (0x06)
|
||||
ROUTE_SUBCMD_PING_REQ (0x07)
|
||||
ROUTE_SUBCMD_PING_RESP (0x08)
|
||||
ROUTE_SUBCMD_NAT_INFO (0x09)
|
||||
ROUTE_SUBCMD_NAT_CHECK_REQ (0x0A)
|
||||
|
||||
|
||||
=== ПОЛНЫЙ FLOW (одна строка) ===
|
||||
|
||||
Handshake: etcp_connections_read_callback_socket -> INIT_REQUEST/RESPONSE -> link->initialized=1 -> etcp_conn_ready -> etcp_on_up -> route_bgp_on_conn_up -> route_bgp_new_conn -> route_bgp_start_link_nat_check -> route_ping_send_req_addr -> (через ETCP) -> route_ping_handle_req -> etcp_send_ping_to_socket -> (UDP) -> route_ping_single_cb -> route_ping_series_finish -> route_ping_handle_resp -> nat_link_check_cb -> route_bgp_send_nat_info -> route_bgp_handle_nat_info
|
||||
|
||||
NAT_CHECK_REQ flow: route_bgp_send_nat_check_req -> BGP_NAT_CHECK_REQ -> route_bgp_handle_nat_check_req -> route_bgp_start_link_nat_check -> [стандартный ping flow]
|
||||
@@ -0,0 +1,29 @@
|
||||
UTUN
|
||||
|
||||
Идея: объединить узлы в единую локальную сеть.
|
||||
Чтобы узлы сами находили оптимальные линки между собой, пробивали NAT где это можно, где нельзя - выбирали посредника с хорошей связью; чтобы подсто было добавлять новые узлы и адреса узлов сразу виделись во всей сети.
|
||||
|
||||
Похож на libp2p но трафик превращает в случайную последовательность кторую сложно классифицировать
|
||||
|
||||
Потенциальные варианты использования:
|
||||
- локальная сеть. хочу объединить офис, сотрудников (включая их работчие подсети), телефоны, офисы, сервер доступа итд в одно адресное пространство.
|
||||
- чат с файлообменником. узлы сети - это мемберы группы и одновременно сети.
|
||||
- CDN. узлы - потребители контента - это мемберы сети, которые распространяют трафик.
|
||||
|
||||
То есть задача utun - обеспечить связность группы узлов и адаптацию работы всей сети к подключению-отключении отдельных узлов.
|
||||
|
||||
Механизмы:
|
||||
1. Пробитие nat.
|
||||
2. автообновление списка узлов и их адресов для подключения
|
||||
3. шифрование трафика и маскировка handshake.
|
||||
handshake просиходит без сигнатур по которым можно опреедлить тип сервиса, весь трафик выглядлит как случайный набор байт случайного размера
|
||||
4. многоканальные подключения, агрегирование каналов, исключение мёртвых каналов
|
||||
подключиться к узлу можно через несколько линков. UTUN будет объединять пропускную способность каналов, наблюдая за состоянием каналов, не допуская перегрузки и обеспечивая быстрые переповторы через другие каналы при потерях или большой задержке.
|
||||
5. динамическая маршрутизация. Узлы могут назначать себе IP адреса и при подключении к сети анонсировать свои адреса/подсети.
|
||||
это максимально упрощает добавление нового узла в сеть.
|
||||
6. автоматический роутинг между узлами: узлы маршрутизируют трафик между собой.
|
||||
используя статические подключения, прописанные в конфиге.
|
||||
7. прямые подключения и поиск лучших подключений
|
||||
узлы могут подключаться друг к другу напрямую, либо если оба узла за строгим NAT - выбирать оптимального посредника.
|
||||
Узлы определяют свой тип подключения и NAT. Например у ростелеком - EIM NAT который можно использовать и для входящих подклюений.
|
||||
Можно использовать IPv6.
|
||||
@@ -0,0 +1,239 @@
|
||||
Вот подробное описание работы модуля динамической маршрутизации.
|
||||
|
||||
---
|
||||
|
||||
## Архитектура: три слоя
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ ЯДРО ОС (netlink / ip route) │ ← tun_route.c — только при старте/стопе
|
||||
│ НЕ динамически, только для configured subnets │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ ПЕРЕСЫЛКА ПАКЕТОВ │ ← routing.c
|
||||
│ route_pkt() → route_lookup() → etcp_route_send │ или queue_data_put в TUN
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ ТАБЛИЦА МАРШРУТОВ (ROUTE_TABLE) │ ← route_lib.c
|
||||
│ route_insert / route_delete / route_lookup │ LPM через бинарный поиск
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ BGP-ОБМЕН (ROUTE_BGP) │ ← route_bgp.c
|
||||
│ NODEINFO (анонс) / WITHDRAW (отзыв) / │ broadcast / loop prevention
|
||||
│ ping (NAT-детекция) │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Ключевые структуры данных
|
||||
|
||||
### `ROUTE_TABLE` (`route_lib.h:46`)
|
||||
```c
|
||||
struct ROUTE_TABLE {
|
||||
struct ROUTE_ENTRY *entries; // отсортированный плоский массив
|
||||
size_t count, capacity;
|
||||
};
|
||||
```
|
||||
Маршруты хранятся в **отсортированном плоском массиве** (не trie, не radix). Сортировка: `network ASC`, затем `prefix_length DESC`. Это позволяет LPM через бинарный поиск.
|
||||
|
||||
### `ROUTE_ENTRY` (`route_lib.h:34`)
|
||||
```c
|
||||
struct ROUTE_ENTRY {
|
||||
uint32_t network; // host byte order
|
||||
uint8_t prefix_length;
|
||||
struct NODEINFO_Q* v_node_info; // NULL = локальный маршрут
|
||||
};
|
||||
```
|
||||
Каждая запись ссылается на узел-владелец (`NODEINFO_Q`). Все маршруты одного узла удаляются за один проход по этому указателю.
|
||||
|
||||
### `NODEINFO` (`route_node.h:73`)
|
||||
Динамическая структура (packed) с полями фиксированной части + динамический хвост: имя, метаданные сокетов, IPv4/IPv6 адреса, подсети, транзитные узлы, hop_list. Передаётся по BGP как NODEINFO-пакет.
|
||||
|
||||
### `NODEINFO_Q` (`route_node.h:151`)
|
||||
Локальная обёртка вокруг `NODEINFO`:
|
||||
```c
|
||||
struct NODEINFO_Q {
|
||||
struct ll_entry ll; // linkage в очереди bgp->nodes (хэш-индекс по node_id)
|
||||
struct ll_queue* paths; // список NODEINFO_PATH — по каким соединениям доступен узел
|
||||
uint8_t last_ver; // последняя известная версия NODEINFO
|
||||
struct NODE_CONNECTIVITY connectivity; // локальное состояние проб (ping/RTT)
|
||||
struct NODEINFO node; // всегда в конце, динамический размер
|
||||
};
|
||||
```
|
||||
|
||||
### `ROUTE_BGP` (`route_bgp.h:90`)
|
||||
```c
|
||||
struct ROUTE_BGP {
|
||||
struct ll_queue* senders_list; // ROUTE_BGP_CONN_ITEM — все активные пиры для broadcast
|
||||
struct ll_queue* nodes; // хэш-индексированная очередь NODEINFO_Q
|
||||
struct NODEINFO_Q* local_node; // свой собственный узел
|
||||
// ... ping_pending, LMDB и т.д.
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Добавление маршрутов
|
||||
|
||||
Есть **три сценария** попадания маршрутов в таблицу:
|
||||
|
||||
### 1. Получение NODEINFO от пира (основной путь)
|
||||
|
||||
**Точка входа:** `route_bgp_receive_cbk()` (`route_bgp.c:162`) → `route_bgp_process_nodeinfo()` (`route_bgp.c:750`).
|
||||
|
||||
Алгоритм `route_bgp_process_nodeinfo`:
|
||||
|
||||
1. **Валидация:** проверка размера, `hop_count < MAX_HOPS(16)`, не свой ли node_id
|
||||
2. **Проверка версии:** `nodeinfo1->last_ver >= new_ver` — значит версия та же или новее → только обновляем paths (через какой conn доступен), маршруты не трогаем
|
||||
3. **Новая версия или новый узел:**
|
||||
- Выделяется/перевыделяется `NODEINFO_Q` (если изменился динамический размер)
|
||||
- Копируется NODEINFO + динамические данные
|
||||
- Источник (`from->peer_node_id`) добавляется в конец hop_list (инкремент `hop_count`)
|
||||
- Старый путь удаляется (`route_bgp_remove_path_by_hop`), новый добавляется (`route_bgp_add_path`)
|
||||
- **`route_insert(bgp->instance->rt, nodeinfo1)`** ← вот здесь маршруты попадают в таблицу
|
||||
- Сохраняется в LMDB
|
||||
4. **Broadcast:** рассылается всем пирам кроме тех, кто уже в hop_list (loop prevention)
|
||||
5. **Connectivity probe:** для новых или изменившихся узлов запускается зондирование связности
|
||||
|
||||
#### `route_insert()` (`route_lib.c:148`) — что происходит внутри:
|
||||
|
||||
```c
|
||||
bool route_insert(struct ROUTE_TABLE *table, struct NODEINFO_Q *node)
|
||||
```
|
||||
|
||||
1. Вызывает `get_node_routes(node, &subnets)` — получает указатель на IPv4-подсети внутри динамических данных NODEINFO (без копирования)
|
||||
2. Конвертирует адреса из `network byte order → host byte order` через `ntohl()`
|
||||
3. Проверяет **пересечения** с существующими маршрутами (точное совпадение network+prefix)
|
||||
4. При необходимости расширяет массив entries (`u_realloc`, ×2)
|
||||
5. Для каждой подсети:
|
||||
- Находит позицию вставки бинарным поиском (`binary_search_insert_pos`): по `network ASC`, затем `prefix_length DESC`
|
||||
- Сдвигает элементы вправо через `memmove()`
|
||||
- Записывает `entry->network`, `entry->prefix_length`, `entry->v_node_info = node`
|
||||
6. Инкрементирует `stats.local_routes` (если `hop_count==0`) или `stats.learned_routes`
|
||||
|
||||
**Важно:** при обновлении существующего узла (realloc) старые маршруты **сначала удаляются** (`route_delete`), потом вставляются новые. Это на случай если изменился состав подсетей.
|
||||
|
||||
### 2. Обновление своего local_node (`route_node.c`)
|
||||
|
||||
`route_bgp_update_my_nodeinfo()` вызывается при:
|
||||
- Инициализации BGP
|
||||
- Изменении конфигурации
|
||||
- NAT-детекции (изменился `nat_type` сокета)
|
||||
|
||||
Алгоритм:
|
||||
1. Считает сокеты, адреса, подсети из конфига
|
||||
2. Сравнивает с текущим `local_node` — если изменилось:
|
||||
- **`route_delete(rt, local_node)`** — удаляет старые локальные маршруты
|
||||
- Освобождает старый `local_node`, выделяет новый
|
||||
- Заполняет NODEINFO (node_id, hop_count=0, ключи, подсети)
|
||||
- Инкрементирует `ver`
|
||||
- **`route_insert(rt, local_node)`** — вставляет новые
|
||||
3. Если изменился состав адресов — помечает `dirty=1`, рассылает обновлённый NODEINFO
|
||||
|
||||
### 3. Получение полной таблицы (синхронизация при connect)
|
||||
|
||||
При установке соединения:
|
||||
1. `route_bgp_new_conn()` (`route_bgp.c:372`) добавляет conn в `senders_list` и отправляет `ROUTE_SUBCMD_REQUEST_TABLE`
|
||||
2. Пир отвечает `route_bgp_handle_request_table()` (`route_bgp.c:1015`):
|
||||
- Шлёт свой `local_node`
|
||||
- Шлёт все известные узлы из `bgp->nodes` (с loop prevention через hop_list)
|
||||
- Шлёт `ROUTE_SUBCMD_TABLE_COMPLETE`
|
||||
3. Каждый полученный NODEINFO обрабатывается через `route_bgp_process_nodeinfo` → `route_insert`
|
||||
|
||||
---
|
||||
|
||||
## Удаление маршрутов
|
||||
|
||||
Есть **два сценария**:
|
||||
|
||||
### 1. Получен WITHDRAW от пира
|
||||
|
||||
**Точка входа:** `route_bgp_process_withdraw()` (`route_bgp.c:890`).
|
||||
|
||||
Алгоритм:
|
||||
1. Извлекает `node_id` (кого удаляем) и `wd_source` (кто инициировал)
|
||||
2. Ищет `NODEINFO_Q` по `node_id`
|
||||
3. Вызывает `route_bgp_remove_path_by_hop(nq, wd_source)` — удаляет все пути, в hop_list которых есть `wd_source`
|
||||
4. Если **путей не осталось** (unreachable):
|
||||
- **`route_delete(bgp->instance->rt, nq)`** — удаляет все маршруты узла из таблицы
|
||||
- Отменяет connectivity probes
|
||||
- Удаляет paths, узел из `bgp->nodes`
|
||||
- Закрывает все соединения etcp_router для этого узла
|
||||
- **Broadcast WITHDRAW** дальше всем пирам (кроме отправителя)
|
||||
|
||||
#### `route_delete()` (`route_lib.c:221`) — что происходит внутри:
|
||||
|
||||
```c
|
||||
void route_delete(struct ROUTE_TABLE *table, struct NODEINFO_Q *node)
|
||||
```
|
||||
|
||||
Проходит по всему массиву entries, для каждой записи где `entry->v_node_info == node`:
|
||||
- Сдвигает оставшиеся элементы влево через `memmove()`
|
||||
- Декрементирует `count` и соответствующий счётчик статистики
|
||||
- **Не освобождает** `NODEINFO_Q` (этим занимается вызывающий код в `route_bgp.c`)
|
||||
|
||||
### 2. Соединение ETCP упало (conn DOWN)
|
||||
|
||||
**Точка входа:** `route_bgp_remove_conn()` (`route_bgp.c:417`).
|
||||
|
||||
Алгоритм:
|
||||
1. Отменяет pending NAT checks для линков этого соединения
|
||||
2. Итерирует **все узлы** в `bgp->nodes`:
|
||||
- Для каждого вызывает `route_bgp_remove_path(nq, conn)` — удаляет путь через этот conn
|
||||
- Если путь был последним (unreachable):
|
||||
- **`route_delete(rt, nq)`**
|
||||
- Удаляет узел из `bgp->nodes`, отменяет пробы
|
||||
- Закрывает etcp_router соединения
|
||||
- **Broadcast WITHDRAW** с `wd_source = peer_node_id` (или свой node_id если удаляемый узел и есть сам пир)
|
||||
3. Удаляет conn из `senders_list`
|
||||
|
||||
---
|
||||
|
||||
## Поиск маршрута и пересылка пакетов
|
||||
|
||||
### `route_lookup()` (`route_lib.c:248`) — LPM
|
||||
|
||||
Бинарный поиск по отсортированному массиву:
|
||||
- Сравнивает `(dest_ip & mask) == (network & mask)`
|
||||
- Запоминает лучший (наибольшая `prefix_length`)
|
||||
- При совпадении продолжает поиск вправо (там могут быть более специфичные /32 внутри /24)
|
||||
- Возвращает `ROUTE_ENTRY*` или NULL
|
||||
|
||||
### `route_pkt()` (`routing.c:53`) — единая точка пересылки
|
||||
|
||||
1. Извлекает IP-пакет (пропуская 1-байтовый cmd)
|
||||
2. Дропает мультикаст (224-239) и броадкаст (255.255.255.255)
|
||||
3. `route_lookup(instance->rt, dst_ip)` — ищет маршрут
|
||||
4. **Нет маршрута** → дроп, `dropped_packets++`
|
||||
5. **Маршрут удалённый** (`v_node_info != NULL && hop_count > 0`) → `etcp_route_send()` (через service router)
|
||||
6. **Маршрут локальный** (нет v_node_info или hop_count==0) → `queue_data_put(instance->tun->input_queue)` (доставка в TUN)
|
||||
|
||||
Два входа в `route_pkt`:
|
||||
- **Из TUN** (исходящий): `routing_pkt_from_tun_cb()` — callback на `tun->output_queue`
|
||||
- **Из ETCP** (входящий): `routing_pkt_from_etcp_cb()` — callback через `etcp_router_bind(ETCP_RT_ID_DATA=0)`
|
||||
|
||||
---
|
||||
|
||||
## Loop prevention
|
||||
|
||||
Distance-vector стиль: каждый NODEINFO-пакет содержит **hop_list** — массив node_id, через которые он прошёл. Перед broadcast проверяется, что целевой пир **не находится** в hop_list. Аналогично при WITHDRAW — `wd_source` ищется в hop_list каждого path.
|
||||
|
||||
---
|
||||
|
||||
## NAT-детекция (влияет на обновление local_node)
|
||||
|
||||
1. При новом соединении сканируются все линки, для каждого вызывается `route_bgp_start_link_nat_check()`
|
||||
2. Находится «третий узел» (другой подключённый пир, не private)
|
||||
3. Через третий узел отправляется ping-запрос на целевой IP/порт (`route_ping_send_req_addr`)
|
||||
4. Третий узел выполняет UDP-пинг, возвращает результат
|
||||
5. По результату устанавливается `NAT_TYPE_EIM` (reachable) или `NAT_TYPE_STRICT`
|
||||
6. Отправляется `NAT_INFO` обратно владельцу сокета
|
||||
7. Владелец обновляет `local_node` → `route_bgp_update_my_nodeinfo()` → удаление/вставка локальных маршрутов → broadcast обновлённого NODEINFO
|
||||
|
||||
---
|
||||
|
||||
## Итого: кто когда вызывает route_insert/route_delete
|
||||
|
||||
| Событие | Операция | Функция |
|
||||
|---|---|---|
|
||||
| Получен новый/обновлённый NODEINFO | `route_insert` | `route_bgp_process_nodeinfo` `:849` |
|
||||
| Обновление своего local_node | `route_delete` старого + `route_insert` нового | `route_bgp_update_my_nodeinfo` (`route_node.c`) |
|
||||
| Получен WITHDRAW (узел недоступен) | `route_delete` | `route_bgp_process_withdraw` `:904` |
|
||||
| Соединение упало (conn DOWN) | `route_delete` для каждого узла без путей | `route_bgp_remove_conn` `:468` |
|
||||
| Переаллокация NODEINFO_Q при обновлении | `route_delete` старого | `route_bgp_process_nodeinfo` `:812` |
|
||||
+63
-4
@@ -1,4 +1,5 @@
|
||||
bin_PROGRAMS = utun
|
||||
noinst_LIBRARIES = libutun.a
|
||||
|
||||
# Sources that are always compiled
|
||||
utun_CORE_SOURCES = \
|
||||
@@ -8,12 +9,13 @@ utun_CORE_SOURCES = \
|
||||
config_updater.c \
|
||||
route_lib.c \
|
||||
route6_lib.c \
|
||||
route_bgp.c \
|
||||
topo_group.c \
|
||||
route_ping.c \
|
||||
route_node.c \
|
||||
route_node_lmdb.c \
|
||||
topo_node.c \
|
||||
topo_node_lmdb.c \
|
||||
route_connectivity.c \
|
||||
conn_mgr.c \
|
||||
db_sync.c \
|
||||
routing.c \
|
||||
tun_if.c \
|
||||
tun_route.c \
|
||||
@@ -35,6 +37,7 @@ utun_CORE_SOURCES = \
|
||||
pkt_normalizer.c \
|
||||
packet_dump.c \
|
||||
etcp_api.c \
|
||||
etcp_connect.c \
|
||||
control_server.c \
|
||||
msg_transport.c \
|
||||
firewall.c \
|
||||
@@ -45,16 +48,71 @@ utun_CORE_SOURCES = \
|
||||
etcp_router.c \
|
||||
proxy/tcp_proxy_server.c \
|
||||
proxy/udp_proxy.c \
|
||||
proxy/socks_proxy.c \
|
||||
proxy/icmp_proxy.c \
|
||||
lwip_tcp/lwip_pbuf.c \
|
||||
lwip_tcp/lwip_tcp.c \
|
||||
lwip_tcp/lwip_tcp_in.c \
|
||||
lwip_tcp/lwip_tcp_out.c
|
||||
|
||||
# libutun: all core sources except main()
|
||||
libutun_a_SOURCES = \
|
||||
utun_instance.c \
|
||||
config_parser.c \
|
||||
config_updater.c \
|
||||
route_lib.c \
|
||||
route6_lib.c \
|
||||
topo_group.c \
|
||||
route_ping.c \
|
||||
topo_node.c \
|
||||
topo_node_lmdb.c \
|
||||
route_connectivity.c \
|
||||
conn_mgr.c \
|
||||
db_sync.c \
|
||||
routing.c \
|
||||
tun_if.c \
|
||||
tun_route.c \
|
||||
tun_linux.c \
|
||||
tun_freebsd.c \
|
||||
tun_windows.c \
|
||||
etcp.c \
|
||||
etcp_connections.c \
|
||||
etcp_bbr.c \
|
||||
etcp_loadbalancer.c \
|
||||
etcp_debug.c \
|
||||
etcp_dump.c \
|
||||
secure_channel.c \
|
||||
crc32.c \
|
||||
stcp_link.c \
|
||||
stcp.c \
|
||||
stcp_server.c \
|
||||
stcp_client.c \
|
||||
pkt_normalizer.c \
|
||||
packet_dump.c \
|
||||
etcp_api.c \
|
||||
etcp_connect.c \
|
||||
control_server.c \
|
||||
msg_transport.c \
|
||||
firewall.c \
|
||||
eim_nat.c \
|
||||
nat_transport.c \
|
||||
dummynet.c \
|
||||
proxy/tcp_proxy_client.c \
|
||||
etcp_router.c \
|
||||
proxy/tcp_proxy_server.c \
|
||||
proxy/udp_proxy.c \
|
||||
proxy/socks_proxy.c \
|
||||
proxy/icmp_proxy.c \
|
||||
lwip_tcp/lwip_pbuf.c \
|
||||
lwip_tcp/lwip_tcp.c \
|
||||
lwip_tcp/lwip_tcp_in.c \
|
||||
lwip_tcp/lwip_tcp_out.c
|
||||
libutun_a_CFLAGS = $(utun_CFLAGS)
|
||||
|
||||
# Platform-specific TUN libs (Windows only)
|
||||
utun_TUN_LIBS = @TUN_LIBS@
|
||||
|
||||
utun_SOURCES = $(utun_CORE_SOURCES) $(utun_TUN_SOURCES)
|
||||
utun_SOURCES = utun.c
|
||||
|
||||
# Include paths
|
||||
utun_CFLAGS = \
|
||||
@@ -65,6 +123,7 @@ utun_CFLAGS = \
|
||||
|
||||
# Libraries
|
||||
utun_LDADD = \
|
||||
libutun.a \
|
||||
$(top_builddir)/lib/libuasync.a \
|
||||
-lpthread \
|
||||
-lm \
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
Архитектура таблицы c быстрой репликацией между несколькими узлами:
|
||||
|
||||
1. В конец таблицы каждый узел может самостоятельно добавлять данные (со своим timestamp, желательно время правильное)
|
||||
2. узлы между собой синхронизируются, распространяя обновления по соседям
|
||||
|
||||
3. TTL: изменение имеет время жизни. узел удаляет собственные записи которые не были отправлены никому в течении суток
|
||||
|
||||
4. Формат записи:
|
||||
|
||||
ID (64bit monotonic autoincrement)
|
||||
chain_hash (256bit) - цепочка хешей. считается так: chain_hash[index+1]=hash(chain_hash[index],ID[index],timestamp[index],datahash[index]). если хеш совпадает - это признак того что все строки выше синхронизированы.
|
||||
timestamp (64bit) - время создания записи. строки должны сортироваться по возрастанию concat(datahash(младшая часть числа,64 bit)+timestamp(старшая часть))
|
||||
datahash (64bit) - хеш данных этой строки
|
||||
data (varchar) - данные в формате json
|
||||
|
||||
алгоритм репликации (2 узла):
|
||||
узел A запрашивает синхронизацию и передает свой last id
|
||||
test_id= min(mast id, peer last id)
|
||||
узел B передает: test_id, chain_hash(test id), hash(test id-1), hash(test id-2), hash(test id-4), hash(test id-8) итд 16,32,..., до первого элемента включитально (лимитируем вылетевший индекс первым элементом)
|
||||
узел A - сравнивает, находит проверяемый диапазон id, отправляет другому узлу 16 (можно больше) хешей (линейно разбив проверяемый диапазон на более короткие поддиапазоны). таким образом узлы уточняют первый ид который не совпал.
|
||||
Когда первая различающияся запись найдена, узел отправляет хеш этой и n (например 32) последующих записей (если записей много). если записей мало (<4) то узел отправляет сразу содержимое записей.
|
||||
(надо додумать алгоритм, чтобы оптимизировать количество итераций - лучше передать больше данных за раз чем много итераций с ожиданием ответной стороны)
|
||||
@@ -0,0 +1,9 @@
|
||||
Задача сделать механизм маршрутизации между узлами для групповых чатов.
|
||||
|
||||
Узлы для всех чатов находятся в одной таблице - это nodeinfo (информация о подключении)
|
||||
В другой таблице есть список узлов конкретного чата.
|
||||
|
||||
надо сделать возможность хранить одновременно несколько групп узлов. в группу узлов добавить тип - utun или чатовая.
|
||||
если utun - в группе используется роутинг (ipv4/ipv6).
|
||||
если чатовая - то только связность между узлами.
|
||||
|
||||
+114
-13
@@ -47,7 +47,8 @@ typedef enum {
|
||||
SECTION_NAT,
|
||||
SECTION_TCP_PROXY_CLIENT,
|
||||
SECTION_TCP_PROXY_SERVER,
|
||||
SECTION_MSG_TRANSPORT
|
||||
SECTION_MSG_TRANSPORT,
|
||||
SECTION_NETWORK
|
||||
} section_type_t;
|
||||
|
||||
static char* trim(char *str) {
|
||||
@@ -357,6 +358,10 @@ static int parse_global(const char *key, const char *value, struct global_config
|
||||
global->my_node_id = strtoull(value, NULL, 16);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "tun_enabled") == 0) {
|
||||
global->tun_enabled = strcasecmp(value, "yes") == 0 || strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0;
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "tun_ifname") == 0) {
|
||||
snprintf(global->tun_ifname, sizeof(global->tun_ifname), "%s", value);
|
||||
return 0;
|
||||
@@ -377,6 +382,10 @@ static int parse_global(const char *key, const char *value, struct global_config
|
||||
global->keepalive_interval = atoi(value);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "keepalive_adaptive") == 0) {
|
||||
global->keepalive_adaptive = atoi(value);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "bbr_max_cwnd") == 0) {
|
||||
global->bbr_max_cwnd = atoi(value);
|
||||
return 0;
|
||||
@@ -390,6 +399,14 @@ static int parse_global(const char *key, const char *value, struct global_config
|
||||
if (strcmp(key, "db_path") == 0) {
|
||||
return assign_string(global->db_path, sizeof(global->db_path), value);
|
||||
}
|
||||
if (strcmp(key, "db_sync_enabled") == 0) {
|
||||
global->db_sync_enabled = atoi(value);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "db_sync_ttl") == 0) {
|
||||
global->db_sync_ttl = (uint32_t)atoi(value);
|
||||
return 0;
|
||||
}
|
||||
/* debug_categories key is deprecated - use [debug] section instead */
|
||||
if (strcmp(key, "enable_timestamp") == 0) {
|
||||
global->enable_timestamp = atoi(value);
|
||||
@@ -411,7 +428,7 @@ static int parse_global(const char *key, const char *value, struct global_config
|
||||
global->tun_test_mode = atoi(value);
|
||||
return 0;
|
||||
}
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown global option '%s'. Valid: my_node_name, my_private_key, my_public_key, my_node_id, tun_ifname, tun_ip, mtu, keepalive_timeout, keepalive_interval, bbr_max_cwnd, debug_level, log_file, db_path, enable_timestamp, enable_function_names, enable_file_lines, enable_colors, tun_test_mode", filename, line_num, key);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown global option '%s'. Valid: my_node_name, my_private_key, my_public_key, my_node_id, tun_ifname, tun_ip, mtu, keepalive_timeout, keepalive_interval, keepalive_adaptive, bbr_max_cwnd, debug_level, log_file, db_path, enable_timestamp, enable_function_names, enable_file_lines, enable_colors, tun_test_mode", filename, line_num, key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -472,6 +489,22 @@ static int parse_tcp_proxy_client(const char *key, const char *value, struct glo
|
||||
global->tcp_proxy_client_via_node_id = strtoull(value, NULL, 16);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "socks_enabled") == 0) {
|
||||
global->tcp_proxy_client_socks_enabled = strcasecmp(value, "yes") == 0 || strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0;
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "socks_addr") == 0) {
|
||||
strncpy(global->tcp_proxy_client_socks_addr, value, sizeof(global->tcp_proxy_client_socks_addr) - 1);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "http_proxy_enabled") == 0) {
|
||||
global->tcp_proxy_client_http_proxy_enabled = strcasecmp(value, "yes") == 0 || strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0;
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "http_proxy_addr") == 0) {
|
||||
strncpy(global->tcp_proxy_client_http_proxy_addr, value, sizeof(global->tcp_proxy_client_http_proxy_addr) - 1);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "forward") == 0) {
|
||||
if (global->tcp_proxy_client_mapping_count >= MAX_TCP_PROXY_CLIENT_MAPPINGS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Too many tcp_proxy_client forward rules (max %d)", MAX_TCP_PROXY_CLIENT_MAPPINGS);
|
||||
@@ -508,7 +541,7 @@ static int parse_tcp_proxy_client(const char *key, const char *value, struct glo
|
||||
global->tcp_proxy_client_mapping_count++;
|
||||
return 0;
|
||||
}
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown tcp_proxy_client option '%s'. Valid: enabled, tun_name, tun_ip, mtu, via_node, forward", filename, line_num, key);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown tcp_proxy_client option '%s'. Valid: enabled, tun_name, tun_ip, mtu, via_node, forward, socks_enabled, socks_addr, http_proxy_enabled, http_proxy_addr", filename, line_num, key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -626,6 +659,8 @@ static int parse_server(const char *key, const char *value, struct CFG_SERVER *s
|
||||
srv->type = CFG_SERVER_TYPE_NAT;
|
||||
} else if (strcmp(value, "private") == 0) {
|
||||
srv->type = CFG_SERVER_TYPE_PRIVATE;
|
||||
} else if (strcmp(value, "local") == 0) {
|
||||
srv->type = CFG_SERVER_TYPE_LOCAL;
|
||||
} else {
|
||||
srv->type = CFG_SERVER_TYPE_UNKNOWN;
|
||||
}
|
||||
@@ -690,6 +725,21 @@ static int parse_client(const char *key, const char *value, struct CFG_CLIENT *c
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_network(const char *key, const char *value, struct CFG_NETWORK *net, const char *filename, int line_num) {
|
||||
if (strcmp(key, "id") == 0) {
|
||||
net->id = strtoull(value, NULL, 16);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(key, "pubkey") == 0) {
|
||||
return assign_string(net->pubkey_hex, sizeof(net->pubkey_hex), value);
|
||||
}
|
||||
if (strcmp(key, "signing_key") == 0) {
|
||||
return assign_string(net->signing_key_hex, sizeof(net->signing_key_hex), value);
|
||||
}
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown network option '%s'. Valid: id, pubkey, signing_key", filename, line_num, key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static section_type_t parse_section_header(const char *line, char *name, size_t name_len) {
|
||||
if (line[0] != '[') return SECTION_UNKNOWN;
|
||||
|
||||
@@ -726,14 +776,15 @@ static section_type_t parse_section_header(const char *line, char *name, size_t
|
||||
|
||||
if (strcasecmp(type, "server") == 0) return SECTION_SERVER;
|
||||
if (strcasecmp(type, "client") == 0) return SECTION_CLIENT;
|
||||
|
||||
if (strcasecmp(type, "network") == 0) return SECTION_NETWORK;
|
||||
|
||||
return SECTION_UNKNOWN;
|
||||
}
|
||||
|
||||
static struct utun_config* parse_config_internal(FILE *fp, const char *filename) {
|
||||
struct utun_config *cfg = u_calloc(1, sizeof(struct utun_config));
|
||||
if (!cfg) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "parse_config_internal: failed to allocate memory for config structure");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "failed to allocate memory for config structure");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -741,6 +792,7 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
cfg->global.name[0] = '\0';
|
||||
cfg->global.keepalive_timeout = 2000; // Default 2 seconds
|
||||
cfg->global.keepalive_interval = 200; // Default 0.2 s
|
||||
cfg->global.keepalive_adaptive = 1; // Default: adaptive enabled
|
||||
cfg->global.bbr_max_cwnd = 1048576; // Default 1MB (was INFLIGHT_LIM_MAX)
|
||||
cfg->global.tcp_recv_buf = 0; // 0 = OS default
|
||||
cfg->global.firewall_rules = NULL;
|
||||
@@ -748,10 +800,13 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
cfg->global.firewall_bypass_all = 0;
|
||||
cfg->global.control_allows = NULL;
|
||||
cfg->global.control_allow_count = 0;
|
||||
cfg->global.db_sync_enabled = 0;
|
||||
cfg->global.db_sync_ttl = 86400;
|
||||
|
||||
section_type_t cur_section = SECTION_UNKNOWN;
|
||||
struct CFG_SERVER *cur_server = NULL;
|
||||
struct CFG_CLIENT *cur_client = NULL;
|
||||
struct CFG_NETWORK *cur_network = NULL;
|
||||
char line[MAX_LINE_LEN];
|
||||
int line_num = 0;
|
||||
|
||||
@@ -775,6 +830,11 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
cfg->clients = cur_client;
|
||||
cur_client = NULL;
|
||||
}
|
||||
if (cur_section == SECTION_NETWORK && cur_network) {
|
||||
cur_network->next = cfg->networks;
|
||||
cfg->networks = cur_network;
|
||||
cur_network = NULL;
|
||||
}
|
||||
|
||||
char name[MAX_CONN_NAME_LEN];
|
||||
cur_section = parse_section_header(trimmed, name, sizeof(name));
|
||||
@@ -782,7 +842,7 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
if (cur_section == SECTION_SERVER) {
|
||||
cur_server = u_calloc(1, sizeof(struct CFG_SERVER));
|
||||
if (!cur_server) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "parse_config_internal: failed to allocate memory for server %s", name);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "failed to allocate memory for server %s", name);
|
||||
goto error;
|
||||
}
|
||||
strcpy(cur_server->name, name);
|
||||
@@ -790,10 +850,17 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
} else if (cur_section == SECTION_CLIENT) {
|
||||
cur_client = u_calloc(1, sizeof(struct CFG_CLIENT));
|
||||
if (!cur_client) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "parse_config_internal: failed to allocate memory for client %s", name);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "failed to allocate memory for client %s", name);
|
||||
goto error;
|
||||
}
|
||||
strcpy(cur_client->name, name);
|
||||
} else if (cur_section == SECTION_NETWORK) {
|
||||
cur_network = u_calloc(1, sizeof(struct CFG_NETWORK));
|
||||
if (!cur_network) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "failed to allocate memory for network %s", name);
|
||||
goto error;
|
||||
}
|
||||
strcpy(cur_network->name, name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -900,6 +967,11 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
// parse_msg_transport already printed the error
|
||||
}
|
||||
break;
|
||||
case SECTION_NETWORK:
|
||||
if (cur_network && parse_network(key, value, cur_network, filename, line_num) < 0) {
|
||||
// parse_network already printed the error
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key);
|
||||
break;
|
||||
@@ -915,11 +987,23 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
|
||||
cur_client->next = cfg->clients;
|
||||
cfg->clients = cur_client;
|
||||
}
|
||||
if (cur_section == SECTION_NETWORK && cur_network) {
|
||||
cur_network->next = cfg->networks;
|
||||
cfg->networks = cur_network;
|
||||
}
|
||||
|
||||
// Set default TUN interface name if not provided
|
||||
if (cfg->global.tun_ifname[0] == '\0') {
|
||||
snprintf(cfg->global.tun_ifname, sizeof(cfg->global.tun_ifname), "tun0");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Auto-generated TUN interface name: %s", cfg->global.tun_ifname);
|
||||
// Post-parse defaults: tun_enabled=1 if not explicitly disabled
|
||||
if (!cfg->global.tun_enabled && cfg->global.tun_ifname[0] == '\0' && cfg->global.tun_ip.family == AF_UNSPEC) {
|
||||
// No TUN config at all — treat as explicitly off (for non-root SOCKS/HTTP-only mode)
|
||||
} else if (!cfg->global.tun_enabled) {
|
||||
// TUN was explicitly disabled via tun_enabled=no
|
||||
} else {
|
||||
// TUN enabled (default) — set default ifname if not provided
|
||||
cfg->global.tun_enabled = 1;
|
||||
if (cfg->global.tun_ifname[0] == '\0') {
|
||||
snprintf(cfg->global.tun_ifname, sizeof(cfg->global.tun_ifname), "tun0");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Auto-generated TUN interface name: %s", cfg->global.tun_ifname);
|
||||
}
|
||||
}
|
||||
|
||||
return cfg;
|
||||
@@ -930,6 +1014,7 @@ error:
|
||||
free_cfg_client_links(cur_client->links);
|
||||
u_free(cur_client);
|
||||
}
|
||||
if (cur_network) u_free(cur_network);
|
||||
free_config(cfg);
|
||||
return NULL;
|
||||
}
|
||||
@@ -968,6 +1053,14 @@ void free_config(struct utun_config *config) {
|
||||
client = next;
|
||||
}
|
||||
|
||||
// Free networks
|
||||
struct CFG_NETWORK *network = config->networks;
|
||||
while (network) {
|
||||
struct CFG_NETWORK *next = network->next;
|
||||
u_free(network);
|
||||
network = next;
|
||||
}
|
||||
|
||||
// Free route entries
|
||||
free_route_entries(config->route_subnets);
|
||||
free_route_entries(config->my_subnets);
|
||||
@@ -1062,8 +1155,16 @@ void print_config(const struct utun_config *cfg) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " allow=%s", ip_str);
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " allow=%s:%d", ip_str, r->port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Networks:");
|
||||
struct CFG_NETWORK *net = cfg->networks;
|
||||
while (net) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " [network:%s] id=%012llx pubkey=%.64s signing_key=%.64s",
|
||||
net->name, (unsigned long long)net->id, net->pubkey_hex, net->signing_key_hex);
|
||||
net = net->next;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " no rules");
|
||||
}
|
||||
|
||||
+20
-2
@@ -24,9 +24,10 @@ struct IP {
|
||||
};
|
||||
|
||||
#define CFG_SERVER_TYPE_UNKNOWN 0
|
||||
#define CFG_SERVER_TYPE_PUBLIC 1
|
||||
#define CFG_SERVER_TYPE_NAT 2
|
||||
#define CFG_SERVER_TYPE_PUBLIC 1
|
||||
#define CFG_SERVER_TYPE_NAT 2
|
||||
#define CFG_SERVER_TYPE_PRIVATE 3
|
||||
#define CFG_SERVER_TYPE_LOCAL 4
|
||||
|
||||
#define CFG_IPV6_MODE_NONE 0 // normal explicit address
|
||||
#define CFG_IPV6_MODE_TEMPORARY 1 // temporary IPv6 from interface
|
||||
@@ -61,6 +62,14 @@ struct CFG_CLIENT {
|
||||
struct CFG_CLIENT *next; // Next client in linked list
|
||||
};
|
||||
|
||||
struct CFG_NETWORK {
|
||||
struct CFG_NETWORK* next;
|
||||
char name[64];
|
||||
uint64_t id; // 56-bit network ID
|
||||
char pubkey_hex[SC_PUBKEY_SIZE * 2 + 1]; // 64 hex chars
|
||||
char signing_key_hex[SC_PRIVKEY_SIZE * 2 + 1]; // 64 hex chars
|
||||
};
|
||||
|
||||
struct CFG_ROUTE_ENTRY {
|
||||
struct CFG_ROUTE_ENTRY* next;
|
||||
struct IP ip;
|
||||
@@ -95,6 +104,7 @@ struct global_config {
|
||||
char my_private_key_hex[MAX_KEY_LEN];
|
||||
char my_public_key_hex[MAX_KEY_LEN];
|
||||
uint64_t my_node_id;
|
||||
int tun_enabled; // 1 = create TUN (default), 0 = skip TUN (run without root)
|
||||
char tun_ifname[16]; // TUN interface name (e.g., "tun12")
|
||||
struct IP tun_ip;
|
||||
int mtu;
|
||||
@@ -105,6 +115,8 @@ struct global_config {
|
||||
// Debug and logging configuration
|
||||
char log_file[256]; // Path to log file (empty = stdout)
|
||||
char db_path[256]; // Path to LMDB nodeinfo database (empty = disabled)
|
||||
int db_sync_enabled; // 1 = enable distributed DB sync (default: 0)
|
||||
uint32_t db_sync_ttl; // TTL for unsent records in seconds (default: 86400)
|
||||
char debug_level[16]; // debug level: error, warn, info, debug, trace
|
||||
int enable_timestamp; // enable timestamps in logs
|
||||
int enable_function_names; // enable function names in logs
|
||||
@@ -121,6 +133,7 @@ struct global_config {
|
||||
int tun_test_mode; // test mode: 1 = don't open real TUN, queues only
|
||||
int keepalive_timeout; // keepalive timeout in ms (default: 2000)
|
||||
int keepalive_interval; // keepalive interval in ms (default: 200)
|
||||
int keepalive_adaptive; // 1 = adaptive period (default), 0 = fixed interval
|
||||
int bbr_max_cwnd; // BBR cwnd cap in bytes (default: 100000)
|
||||
|
||||
// Firewall configuration
|
||||
@@ -161,6 +174,10 @@ struct global_config {
|
||||
uint64_t tcp_proxy_client_via_node_id; // через этот узел проксируются все forward-правила
|
||||
struct tcp_proxy_client_mapping_config tcp_proxy_client_mappings[MAX_TCP_PROXY_CLIENT_MAPPINGS];
|
||||
int tcp_proxy_client_mapping_count;
|
||||
int tcp_proxy_client_socks_enabled;
|
||||
char tcp_proxy_client_socks_addr[64]; // e.g. "127.0.0.1:1080"
|
||||
int tcp_proxy_client_http_proxy_enabled;
|
||||
char tcp_proxy_client_http_proxy_addr[64]; // e.g. "127.0.0.1:8080"
|
||||
|
||||
// TCP proxy server (exit node) configuration ([tcp_proxy_server] section)
|
||||
int tcp_proxy_server_enabled;
|
||||
@@ -173,6 +190,7 @@ struct utun_config {
|
||||
struct CFG_CLIENT* clients;
|
||||
struct CFG_ROUTE_ENTRY* route_subnets;
|
||||
struct CFG_ROUTE_ENTRY* my_subnets;
|
||||
struct CFG_NETWORK* networks;
|
||||
};
|
||||
|
||||
struct utun_config* parse_config(const char *filename);
|
||||
|
||||
+38
-23
@@ -151,21 +151,25 @@ static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_cap
|
||||
char *opt_pos = find_option(*buf, *buf_len, option);
|
||||
|
||||
if (opt_pos) {
|
||||
// Find option line end
|
||||
char *line_end = strchr(opt_pos, '\n');
|
||||
// Determine if old line has trailing \n
|
||||
char *nl = strchr(opt_pos, '\n');
|
||||
int has_trailing_nl = (nl != NULL);
|
||||
char *line_end = nl ? nl : *buf + *buf_len;
|
||||
|
||||
if (!line_end) line_end = *buf + *buf_len;
|
||||
|
||||
// Prepare new line
|
||||
char new_line[MAX_LINE_LEN];
|
||||
int new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value);
|
||||
|
||||
int new_len;
|
||||
if (has_trailing_nl) {
|
||||
new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value);
|
||||
} else {
|
||||
new_len = snprintf(new_line, sizeof(new_line), "%s=%s", option, value);
|
||||
}
|
||||
if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1;
|
||||
|
||||
|
||||
// Calculate length difference
|
||||
size_t old_line_len = line_end - opt_pos + 1;
|
||||
size_t old_line_len = has_trailing_nl ? (line_end - opt_pos + 1) : (line_end - opt_pos);
|
||||
long len_diff = new_len - old_line_len;
|
||||
|
||||
|
||||
// Ensure buffer capacity
|
||||
if (*buf_len + len_diff + 1 > *buf_capacity) {
|
||||
*buf_capacity = *buf_len + len_diff + 1024;
|
||||
@@ -177,11 +181,11 @@ static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_cap
|
||||
opt_pos = find_option(*buf, *buf_len, option);
|
||||
|
||||
if (!opt_pos) return -1;
|
||||
line_end = strchr(opt_pos, '\n');
|
||||
|
||||
if (!line_end) line_end = *buf + *buf_len;
|
||||
nl = strchr(opt_pos, '\n');
|
||||
has_trailing_nl = (nl != NULL);
|
||||
line_end = nl ? nl : *buf + *buf_len;
|
||||
}
|
||||
|
||||
|
||||
// Move content and insert new line
|
||||
memmove(opt_pos + new_len, line_end, *buf_len - (line_end - *buf) + 1);
|
||||
|
||||
@@ -194,24 +198,31 @@ static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_cap
|
||||
char *section_end = find_section_global(*buf, *buf_len);
|
||||
|
||||
if (!section_end) return -1;
|
||||
|
||||
|
||||
// Find end of global section (next [ or end of buffer)
|
||||
char *insert_pos = strchr(section_end, '[');
|
||||
int need_leading_nl = 0;
|
||||
|
||||
if (!insert_pos) insert_pos = *buf + *buf_len;
|
||||
else {
|
||||
if (!insert_pos) {
|
||||
insert_pos = *buf + *buf_len;
|
||||
if (*buf_len > 0 && (*buf)[*buf_len - 1] != '\n')
|
||||
need_leading_nl = 1;
|
||||
} else {
|
||||
// Move to beginning of next line
|
||||
char *prev_nl = insert_pos;
|
||||
while (prev_nl > *buf && *prev_nl != '\n') prev_nl--;
|
||||
if (*prev_nl == '\n') insert_pos = prev_nl + 1;
|
||||
}
|
||||
|
||||
|
||||
// Prepare new line
|
||||
char new_line[MAX_LINE_LEN];
|
||||
int new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value);
|
||||
|
||||
int new_len;
|
||||
if (need_leading_nl)
|
||||
new_len = snprintf(new_line, sizeof(new_line), "\n%s=%s\n", option, value);
|
||||
else
|
||||
new_len = snprintf(new_line, sizeof(new_line), "%s=%s\n", option, value);
|
||||
if (new_len <= 0 || new_len >= (int)sizeof(new_line)) return -1;
|
||||
|
||||
|
||||
// Ensure buffer capacity
|
||||
if (*buf_len + new_len + 1 > *buf_capacity) {
|
||||
*buf_capacity = *buf_len + new_len + 1024;
|
||||
@@ -224,15 +235,19 @@ static int insert_or_replace_option(char **buf, size_t *buf_len, size_t *buf_cap
|
||||
|
||||
if (!section_end) return -1;
|
||||
insert_pos = strchr(section_end, '[');
|
||||
need_leading_nl = 0;
|
||||
|
||||
if (!insert_pos) insert_pos = *buf + *buf_len;
|
||||
else {
|
||||
if (!insert_pos) {
|
||||
insert_pos = *buf + *buf_len;
|
||||
if (*buf_len > 0 && (*buf)[*buf_len - 1] != '\n')
|
||||
need_leading_nl = 1;
|
||||
} else {
|
||||
char *prev_nl = insert_pos;
|
||||
while (prev_nl > *buf && *prev_nl != '\n') prev_nl--;
|
||||
if (*prev_nl == '\n') insert_pos = prev_nl + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Move content and insert new line
|
||||
memmove(insert_pos + new_len, insert_pos, *buf_len - (insert_pos - *buf) + 1);
|
||||
|
||||
|
||||
+207
-197
@@ -17,8 +17,8 @@
|
||||
#include "etcp_router.h"
|
||||
#include "config_parser.h"
|
||||
#include "secure_channel.h"
|
||||
#include "route_node.h"
|
||||
#include "route_bgp.h"
|
||||
#include "topo_node.h"
|
||||
#include "topo_group.h"
|
||||
#include "route_ping.h"
|
||||
#include "route_connectivity.h"
|
||||
#include "conn_mgr.h"
|
||||
@@ -31,13 +31,14 @@ static void cm_start_local_scan(struct CONN_MGR_ENTRY* entry);
|
||||
static void cm_idle_timer_cb(void* arg);
|
||||
static void cm_deliver_result(struct CONN_MGR_ENTRY* entry, int result);
|
||||
static void cm_bg_ping_timer_cb(void* arg);
|
||||
static int cm_nat_compatible(struct ETCP_SOCKET* our, uint8_t target_meta_type);
|
||||
static uint16_t cm_get_node_min_rtt(struct NODEINFO_Q* nq);
|
||||
static int cm_has_direct_ip(struct NODEINFO_Q* nq);
|
||||
static int cm_has_local_addr(struct NODEINFO_Q* nq);
|
||||
static int cm_nat_compatible(struct ETCP_SOCKET* our, uint8_t target_config_type, uint8_t target_nat_type);
|
||||
static uint16_t cm_get_node_min_rtt(struct TOPO_NODEQ* nq);
|
||||
static int cm_has_direct_ip(struct TOPO_NODEQ* nq);
|
||||
static int cm_has_local_addr(struct TOPO_NODEQ* nq);
|
||||
static void cm_entry_destroy(struct CONN_MGR_ENTRY* entry);
|
||||
static uint64_t cm_get_node_max_probe_time(struct NODEINFO_Q* nq);
|
||||
static int cm_is_rtt_fresh(struct NODEINFO_Q* nq, uint64_t now_tb);
|
||||
static void cm_exchange_probe_retry_cb(void* arg);
|
||||
static uint64_t cm_get_node_max_probe_time(struct TOPO_NODEQ* nq);
|
||||
static int cm_is_rtt_fresh(struct TOPO_NODEQ* nq, uint64_t now_tb);
|
||||
|
||||
static void conn_mgr_router_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||||
static void cm_handle_direct_req(struct ETCP_CONN* conn, const uint8_t* data, size_t len);
|
||||
@@ -70,14 +71,14 @@ struct cm_exchange_pending {
|
||||
};
|
||||
|
||||
struct CONN_MGR* conn_mgr_init(struct UTUN_INSTANCE* instance) {
|
||||
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr_init: NULL instance"); return NULL; }
|
||||
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NULL instance"); return NULL; }
|
||||
struct CONN_MGR* mgr = u_calloc(1, sizeof(struct CONN_MGR));
|
||||
if (!mgr) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr_init: alloc failed"); return NULL; }
|
||||
if (!mgr) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "alloc failed"); return NULL; }
|
||||
mgr->instance = instance;
|
||||
mgr->entry_capacity = 8;
|
||||
mgr->entries = u_calloc(mgr->entry_capacity, sizeof(struct CONN_MGR_ENTRY));
|
||||
if (!mgr->entries) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr_init: entries alloc failed"); u_free(mgr); return NULL; }
|
||||
etcp_router_bind(instance, ETCP_ID_CONN_MGR, conn_mgr_router_recv_handler);
|
||||
if (!mgr->entries) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "entries alloc failed"); u_free(mgr); return NULL; }
|
||||
etcp_router_bind(instance, ETCP_RT_ID_CONN_MGR, conn_mgr_router_recv_handler);
|
||||
mgr->bg_ping_timer = uasync_set_timeout(instance->ua, CONN_MGR_BG_PING_INTERVAL_TB, mgr, cm_bg_ping_timer_cb, "conn_mgr_bg_ping");
|
||||
mgr->candidate_ping_timer = uasync_set_timeout(instance->ua, CONN_MGR_CANDIDATE_PING_TB, mgr, cm_candidate_ping_timer_cb, "conn_mgr_cand_ping");
|
||||
mgr->bg_ping_cycle_start_tb = get_time_tb();
|
||||
@@ -90,8 +91,16 @@ void conn_mgr_destroy(struct CONN_MGR* mgr) {
|
||||
if (!mgr) return;
|
||||
if (mgr->bg_ping_timer) { uasync_cancel_timeout(mgr->instance->ua, mgr->bg_ping_timer); mgr->bg_ping_timer = NULL; }
|
||||
if (mgr->candidate_ping_timer) { uasync_cancel_timeout(mgr->instance->ua, mgr->candidate_ping_timer); mgr->candidate_ping_timer = NULL; }
|
||||
etcp_router_unbind(mgr->instance, ETCP_ID_CONN_MGR);
|
||||
etcp_router_unbind(mgr->instance, ETCP_RT_ID_CONN_MGR);
|
||||
for (size_t i = 0; i < mgr->entry_count; i++) cm_entry_destroy(&mgr->entries[i]);
|
||||
struct cm_exchange_pending* ep = mgr->exchange_pending;
|
||||
while (ep) {
|
||||
struct cm_exchange_pending* next = ep->next;
|
||||
if (ep->timeout_timer) uasync_cancel_timeout(mgr->instance->ua, ep->timeout_timer);
|
||||
u_free(ep);
|
||||
ep = next;
|
||||
}
|
||||
mgr->exchange_pending = NULL;
|
||||
u_free(mgr->entries);
|
||||
mgr->initialized = 0;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: destroyed, entries=%zu", (size_t)mgr->entry_count);
|
||||
@@ -102,6 +111,16 @@ static void cm_entry_destroy(struct CONN_MGR_ENTRY* entry) {
|
||||
if (!entry || entry->state == CONN_MGR_STATE_DISCONNECTED) return;
|
||||
if (entry->idle_timer) { uasync_cancel_timeout(entry->mgr->instance->ua, entry->idle_timer); entry->idle_timer = NULL; }
|
||||
if (entry->main.timer) { uasync_cancel_timeout(entry->mgr->instance->ua, entry->main.timer); entry->main.timer = NULL; }
|
||||
struct cm_exchange_pending** pp = &entry->mgr->exchange_pending;
|
||||
while (*pp) {
|
||||
if ((*pp)->entry == entry) {
|
||||
if ((*pp)->timeout_timer && (*pp)->timeout_timer != entry->main.timer) {
|
||||
uasync_cancel_timeout(entry->mgr->instance->ua, (*pp)->timeout_timer);
|
||||
(*pp)->timeout_timer = NULL;
|
||||
}
|
||||
struct cm_exchange_pending* ep = *pp; *pp = ep->next; u_free(ep);
|
||||
} else pp = &(*pp)->next;
|
||||
}
|
||||
entry->state = CONN_MGR_STATE_DISCONNECTED;
|
||||
entry->conn_type = CONN_TYPE_NONE;
|
||||
}
|
||||
@@ -138,9 +157,9 @@ static void cm_deliver_result(struct CONN_MGR_ENTRY* entry, int result) {
|
||||
}
|
||||
|
||||
static void cm_update_nodeinfo(struct CONN_MGR_ENTRY* entry) {
|
||||
struct ROUTE_BGP* bgp = entry->mgr->instance->bgp;
|
||||
if (!bgp) return;
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(bgp, entry->node_id);
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(entry->mgr->instance->topo_groups);
|
||||
if (!group) return;
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(group, entry->node_id);
|
||||
if (!nq) return;
|
||||
nq->conn_mgr_type = entry->conn_type;
|
||||
memcpy(nq->conn_mgr_intermediaries, entry->intermediaries, sizeof(entry->intermediaries));
|
||||
@@ -148,7 +167,7 @@ static void cm_update_nodeinfo(struct CONN_MGR_ENTRY* entry) {
|
||||
}
|
||||
|
||||
static void cm_clear_nodeinfo(struct CONN_MGR* mgr, uint64_t node_id) {
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(mgr->instance->bgp, node_id);
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(mgr->instance->topo_groups), node_id);
|
||||
if (!nq) return;
|
||||
nq->conn_mgr_type = CONN_TYPE_NONE;
|
||||
nq->conn_mgr_intermediariy_count = 0;
|
||||
@@ -157,22 +176,22 @@ static void cm_clear_nodeinfo(struct CONN_MGR* mgr, uint64_t node_id) {
|
||||
int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id, uint32_t idle_timeout_ms,
|
||||
conn_mgr_connect_callback_t cb, void* cb_arg) {
|
||||
if (!mgr) return CONN_MGR_ERR_INTERNAL;
|
||||
struct ROUTE_BGP* bgp = mgr->instance->bgp;
|
||||
if (!bgp) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr_connect: BGP not initialized"); return CONN_MGR_ERR_INTERNAL; }
|
||||
struct NODEINFO_Q* target = nodeinfo_find_by_id(bgp, node_id);
|
||||
if (!target) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr_connect: node 0x%016llx not found", (unsigned long long)node_id); return CONN_MGR_ERR_NOT_FOUND; }
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(mgr->instance->topo_groups);
|
||||
if (!group) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "BGP not initialized"); return CONN_MGR_ERR_INTERNAL; }
|
||||
struct TOPO_NODEQ* target = topo_node_find_by_id(group, node_id);
|
||||
if (!target) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "node 0x%016llx not found", (unsigned long long)node_id); return CONN_MGR_ERR_NOT_FOUND; }
|
||||
struct CONN_MGR_ENTRY* entry = cm_find_entry(mgr, node_id);
|
||||
if (entry && entry->state == CONN_MGR_STATE_CONNECTED) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr_connect: node 0x%016llx already connected", (unsigned long long)node_id);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "node 0x%016llx already connected", (unsigned long long)node_id);
|
||||
return CONN_MGR_ERR_ALREADY_CONNECTED;
|
||||
}
|
||||
if (entry && entry->state == CONN_MGR_STATE_CONNECTING) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "conn_mgr_connect: node 0x%016llx already connecting, adding cb", (unsigned long long)node_id);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "node 0x%016llx already connecting, adding cb", (unsigned long long)node_id);
|
||||
struct cm_cb_node* cn = u_calloc(1, sizeof(struct cm_cb_node));
|
||||
if (cn) { cn->cb = cb; cn->arg = cb_arg; cn->next = entry->cb_list; entry->cb_list = cn; }
|
||||
return CONN_MGR_OK;
|
||||
}
|
||||
struct ETCP_CONN* existing = route_bgp_find_conn_for_node(bgp, node_id);
|
||||
struct ETCP_CONN* existing = topo_group_find_conn_for_node(group, node_id);
|
||||
if (existing && existing->peer_node_id == node_id && existing->links) {
|
||||
struct ETCP_LINK* l = existing->links;
|
||||
while (l) { if (l->link_status && l->initialized) break; l = l->next; }
|
||||
@@ -204,7 +223,7 @@ int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id, uint32_t idle_
|
||||
entry->main.phase = 0;
|
||||
entry->main.timer = NULL;
|
||||
entry->main.request_id = 0;
|
||||
int has_our_local = cm_has_local_addr(bgp->local_node);
|
||||
int has_our_local = cm_has_local_addr(group->local_node);
|
||||
int has_target_local = cm_has_local_addr(target);
|
||||
if (has_our_local && has_target_local) { entry->local_scan_state = CM_TRY_PENDING; cm_start_local_scan(entry); }
|
||||
entry->main_connect_state = CM_TRY_PENDING;
|
||||
@@ -217,11 +236,11 @@ int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id) {
|
||||
struct CONN_MGR_ENTRY* entry = cm_find_entry(mgr, node_id);
|
||||
if (!entry) return CONN_MGR_ERR_NOT_FOUND;
|
||||
struct CONN_MGR_DISCONNECT pkt;
|
||||
pkt.cmd = ETCP_ID_CONN_MGR;
|
||||
pkt.cmd = ETCP_RT_ID_CONN_MGR;
|
||||
pkt.subcmd = CONN_MGR_SUBCMD_DISCONNECT;
|
||||
pkt.node_id = node_id;
|
||||
struct ll_entry* qe = queue_entry_new(sizeof(pkt));
|
||||
if (qe) { memcpy(qe->data, &pkt, sizeof(pkt)); etcp_route_send(mgr->instance, node_id, qe, 1); }
|
||||
struct ll_entry* qe = queue_entry_new(0);
|
||||
if (qe) { qe->dgram = u_malloc(sizeof(pkt)); memcpy(qe->dgram, &pkt, sizeof(pkt)); qe->len = sizeof(pkt); etcp_route_send(mgr->instance, node_id, qe, 1); }
|
||||
cm_clear_nodeinfo(mgr, node_id);
|
||||
cm_entry_destroy(entry);
|
||||
entry->state = CONN_MGR_STATE_DISCONNECTED;
|
||||
@@ -251,23 +270,24 @@ int conn_mgr_get_status(struct CONN_MGR* mgr, uint64_t node_id, uint8_t* out_sta
|
||||
}
|
||||
|
||||
int conn_mgr_add_alien_node(struct CONN_MGR* mgr, const uint8_t* nodeinfo_data, size_t len) {
|
||||
if (!mgr || !nodeinfo_data || len < sizeof(struct NODEINFO)) return CONN_MGR_ERR_INTERNAL;
|
||||
struct ROUTE_BGP* bgp = mgr->instance->bgp;
|
||||
if (!bgp) return CONN_MGR_ERR_INTERNAL;
|
||||
const struct NODEINFO* ni = (const struct NODEINFO*)nodeinfo_data;
|
||||
uint64_t node_id = ni->node_id;
|
||||
struct NODEINFO_Q* existing = nodeinfo_find_by_id(bgp, node_id);
|
||||
if (!mgr || !nodeinfo_data || len < sizeof(struct TOPO_NODE)) return CONN_MGR_ERR_INTERNAL;
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(mgr->instance->topo_groups);
|
||||
if (!group) return CONN_MGR_ERR_INTERNAL;
|
||||
const struct TOPO_NODE* src = (const struct TOPO_NODE*)nodeinfo_data;
|
||||
uint64_t node_id = src->node_id;
|
||||
struct TOPO_NODEQ* existing = topo_node_find_by_id(group, node_id);
|
||||
if (existing) { DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: alien node 0x%016llx already exists, ignoring", (unsigned long long)node_id); return CONN_MGR_OK; }
|
||||
size_t dyn_sz = len - sizeof(struct NODEINFO);
|
||||
size_t data_sz = sizeof(struct NODEINFO_Q) - sizeof(struct ll_entry) + dyn_sz + 8;
|
||||
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)queue_entry_new(data_sz);
|
||||
if (!nq) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr: alien node alloc failed"); return CONN_MGR_ERR_INTERNAL; }
|
||||
memcpy(&nq->node, nodeinfo_data, len);
|
||||
nq->alien = 1;
|
||||
nq->dirty = 0;
|
||||
nq->last_ver = ni->ver;
|
||||
struct TOPO_NODE* ni = u_calloc(1, sizeof(struct TOPO_NODE));
|
||||
if (!ni) return CONN_MGR_ERR_INTERNAL;
|
||||
memcpy(ni, nodeinfo_data, len < sizeof(*ni) ? len : sizeof(*ni));
|
||||
ni->ref_count = 1;
|
||||
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)queue_entry_new(sizeof(struct TOPO_NODEQ));
|
||||
if (!nq) { u_free(ni); return CONN_MGR_ERR_INTERNAL; }
|
||||
nq->node = ni; topo_node_ref(ni);
|
||||
nq->hash_node_id = ni->node_id;
|
||||
nq->alien = 1; nq->dirty = 0; nq->last_ver = ni->ver;
|
||||
nq->connectivity.ping_req_time = 0;
|
||||
if (bgp->nodes) queue_data_put_with_index(bgp->nodes, &nq->ll);
|
||||
if (group->nodes) queue_data_put_with_index(group->nodes, &nq->ll);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: added alien node 0x%016llx", (unsigned long long)node_id);
|
||||
return CONN_MGR_OK;
|
||||
}
|
||||
@@ -277,7 +297,7 @@ int conn_mgr_send(struct CONN_MGR* mgr, uint64_t node_id, struct ll_entry* entry
|
||||
struct CONN_MGR_ENTRY* e = cm_find_entry(mgr, node_id);
|
||||
if (!e || e->state != CONN_MGR_STATE_CONNECTED) { queue_entry_free(entry); return -1; }
|
||||
e->last_traffic_tb = get_time_tb();
|
||||
struct ETCP_ROUTER_CONN* rconn = etcp_router_conn_get(mgr->instance, node_id, ETCP_ID_CONN_MGR);
|
||||
struct ETCP_ROUTER_CONN* rconn = etcp_router_conn_get(mgr->instance, node_id, ETCP_RT_ID_CONN_MGR);
|
||||
if (!rconn) { queue_entry_free(entry); return -1; }
|
||||
int ret = etcp_router_conn_send(rconn, entry->dgram, entry->len);
|
||||
queue_entry_free(entry);
|
||||
@@ -333,19 +353,17 @@ static void cm_idle_timer_cb(void* arg) {
|
||||
entry->idle_timer = uasync_set_timeout(mgr->instance->ua, CONN_MGR_IDLE_CHECK_INTERVAL_TB, entry, cm_idle_timer_cb, "conn_mgr_idle");
|
||||
}
|
||||
|
||||
static int cm_nat_compatible(struct ETCP_SOCKET* our, uint8_t target_meta_type) {
|
||||
uint8_t tt = target_meta_type;
|
||||
if (tt == NAT_VERIFIED_DIRECT) tt = CFG_SERVER_TYPE_PUBLIC;
|
||||
else if (tt >= NAT_VERIFIED_UNKNOWN) tt = CFG_SERVER_TYPE_PUBLIC;
|
||||
static int cm_nat_compatible(struct ETCP_SOCKET* our, uint8_t target_config_type, uint8_t target_nat_type) {
|
||||
uint8_t tt = target_nat_type >= NAT_VERIFIED_UNKNOWN ? CFG_SERVER_TYPE_PUBLIC : target_config_type;
|
||||
uint8_t ot = our->type;
|
||||
if (ot == CFG_SERVER_TYPE_PUBLIC) return 1;
|
||||
if (ot == CFG_SERVER_TYPE_NAT && our->nat_type == NAT_TYPE_EIM && tt == CFG_SERVER_TYPE_PUBLIC) return 1;
|
||||
if (ot == CFG_SERVER_TYPE_NAT && our->nat_type == NAT_TYPE_EIM && tt == CFG_SERVER_TYPE_NAT) return 1;
|
||||
if (ot == CFG_SERVER_TYPE_PRIVATE && tt == CFG_SERVER_TYPE_PRIVATE) return 2;
|
||||
if (ot == CFG_SERVER_TYPE_PUBLIC || ot == CFG_SERVER_TYPE_UNKNOWN) return 1;
|
||||
if (ot == CFG_SERVER_TYPE_NAT && our->nat_type == NAT_VERIFIED_EIM && tt == CFG_SERVER_TYPE_PUBLIC) return 1;
|
||||
if (ot == CFG_SERVER_TYPE_NAT && our->nat_type == NAT_VERIFIED_EIM && tt == CFG_SERVER_TYPE_NAT) return 1;
|
||||
if ((ot == CFG_SERVER_TYPE_PRIVATE || ot == CFG_SERVER_TYPE_LOCAL) && tt == CFG_SERVER_TYPE_PRIVATE) return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t cm_get_node_min_rtt(struct NODEINFO_Q* nq) {
|
||||
static uint16_t cm_get_node_min_rtt(struct TOPO_NODEQ* nq) {
|
||||
uint16_t rtt = 0xFFFF;
|
||||
if (nq->connectivity.interface_status == PROBE_RESULT_REACHABLE && nq->connectivity.interface_min_rtt < rtt)
|
||||
rtt = nq->connectivity.interface_min_rtt;
|
||||
@@ -356,29 +374,25 @@ static uint16_t cm_get_node_min_rtt(struct NODEINFO_Q* nq) {
|
||||
return rtt;
|
||||
}
|
||||
|
||||
static uint64_t cm_get_node_max_probe_time(struct NODEINFO_Q* nq) {
|
||||
static uint64_t cm_get_node_max_probe_time(struct TOPO_NODEQ* nq) {
|
||||
uint64_t t = nq->connectivity.interface_probe_time;
|
||||
if (nq->connectivity.nat_probe_time > t) t = nq->connectivity.nat_probe_time;
|
||||
if (nq->connectivity.real_probe_time > t) t = nq->connectivity.real_probe_time;
|
||||
return t;
|
||||
}
|
||||
|
||||
static int cm_is_rtt_fresh(struct NODEINFO_Q* nq, uint64_t now_tb) {
|
||||
static int cm_is_rtt_fresh(struct TOPO_NODEQ* nq, uint64_t now_tb) {
|
||||
uint64_t last = cm_get_node_max_probe_time(nq);
|
||||
return (last > 0 && (now_tb - last) < (uint64_t)(CONN_MGR_CANDIDATE_CACHE_MS * 10));
|
||||
}
|
||||
|
||||
static int cm_has_direct_ip(struct NODEINFO_Q* nq) {
|
||||
if (!nq) return 0;
|
||||
const struct NODEINFO_IPV4_ADDR* addrs = NULL;
|
||||
const struct NODEINFO_IPV4_SOCKET_META* metas = NULL;
|
||||
int addr_count = get_node_v4_addrs(nq, &addrs);
|
||||
int meta_count = get_node_v4_sockets_meta(nq, &metas);
|
||||
for (int i = 0; i < addr_count; i++) {
|
||||
if (addrs[i].type == ADDR_TYPE_REAL || addrs[i].type == ADDR_TYPE_NAT) {
|
||||
for (int j = 0; j < meta_count; j++) {
|
||||
if (metas[j].id == addrs[i].socket_id &&
|
||||
(metas[j].type == CFG_SERVER_TYPE_PUBLIC || metas[j].type == NAT_VERIFIED_EIM || metas[j].type == NAT_VERIFIED_DIRECT))
|
||||
static int cm_has_direct_ip(struct TOPO_NODEQ* nq) {
|
||||
if (!nq || !nq->node) return 0;
|
||||
for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) {
|
||||
if (a->type == TOPO_ADDR_REAL || a->type == TOPO_ADDR_NAT) {
|
||||
for (const struct TOPO_SOCKMETA4* m = nq->node->v4_sock_meta; m; m = m->next) {
|
||||
if (m->id == a->socket_id && (m->config_type == CFG_SERVER_TYPE_PUBLIC || m->config_type == CFG_SERVER_TYPE_UNKNOWN
|
||||
|| m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -386,16 +400,12 @@ static int cm_has_direct_ip(struct NODEINFO_Q* nq) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cm_has_local_addr(struct NODEINFO_Q* nq) {
|
||||
if (!nq) return 0;
|
||||
const struct NODEINFO_IPV4_ADDR* addrs = NULL;
|
||||
const struct NODEINFO_IPV4_SOCKET_META* metas = NULL;
|
||||
int addr_count = get_node_v4_addrs(nq, &addrs);
|
||||
int meta_count = get_node_v4_sockets_meta(nq, &metas);
|
||||
for (int i = 0; i < addr_count; i++) {
|
||||
if (addrs[i].type == ADDR_TYPE_INTERFACE) {
|
||||
for (int j = 0; j < meta_count; j++) {
|
||||
if (metas[j].id == addrs[i].socket_id && metas[j].type == CFG_SERVER_TYPE_PRIVATE)
|
||||
static int cm_has_local_addr(struct TOPO_NODEQ* nq) {
|
||||
if (!nq || !nq->node) return 0;
|
||||
for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) {
|
||||
if (a->type == TOPO_ADDR_INTERFACE) {
|
||||
for (const struct TOPO_SOCKMETA4* m = nq->node->v4_sock_meta; m; m = m->next) {
|
||||
if (m->id == a->socket_id && (m->config_type == CFG_SERVER_TYPE_PRIVATE || m->config_type == CFG_SERVER_TYPE_LOCAL))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -416,37 +426,22 @@ struct cm_ping_ctx {
|
||||
};
|
||||
|
||||
static void cm_start_local_scan(struct CONN_MGR_ENTRY* entry) {
|
||||
struct ROUTE_BGP* bgp = entry->mgr->instance->bgp;
|
||||
struct NODEINFO_Q* target = nodeinfo_find_by_id(bgp, entry->node_id);
|
||||
if (!target) { entry->local_scan_state = CM_TRY_FAILED; return; }
|
||||
const struct NODEINFO_IPV4_ADDR* addrs = NULL;
|
||||
const struct NODEINFO_IPV4_SOCKET_META* metas = NULL;
|
||||
int addr_count = get_node_v4_addrs(target, &addrs);
|
||||
int meta_count = get_node_v4_sockets_meta(target, &metas);
|
||||
for (int i = 0; i < addr_count; i++) {
|
||||
if (addrs[i].type != ADDR_TYPE_INTERFACE) continue;
|
||||
for (int j = 0; j < meta_count; j++) {
|
||||
if (metas[j].id != addrs[i].socket_id || metas[j].type != CFG_SERVER_TYPE_PRIVATE) continue;
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(entry->mgr->instance->topo_groups);
|
||||
struct TOPO_NODEQ* target = topo_node_find_by_id(group, entry->node_id);
|
||||
if (!target || !target->node) { entry->local_scan_state = CM_TRY_FAILED; return; }
|
||||
for (const struct TOPO_ADDR4* a = target->node->v4_addrs; a; a = a->next) {
|
||||
if (a->type != TOPO_ADDR_INTERFACE) continue;
|
||||
for (const struct TOPO_SOCKMETA4* m = target->node->v4_sock_meta; m; m = m->next) {
|
||||
if (m->id != a->socket_id || (m->config_type != CFG_SERVER_TYPE_PRIVATE && m->config_type != CFG_SERVER_TYPE_LOCAL)) continue;
|
||||
struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets;
|
||||
while (s) {
|
||||
if (s->type == CFG_SERVER_TYPE_PRIVATE && s->local_addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in sin;
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
memcpy(&sin.sin_addr.s_addr, addrs[i].addr, 4);
|
||||
sin.sin_port = htons(addrs[i].port);
|
||||
struct sockaddr_storage sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
memcpy(&sa, &sin, sizeof(sin));
|
||||
struct cm_ping_ctx* ctx = u_calloc(1, sizeof(struct cm_ping_ctx));
|
||||
if (!ctx) continue;
|
||||
ctx->entry = entry;
|
||||
ctx->addr = sa;
|
||||
ctx->sock = s;
|
||||
ctx->phase = 0;
|
||||
ctx->attempt = 0;
|
||||
etcp_send_ping_to_socket(entry->mgr->instance, s, target->node.public_key, &sa,
|
||||
CONN_MGR_LOCAL_SCAN_TIMEOUT_MS, cm_ping_cb, ctx, NULL, 0);
|
||||
if ((s->type == CFG_SERVER_TYPE_PRIVATE || s->type == CFG_SERVER_TYPE_LOCAL) && s->local_addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET;
|
||||
memcpy(&sin.sin_addr.s_addr, a->addr, 4); sin.sin_port = htons(a->port);
|
||||
struct sockaddr_storage sa; memset(&sa, 0, sizeof(sa)); memcpy(&sa, &sin, sizeof(sin));
|
||||
struct cm_ping_ctx* ctx = u_calloc(1, sizeof(struct cm_ping_ctx)); if (!ctx) continue;
|
||||
ctx->entry = entry; ctx->addr = sa; ctx->sock = s; ctx->phase = 0; ctx->attempt = 0;
|
||||
etcp_send_ping_to_socket(entry->mgr->instance, s, target->node->public_key, &sa, CONN_MGR_LOCAL_SCAN_TIMEOUT_MS, cm_ping_cb, ctx, NULL, 0);
|
||||
return;
|
||||
}
|
||||
s = s->next;
|
||||
@@ -454,47 +449,33 @@ static void cm_start_local_scan(struct CONN_MGR_ENTRY* entry) {
|
||||
}
|
||||
}
|
||||
entry->local_scan_state = CM_TRY_FAILED;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: local scan no candidates for 0x%016llx", (unsigned long long)entry->node_id);
|
||||
}
|
||||
|
||||
static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) {
|
||||
if (entry->main_connect_state == CM_TRY_OK) return;
|
||||
if (entry->local_scan_state == CM_TRY_OK) return;
|
||||
struct ROUTE_BGP* bgp = entry->mgr->instance->bgp;
|
||||
struct NODEINFO_Q* target = nodeinfo_find_by_id(bgp, entry->node_id);
|
||||
if (!target) { cm_deliver_result(entry, CONN_MGR_ERR_NOT_FOUND); return; }
|
||||
const struct NODEINFO_IPV4_ADDR* addrs = NULL;
|
||||
const struct NODEINFO_IPV4_SOCKET_META* metas = NULL;
|
||||
int addr_count = get_node_v4_addrs(target, &addrs);
|
||||
int meta_count = get_node_v4_sockets_meta(target, &metas);
|
||||
uint8_t priority_order[] = { ADDR_TYPE_REAL, ADDR_TYPE_NAT, ADDR_TYPE_INTERFACE };
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(entry->mgr->instance->topo_groups);
|
||||
struct TOPO_NODEQ* target = topo_node_find_by_id(group, entry->node_id);
|
||||
if (!target || !target->node) { cm_deliver_result(entry, CONN_MGR_ERR_NOT_FOUND); return; }
|
||||
uint8_t priority_order[] = { TOPO_ADDR_REAL, TOPO_ADDR_NAT, TOPO_ADDR_INTERFACE };
|
||||
for (int pri = 0; pri < 3; pri++) {
|
||||
for (int i = 0; i < addr_count; i++) {
|
||||
if (addrs[i].type != priority_order[pri]) continue;
|
||||
for (int j = 0; j < meta_count; j++) {
|
||||
if (metas[j].id != addrs[i].socket_id) continue;
|
||||
for (const struct TOPO_ADDR4* a = target->node->v4_addrs; a; a = a->next) {
|
||||
if (a->type != priority_order[pri]) continue;
|
||||
for (const struct TOPO_SOCKMETA4* m = target->node->v4_sock_meta; m; m = m->next) {
|
||||
if (m->id != a->socket_id) continue;
|
||||
struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets;
|
||||
while (s) {
|
||||
int compat = cm_nat_compatible(s, metas[j].type);
|
||||
if (compat && s->local_addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in sin;
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
memcpy(&sin.sin_addr.s_addr, addrs[i].addr, 4);
|
||||
sin.sin_port = htons(addrs[i].port);
|
||||
struct sockaddr_storage sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
memcpy(&sa, &sin, sizeof(sin));
|
||||
if (cm_nat_compatible(s, m->config_type, m->nat_type) && s->local_addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET;
|
||||
memcpy(&sin.sin_addr.s_addr, a->addr, 4); sin.sin_port = htons(a->port);
|
||||
struct sockaddr_storage sa; memset(&sa, 0, sizeof(sa)); memcpy(&sa, &sin, sizeof(sin));
|
||||
struct ETCP_CONN* conn = etcp_connection_create(entry->mgr->instance, NULL);
|
||||
if (conn) {
|
||||
etcp_conn_set_ready_cbk(conn, cm_direct_ready_cb, entry);
|
||||
sc_set_peer_public_key(&conn->crypto_ctx, target->node.public_key, 0);
|
||||
struct ETCP_LINK* link = etcp_link_new(conn, s, &sa, 0);
|
||||
if (link) {
|
||||
entry->main.timer = uasync_set_timeout(entry->mgr->instance->ua,
|
||||
CONN_MGR_CONNECT_DIRECT_TIMEOUT_MS * 10, entry, cm_direct_timeout_cb, "conn_mgr_direct");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: phase 1 (direct) started INIT to 0x%016llx",
|
||||
(unsigned long long)entry->node_id);
|
||||
sc_init_ctx(&conn->crypto_ctx, &entry->mgr->instance->my_keys);
|
||||
sc_set_peer_public_key(&conn->crypto_ctx, target->node->public_key, 0);
|
||||
if (etcp_link_new(conn, s, &sa, 0)) {
|
||||
entry->main.timer = uasync_set_timeout(entry->mgr->instance->ua, CONN_MGR_CONNECT_DIRECT_TIMEOUT_MS * 10, entry, cm_direct_timeout_cb, "conn_mgr_direct");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -504,9 +485,7 @@ static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) {
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: phase 1 (direct) no compatible pairs for 0x%016llx",
|
||||
(unsigned long long)entry->node_id);
|
||||
int has_our = cm_has_direct_ip(bgp->local_node);
|
||||
int has_our = cm_has_direct_ip(group->local_node);
|
||||
int has_target = cm_has_direct_ip(target);
|
||||
if (has_our && !has_target) { cm_start_phase_reverse(entry); return; }
|
||||
cm_start_phase_indirect(entry);
|
||||
@@ -515,9 +494,9 @@ static void cm_start_phase_direct(struct CONN_MGR_ENTRY* entry) {
|
||||
static void cm_direct_timeout_cb(void* arg) {
|
||||
struct CONN_MGR_ENTRY* entry = (struct CONN_MGR_ENTRY*)arg;
|
||||
entry->main.timer = NULL;
|
||||
struct ROUTE_BGP* bgp = entry->mgr->instance->bgp;
|
||||
struct NODEINFO_Q* target = nodeinfo_find_by_id(bgp, entry->node_id);
|
||||
int has_our = cm_has_direct_ip(bgp->local_node);
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(entry->mgr->instance->topo_groups);
|
||||
struct TOPO_NODEQ* target = topo_node_find_by_id(group, entry->node_id);
|
||||
int has_our = cm_has_direct_ip(group->local_node);
|
||||
int has_target = target ? cm_has_direct_ip(target) : 0;
|
||||
if (entry->local_scan_state != CM_TRY_OK) {
|
||||
if (has_our && !has_target) { cm_start_phase_reverse(entry); return; }
|
||||
@@ -564,7 +543,7 @@ static void cm_ping_cb(int success, uint16_t rtt, void* arg, uint64_t nonce,
|
||||
ctx->attempt++;
|
||||
if (ctx->attempt < CONN_MGR_LOCAL_SCAN_ATTEMPTS && ctx->phase == 0) {
|
||||
etcp_send_ping_to_socket(entry->mgr->instance, ctx->sock,
|
||||
nodeinfo_find_by_id(entry->mgr->instance->bgp, entry->node_id)->node.public_key,
|
||||
topo_node_find_by_id(topo_groups_get_default(entry->mgr->instance->topo_groups), entry->node_id)->node->public_key,
|
||||
&ctx->addr, CONN_MGR_LOCAL_SCAN_TIMEOUT_MS, cm_ping_cb, ctx, NULL, 0);
|
||||
return;
|
||||
}
|
||||
@@ -575,29 +554,21 @@ static void cm_ping_cb(int success, uint16_t rtt, void* arg, uint64_t nonce,
|
||||
}
|
||||
|
||||
static void cm_start_phase_reverse(struct CONN_MGR_ENTRY* entry) {
|
||||
struct ROUTE_BGP* bgp = entry->mgr->instance->bgp;
|
||||
struct NODEINFO_Q* local = bgp->local_node;
|
||||
if (!local) { cm_deliver_result(entry, CONN_MGR_ERR_INTERNAL); return; }
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(entry->mgr->instance->topo_groups);
|
||||
struct TOPO_NODEQ* local = group->local_node;
|
||||
if (!local || !local->node) { cm_deliver_result(entry, CONN_MGR_ERR_INTERNAL); return; }
|
||||
uint32_t req_id = ++entry->mgr->next_request_id;
|
||||
entry->main.request_id = req_id;
|
||||
entry->main.phase = 2;
|
||||
const struct NODEINFO_IPV4_ADDR* addrs = NULL;
|
||||
const struct NODEINFO_IPV4_SOCKET_META* metas = NULL;
|
||||
int addr_count = get_node_v4_addrs(local, &addrs);
|
||||
int meta_count = get_node_v4_sockets_meta(local, &metas);
|
||||
entry->main.request_id = req_id; entry->main.phase = 2;
|
||||
uint8_t direct_count = 0;
|
||||
struct { uint8_t type; uint8_t ip[4]; uint16_t port; uint8_t socket_id; } out_addrs[8];
|
||||
for (int i = 0; i < addr_count && direct_count < 8; i++) {
|
||||
if (addrs[i].type != ADDR_TYPE_REAL && addrs[i].type != ADDR_TYPE_NAT) continue;
|
||||
for (int j = 0; j < meta_count; j++) {
|
||||
if (metas[j].id == addrs[i].socket_id &&
|
||||
(metas[j].type == CFG_SERVER_TYPE_PUBLIC || metas[j].type == NAT_VERIFIED_EIM || metas[j].type == NAT_VERIFIED_DIRECT)) {
|
||||
out_addrs[direct_count].type = addrs[i].type;
|
||||
memcpy(out_addrs[direct_count].ip, addrs[i].addr, 4);
|
||||
out_addrs[direct_count].port = htons(addrs[i].port);
|
||||
out_addrs[direct_count].socket_id = addrs[i].socket_id;
|
||||
direct_count++;
|
||||
break;
|
||||
for (const struct TOPO_ADDR4* a = local->node->v4_addrs; a && direct_count < 8; a = a->next) {
|
||||
if (a->type != TOPO_ADDR_REAL && a->type != TOPO_ADDR_NAT) continue;
|
||||
for (const struct TOPO_SOCKMETA4* m = local->node->v4_sock_meta; m; m = m->next) {
|
||||
if (m->id == a->socket_id && (m->config_type == CFG_SERVER_TYPE_PUBLIC || m->config_type == CFG_SERVER_TYPE_UNKNOWN
|
||||
|| m->nat_type == NAT_VERIFIED_EIM || m->nat_type == NAT_VERIFIED_DIRECT)) {
|
||||
out_addrs[direct_count].type = a->type; memcpy(out_addrs[direct_count].ip, a->addr, 4);
|
||||
out_addrs[direct_count].port = htons(a->port); out_addrs[direct_count].socket_id = a->socket_id;
|
||||
direct_count++; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -606,7 +577,7 @@ static void cm_start_phase_reverse(struct CONN_MGR_ENTRY* entry) {
|
||||
uint8_t* pkt = u_malloc(pkt_size);
|
||||
if (!pkt) { cm_deliver_result(entry, CONN_MGR_ERR_INTERNAL); return; }
|
||||
struct CONN_MGR_DIRECT_REQ* req = (struct CONN_MGR_DIRECT_REQ*)pkt;
|
||||
req->cmd = ETCP_ID_CONN_MGR;
|
||||
req->cmd = ETCP_RT_ID_CONN_MGR;
|
||||
req->subcmd = CONN_MGR_SUBCMD_DIRECT_REQ;
|
||||
req->request_id = req_id;
|
||||
req->addr_count = direct_count;
|
||||
@@ -617,11 +588,10 @@ static void cm_start_phase_reverse(struct CONN_MGR_ENTRY* entry) {
|
||||
memcpy(p, &out_addrs[i].port, 2); p += 2;
|
||||
*p++ = out_addrs[i].socket_id;
|
||||
}
|
||||
struct ll_entry* qe = queue_entry_new(pkt_size);
|
||||
struct ll_entry* qe = queue_entry_new(0);
|
||||
if (!qe) { u_free(pkt); cm_deliver_result(entry, CONN_MGR_ERR_INTERNAL); return; }
|
||||
memcpy(qe->data, pkt, pkt_size);
|
||||
qe->dgram = pkt;
|
||||
qe->len = (uint16_t)pkt_size;
|
||||
u_free(pkt);
|
||||
etcp_route_send(entry->mgr->instance, entry->node_id, qe, 1);
|
||||
|
||||
struct cm_reverse_pending* rp = u_calloc(1, sizeof(struct cm_reverse_pending));
|
||||
@@ -660,7 +630,7 @@ static void cm_reverse_ready_cb(struct ETCP_CONN* conn, void* arg) {
|
||||
static int cm_start_phase_indirect(struct CONN_MGR_ENTRY* entry) {
|
||||
struct CONN_MGR* mgr = entry->mgr;
|
||||
if (mgr->best_candidate_count == 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr: phase 3 (indirect) no candidates for 0x%016llx",
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "conn_mgr: phase 3 (indirect) no candidates for 0x%016llx (direct+reverse failed)",
|
||||
(unsigned long long)entry->node_id);
|
||||
return CONN_MGR_ERR_UNREACHABLE;
|
||||
}
|
||||
@@ -669,15 +639,17 @@ static int cm_start_phase_indirect(struct CONN_MGR_ENTRY* entry) {
|
||||
entry->main.phase = 3;
|
||||
struct CONN_MGR_INTERM_EXCHANGE_REQ req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.cmd = ETCP_ID_CONN_MGR;
|
||||
req.cmd = ETCP_RT_ID_CONN_MGR;
|
||||
req.subcmd = CONN_MGR_SUBCMD_INTERM_EXCHANGE_REQ;
|
||||
req.request_id = req_id;
|
||||
req.candidate_count = mgr->best_candidate_count > 4 ? 4 : mgr->best_candidate_count;
|
||||
for (uint8_t i = 0; i < req.candidate_count; i++)
|
||||
req.candidates[i] = mgr->best_candidates[i];
|
||||
struct ll_entry* qe = queue_entry_new(sizeof(req));
|
||||
struct ll_entry* qe = queue_entry_new(0);
|
||||
if (!qe) return CONN_MGR_ERR_INTERNAL;
|
||||
memcpy(qe->data, &req, sizeof(req));
|
||||
qe->dgram = u_malloc(sizeof(req));
|
||||
memcpy(qe->dgram, &req, sizeof(req));
|
||||
qe->len = (uint16_t)sizeof(req);
|
||||
etcp_route_send(entry->mgr->instance, entry->node_id, qe, 1);
|
||||
entry->main.timer = uasync_set_timeout(mgr->instance->ua, CONN_MGR_INTERM_EXCHANGE_TIMEOUT_MS * 10,
|
||||
entry, cm_exchange_timeout_cb, "conn_mgr_interm_exch");
|
||||
@@ -716,7 +688,7 @@ static void cm_compute_intermediaries(struct CONN_MGR_ENTRY* entry, struct CONN_
|
||||
}
|
||||
for (uint8_t i = 0; i < resp->my_count && count < 8; i++) {
|
||||
uint64_t id = resp->my_candidates[i].node_id;
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(entry->mgr->instance->bgp, id);
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(entry->mgr->instance->topo_groups), id);
|
||||
uint16_t our_rtt = nq ? cm_get_node_min_rtt(nq) : 0;
|
||||
if (our_rtt == 0xFFFF) our_rtt = 0;
|
||||
all[count].node_id = id;
|
||||
@@ -735,18 +707,18 @@ static void cm_compute_intermediaries(struct CONN_MGR_ENTRY* entry, struct CONN_
|
||||
|
||||
struct CONN_MGR_INTERM_SELECTED pkt;
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
pkt.cmd = ETCP_ID_CONN_MGR;
|
||||
pkt.cmd = ETCP_RT_ID_CONN_MGR;
|
||||
pkt.subcmd = CONN_MGR_SUBCMD_INTERM_SELECTED;
|
||||
pkt.request_id = 0;
|
||||
pkt.count = sel;
|
||||
for (uint8_t i = 0; i < sel; i++) {
|
||||
pkt.selected[i].node_id = all[i].node_id;
|
||||
pkt.selected[i].rtt = 0;
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(entry->mgr->instance->bgp, all[i].node_id);
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(entry->mgr->instance->topo_groups), all[i].node_id);
|
||||
if (nq) pkt.selected[i].rtt = cm_get_node_min_rtt(nq);
|
||||
}
|
||||
struct ll_entry* qe = queue_entry_new(sizeof(pkt));
|
||||
if (qe) { memcpy(qe->data, &pkt, sizeof(pkt)); etcp_route_send(entry->mgr->instance, entry->node_id, qe, 1); }
|
||||
struct ll_entry* qe = queue_entry_new(0);
|
||||
if (qe) { qe->dgram = u_malloc(sizeof(pkt)); memcpy(qe->dgram, &pkt, sizeof(pkt)); qe->len = sizeof(pkt); etcp_route_send(entry->mgr->instance, entry->node_id, qe, 1); }
|
||||
|
||||
entry->conn_type = CONN_TYPE_INDIRECT;
|
||||
entry->state = CONN_MGR_STATE_CONNECTED;
|
||||
@@ -756,6 +728,26 @@ static void cm_compute_intermediaries(struct CONN_MGR_ENTRY* entry, struct CONN_
|
||||
cm_deliver_result(entry, CONN_MGR_OK);
|
||||
}
|
||||
|
||||
static void cm_exchange_probe_retry_cb(void* arg) {
|
||||
struct cm_exchange_pending* ep = (struct cm_exchange_pending*)arg;
|
||||
struct CONN_MGR* mgr = ep->entry->mgr;
|
||||
ep->timeout_timer = NULL;
|
||||
uint8_t need_probe = 0;
|
||||
uint64_t now_tb = get_time_tb();
|
||||
for (uint8_t i = 0; i < ep->cached_resp.my_count && i < 4; i++) {
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(mgr->instance->topo_groups), ep->cached_resp.my_candidates[i].node_id);
|
||||
if (!nq || !cm_is_rtt_fresh(nq, now_tb)) { need_probe++; break; }
|
||||
}
|
||||
if (need_probe == 0) {
|
||||
cm_compute_intermediaries(ep->entry, &ep->cached_resp);
|
||||
struct cm_exchange_pending** pp = &mgr->exchange_pending;
|
||||
while (*pp) { if (*pp == ep) { *pp = ep->next; break; } pp = &(*pp)->next; }
|
||||
u_free(ep);
|
||||
} else {
|
||||
ep->timeout_timer = uasync_set_timeout(mgr->instance->ua, 2000, ep, cm_exchange_probe_retry_cb, "cm_exch_probe");
|
||||
}
|
||||
}
|
||||
|
||||
static void cm_handle_interm_exchange_resp(struct CONN_MGR* mgr, const uint8_t* data, size_t len) {
|
||||
const struct CONN_MGR_INTERM_EXCHANGE_RESP* resp = (const struct CONN_MGR_INTERM_EXCHANGE_RESP*)data;
|
||||
size_t min_size = offsetof(struct CONN_MGR_INTERM_EXCHANGE_RESP, your_candidates);
|
||||
@@ -773,14 +765,14 @@ static void cm_handle_interm_exchange_resp(struct CONN_MGR* mgr, const uint8_t*
|
||||
uint8_t need_probe = 0;
|
||||
uint64_t now_tb = get_time_tb();
|
||||
for (uint8_t i = 0; i < ep->cached_resp.my_count && i < 4; i++) {
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(mgr->instance->bgp, ep->cached_resp.my_candidates[i].node_id);
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(mgr->instance->topo_groups), ep->cached_resp.my_candidates[i].node_id);
|
||||
if (!nq || !cm_is_rtt_fresh(nq, now_tb)) { need_probe++; route_connectivity_probe_node(mgr->instance, nq); }
|
||||
}
|
||||
if (need_probe == 0) { ep->probes_done = 1; cm_compute_intermediaries(ep->entry, &ep->cached_resp);
|
||||
struct cm_exchange_pending** pp = &mgr->exchange_pending;
|
||||
while (*pp) { if (*pp == ep) { *pp = ep->next; break; } pp = &(*pp)->next; }
|
||||
u_free(ep); return; }
|
||||
uasync_set_timeout(mgr->instance->ua, 2000, ep, NULL, "cm_exch_probe");
|
||||
ep->timeout_timer = uasync_set_timeout(mgr->instance->ua, 2000, ep, cm_exchange_probe_retry_cb, "cm_exch_probe");
|
||||
}
|
||||
|
||||
static void cm_handle_interm_selected(struct CONN_MGR* mgr, const uint8_t* data, size_t len) {
|
||||
@@ -854,7 +846,7 @@ static void cm_handle_direct_req(struct ETCP_CONN* conn, const uint8_t* data, si
|
||||
(void)sock_id; (void)type;
|
||||
struct ETCP_SOCKET* s = conn->instance->etcp_sockets;
|
||||
while (s) {
|
||||
if (s->type == CFG_SERVER_TYPE_PUBLIC && s->local_addr.ss_family == AF_INET) {
|
||||
if ((s->type == CFG_SERVER_TYPE_PUBLIC || s->type == CFG_SERVER_TYPE_UNKNOWN) && s->local_addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in sin;
|
||||
memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET;
|
||||
memcpy(&sin.sin_addr.s_addr, ip, 4); sin.sin_port = port;
|
||||
@@ -862,8 +854,9 @@ static void cm_handle_direct_req(struct ETCP_CONN* conn, const uint8_t* data, si
|
||||
memset(&sa, 0, sizeof(sa)); memcpy(&sa, &sin, sizeof(sin));
|
||||
struct ETCP_CONN* new_conn = etcp_connection_create(conn->instance, NULL);
|
||||
if (new_conn) {
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(conn->instance->bgp, src_node_id);
|
||||
if (nq) sc_set_peer_public_key(&new_conn->crypto_ctx, nq->node.public_key, 0);
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(conn->instance->topo_groups), src_node_id);
|
||||
if (nq) { sc_init_ctx(&new_conn->crypto_ctx, &conn->instance->my_keys);
|
||||
sc_set_peer_public_key(&new_conn->crypto_ctx, nq->node->public_key, 0); }
|
||||
struct cm_reverse_pending* rp = u_calloc(1, sizeof(struct cm_reverse_pending));
|
||||
if (rp) { rp->request_id = req->request_id; rp->entry = NULL; rp->next = mgr->reverse_pending;
|
||||
etcp_conn_set_ready_cbk(new_conn, cm_reverse_ready_cb, rp); mgr->reverse_pending = rp; }
|
||||
@@ -883,13 +876,13 @@ static void cm_handle_interm_exchange_req(struct ETCP_CONN* conn,
|
||||
uint64_t now_tb = get_time_tb();
|
||||
for (uint8_t i = 0; i < req->candidate_count && i < 4; i++) {
|
||||
uint64_t cand_id = req->candidates[i].node_id;
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(mgr->instance->bgp, cand_id);
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(mgr->instance->topo_groups), cand_id);
|
||||
if (nq && cm_is_rtt_fresh(nq, now_tb)) continue;
|
||||
route_connectivity_probe_node(mgr->instance, nq);
|
||||
}
|
||||
struct CONN_MGR_INTERM_EXCHANGE_RESP resp;
|
||||
memset(&resp, 0, sizeof(resp));
|
||||
resp.cmd = ETCP_ID_CONN_MGR;
|
||||
resp.cmd = ETCP_RT_ID_CONN_MGR;
|
||||
resp.subcmd = CONN_MGR_SUBCMD_INTERM_EXCHANGE_RESP;
|
||||
resp.request_id = req->request_id;
|
||||
resp.my_count = mgr->best_candidate_count > 4 ? 4 : mgr->best_candidate_count;
|
||||
@@ -898,15 +891,17 @@ static void cm_handle_interm_exchange_req(struct ETCP_CONN* conn,
|
||||
resp.your_count = req->candidate_count > 4 ? 4 : req->candidate_count;
|
||||
for (uint8_t i = 0; i < resp.your_count; i++) {
|
||||
resp.your_candidates[i].node_id = req->candidates[i].node_id;
|
||||
struct NODEINFO_Q* nq2 = nodeinfo_find_by_id(mgr->instance->bgp, req->candidates[i].node_id);
|
||||
struct TOPO_NODEQ* nq2 = topo_node_find_by_id(topo_groups_get_default(mgr->instance->topo_groups), req->candidates[i].node_id);
|
||||
uint16_t rtt = nq2 ? cm_get_node_min_rtt(nq2) : 0;
|
||||
resp.your_candidates[i].rtt = rtt;
|
||||
}
|
||||
size_t resp_size = offsetof(struct CONN_MGR_INTERM_EXCHANGE_RESP, your_candidates) +
|
||||
(size_t)resp.your_count * sizeof(struct CONN_MGR_CANDIDATE);
|
||||
struct ll_entry* qe = queue_entry_new(resp_size);
|
||||
struct ll_entry* qe = queue_entry_new(0);
|
||||
if (qe) {
|
||||
memcpy(qe->data, &resp, resp_size);
|
||||
qe->dgram = u_malloc(resp_size);
|
||||
memcpy(qe->dgram, &resp, resp_size);
|
||||
qe->len = (uint16_t)resp_size;
|
||||
etcp_route_send(mgr->instance, conn->peer_node_id, qe, 1);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: sent EXCHANGE_RESP to 0x%016llx my=%u your=%u",
|
||||
@@ -921,15 +916,20 @@ static void cm_handle_disconnect(struct CONN_MGR* mgr, uint64_t node_id) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: remote disconnect from 0x%016llx", (unsigned long long)node_id);
|
||||
}
|
||||
|
||||
static void cm_bg_ping_noop_cb(int success, uint16_t rtt, void* arg, uint64_t nonce,
|
||||
const uint8_t* resp_data, size_t resp_data_len) {
|
||||
(void)success; (void)rtt; (void)arg; (void)nonce; (void)resp_data; (void)resp_data_len;
|
||||
}
|
||||
|
||||
static void cm_bg_ping_timer_cb(void* arg) {
|
||||
struct CONN_MGR* mgr = (struct CONN_MGR*)arg;
|
||||
struct ROUTE_BGP* bgp = mgr->instance->bgp;
|
||||
if (!bgp || !bgp->nodes) {
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(mgr->instance->topo_groups);
|
||||
if (!group || !group->nodes) {
|
||||
mgr->bg_ping_timer = uasync_set_timeout(mgr->instance->ua, CONN_MGR_BG_PING_INTERVAL_TB, mgr, cm_bg_ping_timer_cb, "conn_mgr_bg_ping");
|
||||
return;
|
||||
}
|
||||
uint64_t now_tb = get_time_tb();
|
||||
struct ll_entry* e = bgp->nodes->head;
|
||||
struct ll_entry* e = group->nodes->head;
|
||||
size_t total = 0;
|
||||
while (e) { total++; e = e->next; }
|
||||
if (total == 0) {
|
||||
@@ -946,21 +946,31 @@ static void cm_bg_ping_timer_cb(void* arg) {
|
||||
mgr->bg_ping_cycle_start_tb = now_tb;
|
||||
}
|
||||
size_t cursor = 0;
|
||||
e = bgp->nodes->head;
|
||||
e = group->nodes->head;
|
||||
while (e && cursor < mgr->bg_ping_cursor) { cursor++; e = e->next; }
|
||||
if (e) {
|
||||
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e;
|
||||
uint64_t node_id = nq->node.node_id;
|
||||
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e;
|
||||
uint64_t node_id = nq->node->node_id;
|
||||
if (node_id != mgr->instance->node_id) {
|
||||
struct ETCP_CONN* dconn = route_bgp_find_conn_for_node(bgp, node_id);
|
||||
struct ETCP_CONN* dconn = topo_group_find_conn_for_node(group, node_id);
|
||||
int has_active = 0;
|
||||
if (dconn && dconn->links) {
|
||||
struct ETCP_LINK* l = dconn->links;
|
||||
while (l) { if (l->initialized && l->link_status) { has_active = 1; break; } l = l->next; }
|
||||
}
|
||||
if (!has_active) {
|
||||
etcp_send_ping(mgr->instance, nq->node.public_key, NULL, CONN_PROBE_TIMEOUT_MS,
|
||||
NULL, NULL, NULL, 0);
|
||||
for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) {
|
||||
if (a->type != TOPO_ADDR_REAL && a->type != TOPO_ADDR_NAT) continue;
|
||||
struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET;
|
||||
memcpy(&sin.sin_addr.s_addr, a->addr, 4); sin.sin_port = htons(a->port);
|
||||
struct sockaddr_storage sa; memcpy(&sa, &sin, sizeof(sin));
|
||||
struct ETCP_SOCKET* s = mgr->instance->etcp_sockets;
|
||||
while (s) { if (s->local_addr.ss_family == AF_INET) break; s = s->next; }
|
||||
if (!s) break;
|
||||
etcp_send_ping_to_socket(mgr->instance, s, nq->node->public_key, &sa,
|
||||
CONN_PROBE_TIMEOUT_MS, cm_bg_ping_noop_cb, NULL, NULL, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
mgr->bg_ping_cursor++;
|
||||
@@ -973,7 +983,7 @@ static void cm_candidate_ping_timer_cb(void* arg) {
|
||||
uint64_t now_tb = get_time_tb();
|
||||
for (uint8_t i = 0; i < mgr->best_candidate_count; i++) {
|
||||
uint64_t cand_id = mgr->best_candidates[i].node_id;
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(mgr->instance->bgp, cand_id);
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(topo_groups_get_default(mgr->instance->topo_groups), cand_id);
|
||||
if (!nq) {
|
||||
memmove(&mgr->best_candidates[i], &mgr->best_candidates[i+1], (mgr->best_candidate_count - i - 1) * sizeof(mgr->best_candidates[0]));
|
||||
mgr->best_candidate_count--; i--; continue;
|
||||
@@ -984,14 +994,14 @@ static void cm_candidate_ping_timer_cb(void* arg) {
|
||||
memmove(&mgr->best_candidates[i], &mgr->best_candidates[i+1], (mgr->best_candidate_count - i - 1) * sizeof(mgr->best_candidates[0]));
|
||||
mgr->best_candidate_count--; i--; continue;
|
||||
}
|
||||
struct ETCP_CONN* dconn = route_bgp_find_conn_for_node(mgr->instance->bgp, cand_id);
|
||||
struct ETCP_CONN* dconn = topo_group_find_conn_for_node(topo_groups_get_default(mgr->instance->topo_groups), cand_id);
|
||||
int has_active = 0;
|
||||
if (dconn && dconn->links) {
|
||||
struct ETCP_LINK* l = dconn->links;
|
||||
while (l) { if (l->initialized && l->link_status) { has_active = 1; break; } l = l->next; }
|
||||
}
|
||||
if (has_active)
|
||||
etcp_send_ping(mgr->instance, nq->node.public_key, &dconn->links->remote_addr, CONN_PROBE_TIMEOUT_MS, NULL, NULL, NULL, 0);
|
||||
etcp_send_ping(mgr->instance, nq->node->public_key, &dconn->links->remote_addr, CONN_PROBE_TIMEOUT_MS, NULL, NULL, NULL, 0);
|
||||
else
|
||||
route_connectivity_probe_node(mgr->instance, nq);
|
||||
}
|
||||
|
||||
+375
-117
@@ -1,190 +1,448 @@
|
||||
#ifndef CONN_MGR_H
|
||||
#define CONN_MGR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "etcp_api.h"
|
||||
#include "etcp_router.h"
|
||||
#include "route_node.h"
|
||||
#include "topo_node.h"
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
struct ETCP_CONN;
|
||||
struct NODEINFO_Q;
|
||||
struct ROUTE_BGP;
|
||||
struct TOPO_NODEQ;
|
||||
struct TOPO_GROUP;
|
||||
|
||||
#define CONN_MGR_MAX_CANDIDATES 3
|
||||
#define CONN_MGR_CANDIDATE_CACHE_MS 20000
|
||||
#define CONN_MGR_CANDIDATE_STALE_TB 300000
|
||||
#define CONN_MGR_CANDIDATE_PING_TB 20000
|
||||
#define CONN_MGR_BG_PING_INTERVAL_TB 1000
|
||||
#define CONN_MGR_BG_PING_CYCLE_MIN_TB 100000
|
||||
#define CONN_MGR_IDLE_CHECK_INTERVAL_TB 10000
|
||||
#define CONN_MGR_LOCAL_SCAN_ATTEMPTS 3
|
||||
#define CONN_MGR_LOCAL_SCAN_TIMEOUT_MS 100
|
||||
#define CONN_MGR_CONNECT_DIRECT_TIMEOUT_MS 5000
|
||||
#define CONN_MGR_CONNECT_REVERSE_TIMEOUT_MS 15000
|
||||
#define CONN_MGR_INTERM_EXCHANGE_TIMEOUT_MS 15000
|
||||
/**
|
||||
* @file conn_mgr.h
|
||||
* @brief Менеджер установки соединений. Трёхфазная машина подключения к нодам
|
||||
* с выбором оптимального пути: прямой → обратный → через посредников.
|
||||
*
|
||||
* Основные возможности:
|
||||
* - Автоматический выбор лучшего способа подключения к целевой ноде
|
||||
* - Фаза 1 (DIRECT): попытка прямого INIT-рукопожатия на все известные IPv4-адреса цели.
|
||||
* Итерация по парам (наш сокет, целевой адрес) с проверкой NAT-совместимости.
|
||||
* Таймаут: CONN_MGR_CONNECT_DIRECT_TIMEOUT_MS
|
||||
* - Фаза 2 (REVERSE): если у нас есть прямой IP, а у цели нет — отправляем DIRECT_REQ
|
||||
* с нашими адресами через BGP-маршрут. Цель сама инициирует INIT в нашу сторону.
|
||||
* Таймаут: CONN_MGR_CONNECT_REVERSE_TIMEOUT_MS
|
||||
* - Фаза 3 (INDIRECT): если DIRECT и REVERSE не сработали — обмениваемся через BGP
|
||||
* списками лучших промежуточных узлов-кандидатов с RTT, выбираем общих с минимальным
|
||||
* суммарным RTT. Трафик идёт через etcp_router (транзитная ретрансляция).
|
||||
* Таймаут: CONN_MGR_INTERM_EXCHANGE_TIMEOUT_MS
|
||||
* - Фоновое сканирование локальной сети (broadcast/multicast UDP-ping)
|
||||
* - Фоновое поддержание списка лучших кандидатов-посредников (периодический ping,
|
||||
* удаление stale-кандидатов)
|
||||
* - Фоновый обход всех известных BGP-нод для поддержания RTT-замеров
|
||||
* - Управление idle-таймаутом с автоматическим разрывом неактивных соединений
|
||||
*
|
||||
* @note Правила использования:
|
||||
* 1. conn_mgr_init() вызывается один раз при старте экземпляра utun
|
||||
* 2. conn_mgr_connect_node() — асинхронный вызов, результат приходит в callback
|
||||
* 3. Повторный вызов conn_mgr_connect_node для уже подключённой ноды вернёт
|
||||
* CONN_MGR_ERR_ALREADY_CONNECTED; для ноды в состоянии CONNECTING добавит
|
||||
* дополнительный callback в список
|
||||
* 4. conn_mgr_disconnect_node() отправляет DISCONNECT-пакет целевой ноде и
|
||||
* сбрасывает conn_mgr_type в NODEINFO_Q
|
||||
* 5. Для alien-нод (подключившихся к нам самостоятельно) используется
|
||||
* conn_mgr_add_alien_node() вместо conn_mgr_connect_node()
|
||||
* 6. conn_mgr_update_best_candidates() вызывается извне (обычно из route_connectivity)
|
||||
* для обновления RTT кандидатов
|
||||
*
|
||||
* @note Внутренние таймеры (всё в timebase-единицах, 1 tb = 0.1 мс):
|
||||
* - bg_ping_timer: фоновый ping нод (CONN_MGR_BG_PING_INTERVAL_TB)
|
||||
* - candidate_ping_timer: обновление RTT кандидатов (CONN_MGR_CANDIDATE_PING_TB)
|
||||
* - idle_timer: проверка неактивности подключённых нод (CONN_MGR_IDLE_CHECK_INTERVAL_TB)
|
||||
*/
|
||||
|
||||
#define CONN_MGR_OK 0
|
||||
#define CONN_MGR_ERR_NOT_FOUND -1
|
||||
#define CONN_MGR_ERR_NO_ADDRESSES -2
|
||||
#define CONN_MGR_ERR_TIMEOUT -3
|
||||
#define CONN_MGR_ERR_UNREACHABLE -4
|
||||
#define CONN_MGR_ERR_REFUSED -5
|
||||
#define CONN_MGR_ERR_ALREADY_CONNECTED -6
|
||||
#define CONN_MGR_ERR_INTERNAL -7
|
||||
/* ==================== Константы и лимиты ==================== */
|
||||
|
||||
#define CONN_MGR_MAX_CANDIDATES 3 ///< Макс. число лучших кандидатов-посредников
|
||||
#define CONN_MGR_CANDIDATE_CACHE_MS 20000 ///< Время жизни кандидата в кеше (мс) — не используется напрямую в .h
|
||||
#define CONN_MGR_CANDIDATE_STALE_TB 300000 ///< Порог устаревания кандидата (tb), после которого удаляется из списка
|
||||
#define CONN_MGR_CANDIDATE_PING_TB 20000 ///< Интервал фонового ping кандидатов (tb, ~2 сек)
|
||||
#define CONN_MGR_BG_PING_INTERVAL_TB 1000 ///< Интервал фонового ping BGP-нод (tb, ~100 мс)
|
||||
#define CONN_MGR_BG_PING_CYCLE_MIN_TB 100000 ///< Минимальная длительность полного цикла обхода нод (tb, ~10 сек)
|
||||
#define CONN_MGR_IDLE_CHECK_INTERVAL_TB 10000 ///< Интервал проверки idle-таймаута (tb, ~1 сек)
|
||||
#define CONN_MGR_LOCAL_SCAN_ATTEMPTS 3 ///< Число попыток локального сканирования
|
||||
#define CONN_MGR_LOCAL_SCAN_TIMEOUT_MS 100 ///< Таймаут локального сканирования (мс)
|
||||
#define CONN_MGR_CONNECT_DIRECT_TIMEOUT_MS 5000 ///< Таймаут фазы DIRECT (мс)
|
||||
#define CONN_MGR_CONNECT_REVERSE_TIMEOUT_MS 15000 ///< Таймаут фазы REVERSE (мс)
|
||||
#define CONN_MGR_INTERM_EXCHANGE_TIMEOUT_MS 15000 ///< Таймаут фазы INDIRECT (мс)
|
||||
|
||||
/* ==================== Коды возврата ==================== */
|
||||
|
||||
#define CONN_MGR_OK 0 ///< Успех
|
||||
#define CONN_MGR_ERR_NOT_FOUND -1 ///< Нода не найдена в BGP
|
||||
#define CONN_MGR_ERR_NO_ADDRESSES -2 ///< Нет известных адресов для подключения
|
||||
#define CONN_MGR_ERR_TIMEOUT -3 ///< Таймаут соединения
|
||||
#define CONN_MGR_ERR_UNREACHABLE -4 ///< Нода недостижима (все фазы провалились)
|
||||
#define CONN_MGR_ERR_REFUSED -5 ///< Соединение отклонено (зарезервировано)
|
||||
#define CONN_MGR_ERR_ALREADY_CONNECTED -6 ///< Уже подключены
|
||||
#define CONN_MGR_ERR_INTERNAL -7 ///< Внутренняя ошибка
|
||||
|
||||
/* ==================== Состояния ==================== */
|
||||
|
||||
/**
|
||||
* @enum CONN_MGR_STATE
|
||||
* @brief Состояние подключения к ноде.
|
||||
*/
|
||||
enum CONN_MGR_STATE {
|
||||
CONN_MGR_STATE_DISCONNECTED = 0,
|
||||
CONN_MGR_STATE_CONNECTING = 1,
|
||||
CONN_MGR_STATE_CONNECTED = 2,
|
||||
CONN_MGR_STATE_DISCONNECTED = 0, ///< Не подключены
|
||||
CONN_MGR_STATE_CONNECTING = 1, ///< В процессе установки соединения
|
||||
CONN_MGR_STATE_CONNECTED = 2, ///< Подключены
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum CM_TRY_STATE
|
||||
* @brief Состояние отдельной попытки (локальное сканирование / основное подключение).
|
||||
*/
|
||||
enum CM_TRY_STATE {
|
||||
CM_TRY_NONE = 0,
|
||||
CM_TRY_PENDING = 1,
|
||||
CM_TRY_OK = 2,
|
||||
CM_TRY_FAILED = 3,
|
||||
CM_TRY_NONE = 0, ///< Попытка не начиналась
|
||||
CM_TRY_PENDING = 1, ///< Попытка в процессе
|
||||
CM_TRY_OK = 2, ///< Попытка успешна
|
||||
CM_TRY_FAILED = 3, ///< Попытка провалилась
|
||||
};
|
||||
|
||||
#define CONN_MGR_SUBCMD_DIRECT_REQ 0x01
|
||||
#define CONN_MGR_SUBCMD_DIRECT_RESP 0x02
|
||||
#define CONN_MGR_SUBCMD_INTERM_EXCHANGE_REQ 0x03
|
||||
#define CONN_MGR_SUBCMD_INTERM_EXCHANGE_RESP 0x04
|
||||
#define CONN_MGR_SUBCMD_INTERM_SELECTED 0x05
|
||||
#define CONN_MGR_SUBCMD_DISCONNECT 0x06
|
||||
/* ==================== Протокол CONN_MGR (ETCP_RT_ID_CONN_MGR = 0x11) ==================== */
|
||||
|
||||
/**
|
||||
* @defgroup conn_mgr_subcmd Подкоманды протокола CONN_MGR
|
||||
* @{
|
||||
*/
|
||||
#define CONN_MGR_SUBCMD_DIRECT_REQ 0x01 ///< "Я за NAT, подключись ко мне" — передача наших адресов цели
|
||||
#define CONN_MGR_SUBCMD_DIRECT_RESP 0x02 ///< Ответ на DIRECT_REQ (зарезервирован, не реализован)
|
||||
#define CONN_MGR_SUBCMD_INTERM_EXCHANGE_REQ 0x03 ///< Запрос обмена кандидатами-посредниками
|
||||
#define CONN_MGR_SUBCMD_INTERM_EXCHANGE_RESP 0x04 ///< Ответ с нашими кандидатами + замеры RTT до кандидатов инициатора
|
||||
#define CONN_MGR_SUBCMD_INTERM_SELECTED 0x05 ///< Выбранные посредники (финальный шаг INDIRECT-фазы)
|
||||
#define CONN_MGR_SUBCMD_DISCONNECT 0x06 ///< Уведомление о разрыве соединения
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @struct CONN_MGR_CANDIDATE
|
||||
* @brief Запись о кандидате-посреднике: идентификатор ноды + RTT до неё.
|
||||
*
|
||||
* Используется в списке best_candidates и во всех пакетах обмена кандидатами.
|
||||
*/
|
||||
struct CONN_MGR_CANDIDATE {
|
||||
uint64_t node_id;
|
||||
uint16_t rtt;
|
||||
uint64_t node_id; ///< Идентификатор ноды-кандидата
|
||||
uint16_t rtt; ///< RTT до кандидата (мс)
|
||||
} __attribute__((packed));
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
/**
|
||||
* @struct CONN_MGR_DIRECT_REQ
|
||||
* @brief Пакет фазы REVERSE: "подключись ко мне".
|
||||
*
|
||||
* Отправляется когда мы имеем прямой IP, но цель за NAT.
|
||||
* Содержит наши адреса, по которым цель должна инициировать INIT.
|
||||
*/
|
||||
struct CONN_MGR_DIRECT_REQ {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd;
|
||||
uint32_t request_id;
|
||||
uint8_t addr_count;
|
||||
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
|
||||
uint8_t subcmd; ///< CONN_MGR_SUBCMD_DIRECT_REQ
|
||||
uint32_t request_id; ///< Идентификатор запроса для сопоставления ответа
|
||||
uint8_t addr_count; ///< Количество адресов, следующих за заголовком
|
||||
/* далее: addr_count записей по 8 байт каждая (type:1, ip:4, port:2, sock_id:1) */
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct CONN_MGR_DIRECT_RESP
|
||||
* @brief Ответ на DIRECT_REQ (зарезервирован, не отправляется).
|
||||
*/
|
||||
struct CONN_MGR_DIRECT_RESP {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd;
|
||||
uint32_t request_id;
|
||||
uint8_t accepted;
|
||||
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
|
||||
uint8_t subcmd; ///< CONN_MGR_SUBCMD_DIRECT_RESP
|
||||
uint32_t request_id; ///< Идентификатор запроса
|
||||
uint8_t accepted; ///< 1 — принято, 0 — отклонено
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct CONN_MGR_INTERM_EXCHANGE_REQ
|
||||
* @brief Запрос обмена списками кандидатов-посредников (фаза INDIRECT, инициатор).
|
||||
*
|
||||
* Инициатор отправляет свой топ-4 кандидатов. Цель должна ответить
|
||||
* CONN_MGR_INTERM_EXCHANGE_RESP со своими кандидатами и замерами RTT до наших.
|
||||
*/
|
||||
struct CONN_MGR_INTERM_EXCHANGE_REQ {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd;
|
||||
uint32_t request_id;
|
||||
uint8_t candidate_count;
|
||||
struct CONN_MGR_CANDIDATE candidates[4];
|
||||
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
|
||||
uint8_t subcmd; ///< CONN_MGR_SUBCMD_INTERM_EXCHANGE_REQ
|
||||
uint32_t request_id; ///< Идентификатор запроса
|
||||
uint8_t candidate_count; ///< Количество кандидатов в массиве (≤4)
|
||||
struct CONN_MGR_CANDIDATE candidates[4]; ///< Наши лучшие кандидаты с RTT
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct CONN_MGR_INTERM_EXCHANGE_RESP
|
||||
* @brief Ответ на INTERM_EXCHANGE_REQ (фаза INDIRECT, цель).
|
||||
*
|
||||
* Содержит два списка:
|
||||
* - my_candidates: лучшие кандидаты цели с RTT до них (измеренным целью)
|
||||
* - your_candidates: кандидаты инициатора, но с RTT, измеренным со стороны цели
|
||||
*/
|
||||
struct CONN_MGR_INTERM_EXCHANGE_RESP {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd;
|
||||
uint32_t request_id;
|
||||
uint8_t my_count;
|
||||
uint8_t your_count;
|
||||
struct CONN_MGR_CANDIDATE my_candidates[4];
|
||||
struct CONN_MGR_CANDIDATE your_candidates[4];
|
||||
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
|
||||
uint8_t subcmd; ///< CONN_MGR_SUBCMD_INTERM_EXCHANGE_RESP
|
||||
uint32_t request_id; ///< Идентификатор запроса
|
||||
uint8_t my_count; ///< Количество наших кандидатов (≤4)
|
||||
uint8_t your_count; ///< Количество кандидатов инициатора (≤4)
|
||||
struct CONN_MGR_CANDIDATE my_candidates[4]; ///< Наши кандидаты + наш RTT до них
|
||||
struct CONN_MGR_CANDIDATE your_candidates[4]; ///< Кандидаты инициатора + наш RTT до них
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct CONN_MGR_INTERM_SELECTED
|
||||
* @brief Финальный выбор посредников (фаза INDIRECT).
|
||||
*
|
||||
* Инициатор вычисляет лучших общих посредников по сумме RTT
|
||||
* и отправляет выбранный список цели.
|
||||
*/
|
||||
struct CONN_MGR_INTERM_SELECTED {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd;
|
||||
uint32_t request_id;
|
||||
uint8_t count;
|
||||
struct CONN_MGR_CANDIDATE selected[3];
|
||||
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
|
||||
uint8_t subcmd; ///< CONN_MGR_SUBCMD_INTERM_SELECTED
|
||||
uint32_t request_id; ///< Идентификатор запроса (всегда 0 для извещения)
|
||||
uint8_t count; ///< Количество выбранных посредников (≤3)
|
||||
struct CONN_MGR_CANDIDATE selected[3]; ///< Выбранные посредники (node_id + RTT от нас до них)
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct CONN_MGR_DISCONNECT
|
||||
* @brief Уведомление о разрыве соединения.
|
||||
*/
|
||||
struct CONN_MGR_DISCONNECT {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd;
|
||||
uint64_t node_id;
|
||||
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
|
||||
uint8_t subcmd; ///< CONN_MGR_SUBCMD_DISCONNECT
|
||||
uint64_t node_id; ///< Идентификатор ноды, с которой разрываем соединение
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
#define CONN_MGR_DIRECT_REQ_HDR_SIZE sizeof(struct CONN_MGR_DIRECT_REQ)
|
||||
#define CONN_MGR_DIRECT_RESP_HDR_SIZE sizeof(struct CONN_MGR_DIRECT_RESP)
|
||||
#define CONN_MGR_INTERM_EXCHANGE_REQ_SIZE sizeof(struct CONN_MGR_INTERM_EXCHANGE_REQ)
|
||||
#define CONN_MGR_INTERM_EXCHANGE_RESP_SIZE sizeof(struct CONN_MGR_INTERM_EXCHANGE_RESP)
|
||||
#define CONN_MGR_INTERM_SELECTED_SIZE sizeof(struct CONN_MGR_INTERM_SELECTED)
|
||||
#define CONN_MGR_DISCONNECT_SIZE sizeof(struct CONN_MGR_DISCONNECT)
|
||||
/* ==================== Размеры пакетов протокола ==================== */
|
||||
|
||||
#define CONN_MGR_DIRECT_REQ_HDR_SIZE sizeof(struct CONN_MGR_DIRECT_REQ) ///< Размер заголовка DIRECT_REQ (без addr_count адресов)
|
||||
#define CONN_MGR_DIRECT_RESP_HDR_SIZE sizeof(struct CONN_MGR_DIRECT_RESP) ///< Размер DIRECT_RESP
|
||||
#define CONN_MGR_INTERM_EXCHANGE_REQ_SIZE sizeof(struct CONN_MGR_INTERM_EXCHANGE_REQ) ///< Размер INTERM_EXCHANGE_REQ
|
||||
#define CONN_MGR_INTERM_EXCHANGE_RESP_SIZE sizeof(struct CONN_MGR_INTERM_EXCHANGE_RESP) ///< Размер INTERM_EXCHANGE_RESP (макс.)
|
||||
#define CONN_MGR_INTERM_SELECTED_SIZE sizeof(struct CONN_MGR_INTERM_SELECTED) ///< Размер INTERM_SELECTED
|
||||
#define CONN_MGR_DISCONNECT_SIZE sizeof(struct CONN_MGR_DISCONNECT) ///< Размер DISCONNECT
|
||||
|
||||
/* ==================== Callback-типы и внутренние структуры ==================== */
|
||||
|
||||
/**
|
||||
* @typedef conn_mgr_connect_callback_t
|
||||
* @brief Callback результата подключения.
|
||||
* @param result код возврата (CONN_MGR_OK или CONN_MGR_ERR_*)
|
||||
* @param node_id идентификатор ноды
|
||||
* @param arg пользовательский аргумент
|
||||
*/
|
||||
typedef void (*conn_mgr_connect_callback_t)(int result, uint64_t node_id, void* arg);
|
||||
|
||||
/**
|
||||
* @struct cm_cb_node
|
||||
* @brief Узел связного списка callback'ов для одной ноды.
|
||||
*
|
||||
* Позволяет регистрировать несколько callback'ов на одно подключение.
|
||||
* Новые callback'и добавляются в начало списка.
|
||||
*/
|
||||
struct cm_cb_node {
|
||||
conn_mgr_connect_callback_t cb;
|
||||
void* arg;
|
||||
struct cm_cb_node* next;
|
||||
conn_mgr_connect_callback_t cb; ///< Функция-callback
|
||||
void* arg; ///< Пользовательский аргумент
|
||||
struct cm_cb_node* next; ///< Следующий callback в списке
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct CONN_MGR_ENTRY
|
||||
* @brief Запись о подключении к одной ноде.
|
||||
*
|
||||
* Хранит состояние трёхфазной машины, список выбранных посредников,
|
||||
* idle-таймер и список ожидающих callback'ов.
|
||||
*/
|
||||
struct CONN_MGR_ENTRY {
|
||||
uint64_t node_id;
|
||||
uint8_t state;
|
||||
uint8_t conn_type;
|
||||
uint8_t alien;
|
||||
uint32_t idle_timeout_ms;
|
||||
uint64_t last_traffic_tb;
|
||||
void* idle_timer;
|
||||
uint64_t node_id; ///< Идентификатор целевой ноды
|
||||
uint8_t state; ///< Текущее состояние (enum CONN_MGR_STATE)
|
||||
uint8_t conn_type; ///< Тип соединения (CONN_TYPE_DIRECT/REVERSE/INDIRECT)
|
||||
uint8_t alien; ///< 1 — alien-нода (сама подключилась к нам), 0 — мы инициаторы
|
||||
uint32_t idle_timeout_ms; ///< Таймаут неактивности (мс), 0 — без таймаута
|
||||
uint64_t last_traffic_tb; ///< Время последнего трафика (tb) для idle-проверки
|
||||
void* idle_timer; ///< Хендл таймера idle-проверки
|
||||
|
||||
struct cm_cb_node* cb_list;
|
||||
struct cm_cb_node* cb_list; ///< Связный список callback'ов результата
|
||||
|
||||
uint64_t intermediaries[CONN_MGR_MAX_INTERMEDIARIES];
|
||||
uint8_t intermediariy_count;
|
||||
uint8_t rr_idx;
|
||||
uint64_t intermediaries[CONN_MGR_MAX_INTERMEDIARIES]; ///< Выбранные посредники (node_id)
|
||||
uint8_t intermediariy_count; ///< Количество выбранных посредников
|
||||
uint8_t rr_idx; ///< Round-robin индекс для выбора посредника при отправке
|
||||
|
||||
uint8_t local_scan_state;
|
||||
uint8_t main_connect_state;
|
||||
uint8_t local_scan_state; ///< Состояние локального сканирования (enum CM_TRY_STATE)
|
||||
uint8_t main_connect_state; ///< Состояние основного подключения (enum CM_TRY_STATE)
|
||||
struct {
|
||||
uint8_t phase;
|
||||
void* timer;
|
||||
uint32_t request_id;
|
||||
void* conn_ctx;
|
||||
uint8_t phase; ///< Текущая фаза: 1=DIRECT, 2=REVERSE, 3=INDIRECT
|
||||
void* timer; ///< Хендл таймера текущей фазы
|
||||
uint32_t request_id; ///< request_id для INDIRECT-обмена
|
||||
void* conn_ctx; ///< Контекст ETCP-соединения (для REVERSE)
|
||||
} main;
|
||||
|
||||
struct CONN_MGR* mgr;
|
||||
struct CONN_MGR* mgr; ///< Обратная ссылка на менеджер
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct CONN_MGR
|
||||
* @brief Корневая структура менеджера соединений.
|
||||
*
|
||||
* Создаётся один раз в conn_mgr_init(), хранится в UTUN_INSTANCE.
|
||||
* Содержит динамический массив entries, список лучших кандидатов,
|
||||
* таймеры фонового ping и список ожидающих обменов (reverse/exchange pending).
|
||||
*/
|
||||
struct CONN_MGR {
|
||||
struct UTUN_INSTANCE* instance;
|
||||
struct CONN_MGR_ENTRY* entries;
|
||||
size_t entry_count;
|
||||
size_t entry_capacity;
|
||||
struct UTUN_INSTANCE* instance; ///< Родительский экземпляр utun
|
||||
|
||||
void* bg_ping_timer;
|
||||
size_t bg_ping_cursor;
|
||||
uint64_t bg_ping_cycle_start_tb;
|
||||
struct CONN_MGR_ENTRY* entries; ///< Динамический массив записей о подключениях
|
||||
size_t entry_count; ///< Текущее количество записей
|
||||
size_t entry_capacity; ///< Выделенная ёмкость массива entries
|
||||
|
||||
void* candidate_ping_timer;
|
||||
void* bg_ping_timer; ///< Хендл таймера фонового ping BGP-нод
|
||||
size_t bg_ping_cursor; ///< Курсор текущей ноды в цикле обхода
|
||||
uint64_t bg_ping_cycle_start_tb; ///< Время старта текущего цикла обхода (tb)
|
||||
|
||||
struct CONN_MGR_CANDIDATE best_candidates[CONN_MGR_MAX_CANDIDATES];
|
||||
uint8_t best_candidate_count;
|
||||
void* candidate_ping_timer; ///< Хендл таймера обновления RTT кандидатов
|
||||
|
||||
uint32_t next_request_id;
|
||||
uint8_t initialized;
|
||||
struct CONN_MGR_CANDIDATE best_candidates[CONN_MGR_MAX_CANDIDATES]; ///< Топ кандидатов-посредников, отсортированных по RTT
|
||||
uint8_t best_candidate_count; ///< Количество актуальных кандидатов (≤CONN_MGR_MAX_CANDIDATES)
|
||||
|
||||
struct cm_reverse_pending* reverse_pending;
|
||||
struct cm_exchange_pending* exchange_pending;
|
||||
uint32_t next_request_id; ///< Счётчик request_id для INTERM_EXCHANGE
|
||||
uint8_t initialized; ///< Флаг инициализации
|
||||
|
||||
struct cm_reverse_pending* reverse_pending; ///< Связный список ожидающих REVERSE-подключений
|
||||
struct cm_exchange_pending* exchange_pending; ///< Связный список ожидающих INDIRECT-обменов
|
||||
};
|
||||
|
||||
/* ==================== Жизненный цикл ==================== */
|
||||
|
||||
/**
|
||||
* @brief Инициализирует менеджер соединений.
|
||||
* @param instance родительский экземпляр utun
|
||||
* @return указатель на CONN_MGR или NULL при ошибке
|
||||
*
|
||||
* Регистрирует обработчик в etcp_router для ETCP_RT_ID_CONN_MGR,
|
||||
* запускает фоновые таймеры (bg_ping, candidate_ping).
|
||||
*/
|
||||
struct CONN_MGR* conn_mgr_init(struct UTUN_INSTANCE* instance);
|
||||
void conn_mgr_destroy(struct CONN_MGR* mgr);
|
||||
|
||||
int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id,
|
||||
uint32_t idle_timeout_ms,
|
||||
conn_mgr_connect_callback_t cb, void* cb_arg);
|
||||
int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id);
|
||||
int conn_mgr_set_idle_timeout(struct CONN_MGR* mgr, uint64_t node_id, uint32_t timeout_ms);
|
||||
int conn_mgr_get_status(struct CONN_MGR* mgr, uint64_t node_id,
|
||||
uint8_t* out_state, uint8_t* out_conn_type);
|
||||
int conn_mgr_add_alien_node(struct CONN_MGR* mgr, const uint8_t* nodeinfo_data, size_t len);
|
||||
int conn_mgr_send(struct CONN_MGR* mgr, uint64_t node_id, struct ll_entry* entry);
|
||||
/**
|
||||
* @brief Уничтожает менеджер соединений.
|
||||
* @param mgr менеджер
|
||||
*
|
||||
* Освобождает все записи, таймеры, pending-структуры.
|
||||
* Вызывает cm_clear_nodeinfo для каждой записи.
|
||||
*/
|
||||
void conn_mgr_destroy(struct CONN_MGR* mgr);
|
||||
|
||||
/* ==================== Подключение / отключение ==================== */
|
||||
|
||||
/**
|
||||
* @brief Запускает асинхронное подключение к ноде.
|
||||
* @param mgr менеджер
|
||||
* @param node_id идентификатор целевой ноды
|
||||
* @param idle_timeout_ms таймаут неактивности (мс), 0 = без таймаута
|
||||
* @param cb callback результата (CONN_MGR_OK, CONN_MGR_ERR_TIMEOUT, CONN_MGR_ERR_UNREACHABLE и т.д.)
|
||||
* @param cb_arg пользовательский аргумент для callback
|
||||
* @return CONN_MGR_OK если процесс запущен, CONN_MGR_ERR_* при ошибке
|
||||
*
|
||||
* @note Если нода уже подключена — сразу вызывает callback с CONN_MGR_OK и возвращает CONN_MGR_OK.
|
||||
* @note Если нода в процессе подключения — добавляет callback в список ожидания.
|
||||
* @note Автоматически определяет наличие BGP-соединения (уже существующий ETCP-линк).
|
||||
* @note Для alien-нод используйте conn_mgr_add_alien_node().
|
||||
*/
|
||||
int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id,
|
||||
uint32_t idle_timeout_ms,
|
||||
conn_mgr_connect_callback_t cb, void* cb_arg);
|
||||
|
||||
/**
|
||||
* @brief Разрывает подключение к ноде.
|
||||
* @param mgr менеджер
|
||||
* @param node_id идентификатор ноды
|
||||
* @return CONN_MGR_OK или CONN_MGR_ERR_NOT_FOUND
|
||||
*
|
||||
* Отправляет DISCONNECT-пакет целевой ноде, сбрасывает conn_mgr_type в NODEINFO_Q,
|
||||
* уничтожает запись entry.
|
||||
*/
|
||||
int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id);
|
||||
|
||||
/* ==================== Управление idle-таймаутом ==================== */
|
||||
|
||||
/**
|
||||
* @brief Устанавливает idle-таймаут для подключённой ноды.
|
||||
* @param mgr менеджер
|
||||
* @param node_id идентификатор ноды
|
||||
* @param timeout_ms таймаут неактивности (мс), 0 = отключить проверку
|
||||
* @return CONN_MGR_OK или CONN_MGR_ERR_NOT_FOUND / CONN_MGR_ERR_INTERNAL
|
||||
*
|
||||
* Если таймаут > 0 и нода в состоянии CONNECTED — запускает периодический таймер.
|
||||
* При отсутствии трафика в течение timeout_ms соединение автоматически разрывается.
|
||||
*/
|
||||
int conn_mgr_set_idle_timeout(struct CONN_MGR* mgr, uint64_t node_id, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Получает текущий статус подключения к ноде.
|
||||
* @param mgr менеджер
|
||||
* @param node_id идентификатор ноды
|
||||
* @param out_state [out] состояние (enum CONN_MGR_STATE)
|
||||
* @param out_conn_type [out] тип соединения (CONN_TYPE_DIRECT/REVERSE/INDIRECT)
|
||||
* @return CONN_MGR_OK
|
||||
*/
|
||||
int conn_mgr_get_status(struct CONN_MGR* mgr, uint64_t node_id,
|
||||
uint8_t* out_state, uint8_t* out_conn_type);
|
||||
|
||||
/* ==================== Alien-ноды ==================== */
|
||||
|
||||
/**
|
||||
* @brief Регистрирует alien-ноду (подключившуюся к нам самостоятельно).
|
||||
* @param mgr менеджер
|
||||
* @param nodeinfo_data сырые данные NODEINFO (BGP-пакет)
|
||||
* @param len длина данных
|
||||
* @return CONN_MGR_OK или CONN_MGR_ERR_INTERNAL
|
||||
*
|
||||
* Alien-нода — это нода, которая сама инициировала INIT-подключение к нам.
|
||||
* Для неё не запускаются фазы подключения, сразу устанавливается CONN_MGR_STATE_CONNECTED.
|
||||
* Создаёт entry с флагом alien=1.
|
||||
*/
|
||||
int conn_mgr_add_alien_node(struct CONN_MGR* mgr, const uint8_t* nodeinfo_data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Отправляет данные подключённой ноде через etcp_router.
|
||||
* @param mgr менеджер
|
||||
* @param node_id идентификатор целевой ноды
|
||||
* @param entry ll_entry с данными для отправки (освобождается после отправки)
|
||||
* @return CONN_MGR_OK или CONN_MGR_ERR_NOT_FOUND
|
||||
*
|
||||
* Для INDIRECT-соединений пакет автоматически маршрутизируется через
|
||||
* выбранного посредника (round-robin по intermediaries[]).
|
||||
* Обновляет last_traffic_tb для idle-проверки.
|
||||
*/
|
||||
int conn_mgr_send(struct CONN_MGR* mgr, uint64_t node_id, struct ll_entry* entry);
|
||||
|
||||
/* ==================== Управление кандидатами ==================== */
|
||||
|
||||
/**
|
||||
* @brief Обновляет RTT кандидата в списке лучших посредников.
|
||||
* @param mgr менеджер
|
||||
* @param node_id идентификатор ноды-кандидата
|
||||
* @param rtt RTT до кандидата (мс)
|
||||
*
|
||||
* Если кандидат уже в списке — обновляет RTT. Если нет — вставляет с сортировкой
|
||||
* по возрастанию RTT. Если список заполнен и новый кандидат имеет худший RTT —
|
||||
* игнорируется. Список всегда отсортирован: best_candidates[0] — наименьший RTT.
|
||||
*
|
||||
* @note Обычно вызывается из route_connectivity после успешного probe.
|
||||
*/
|
||||
void conn_mgr_update_best_candidates(struct CONN_MGR* mgr, uint64_t node_id, uint16_t rtt);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+114
-25
@@ -10,8 +10,9 @@
|
||||
#include "etcp_connections.h"
|
||||
#include "tun_if.h"
|
||||
#include "route_lib.h"
|
||||
#include "route_bgp.h"
|
||||
#include "route_node.h"
|
||||
#include "topo_group.h"
|
||||
#include "etcp_router.h"
|
||||
#include "topo_node.h"
|
||||
#include "pkt_normalizer.h"
|
||||
#include "../tools/etcpmon/etcpmon_protocol.h"
|
||||
#include "../lib/u_async.h"
|
||||
@@ -90,6 +91,7 @@ static void client_write_callback(socket_t fd, void* arg);
|
||||
static void client_except_callback(socket_t fd, void* arg);
|
||||
static void handle_client_data(struct control_server* server, struct control_client* client);
|
||||
static void close_client(struct control_server* server, struct control_client* client);
|
||||
static void control_client_idle_cb(void* arg);
|
||||
|
||||
static void send_conn_list(struct control_server* server, struct control_client* client, uint8_t seq_id);
|
||||
static void send_socket_list(struct control_server* server, struct control_client* client, uint8_t seq_id);
|
||||
@@ -98,7 +100,7 @@ static void send_debug_config(struct control_server* server, struct control_clie
|
||||
static void send_error(struct control_client* client, uint8_t error_code, const char* msg, uint8_t seq_id);
|
||||
|
||||
static void control_server_send_node_list(struct control_server* server, struct control_client* client, uint8_t seq_id);
|
||||
static void send_single_node_info(struct control_server* server, struct control_client* client, struct NODEINFO_Q* nq);
|
||||
static void send_single_node_info(struct control_server* server, struct control_client* client, struct TOPO_NODEQ* nq);
|
||||
static int control_client_send(struct control_server* server, struct control_client* client, uint8_t* dgram, uint16_t len);
|
||||
|
||||
static struct ETCP_CONN* find_connection_by_peer_id(struct UTUN_INSTANCE* instance, uint64_t peer_id);
|
||||
@@ -424,6 +426,7 @@ static void accept_callback(socket_t fd, void* arg) {
|
||||
}
|
||||
client->subscribed_nodes = 0;
|
||||
client->write_registered = 0;
|
||||
client->idle_timer = uasync_set_timeout(server->ua, 300000, client, control_client_idle_cb, "ctrl_idle");
|
||||
|
||||
/* Register with uasync */
|
||||
client->socket_id = uasync_add_socket_t(server->ua, client_fd,
|
||||
@@ -476,6 +479,12 @@ static void client_read_callback(socket_t fd, void* arg) {
|
||||
struct control_client* client = (struct control_client*)arg;
|
||||
struct control_server* server = client->server;
|
||||
|
||||
if (client->recv_len >= ETCPMON_MAX_MSG_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Control recv buffer overflow (pre-check)");
|
||||
close_client(server, client);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read available data */
|
||||
uint8_t* buf = client->recv_buffer + client->recv_len;
|
||||
size_t buf_space = ETCPMON_MAX_MSG_SIZE - client->recv_len;
|
||||
@@ -603,6 +612,7 @@ static void client_write_callback(socket_t fd, void* arg) {
|
||||
|
||||
uasync_set_socket_write(server->ua, client->socket_id, 0);
|
||||
client->write_registered = 0;
|
||||
if (q->count > 128) DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "ctrl_out: large output queue count=%d fd=%d", q->count, (int)client->fd);
|
||||
(void)fd;
|
||||
}
|
||||
|
||||
@@ -633,9 +643,19 @@ static int control_client_send(struct control_server* server, struct control_cli
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void control_client_idle_cb(void* arg) {
|
||||
struct control_client* client = (struct control_client*)arg;
|
||||
if (!client || !client->server) return;
|
||||
client->idle_timer = NULL;
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Control client idle timeout fd=%d, closing", client->fd);
|
||||
close_client(client->server, client);
|
||||
}
|
||||
|
||||
static void close_client(struct control_server* server, struct control_client* client) {
|
||||
if (!server || !client) return;
|
||||
|
||||
if (client->idle_timer) { uasync_cancel_timeout(server->ua, client->idle_timer); client->idle_timer = NULL; }
|
||||
|
||||
/* Log disconnection */
|
||||
if (server->log_file) {
|
||||
fprintf(server->log_file, "%llu: [LOG] Client connection closed\n",
|
||||
@@ -764,9 +784,30 @@ static void handle_client_data(struct control_server* server, struct control_cli
|
||||
struct etcpmon_cmd_action* cmd = (struct etcpmon_cmd_action*)payload;
|
||||
if (strncmp(cmd->action, "nat", 3) == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONTROL, "Action 'nat' received, requesting NAT check for all links");
|
||||
route_bgp_request_nat_check_all(server->instance->bgp);
|
||||
topo_group_request_nat_check_all(topo_groups_get_default(server->instance->topo_groups));
|
||||
} else if (strncmp(cmd->action, "nodes", 5) == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONTROL, "Action 'nodes' received, dumping all BGP nodes");
|
||||
uint16_t text_max = (ETCPMON_MAX_MSG_SIZE > 1024) ? ETCPMON_MAX_MSG_SIZE - 512 : 1024;
|
||||
char* text = (char*)u_malloc(text_max);
|
||||
if (text) {
|
||||
int text_len = topo_node_format_all(topo_groups_get_default(server->instance->topo_groups), text, text_max);
|
||||
if (text_len < 0) text_len = 0;
|
||||
text[text_len] = '\0';
|
||||
uint16_t rsp_size = ETCPMON_ACTION_RESULT_SIZE(text_len);
|
||||
uint8_t* buffer = (uint8_t*)u_malloc(rsp_size);
|
||||
if (buffer) {
|
||||
struct etcpmon_msg_header* rsp_hdr = (struct etcpmon_msg_header*)buffer;
|
||||
etcpmon_build_header(rsp_hdr, sizeof(struct etcpmon_rsp_action_result) + text_len + 1,
|
||||
ETCPMON_RSP_ACTION_RESULT, req_seq);
|
||||
struct etcpmon_rsp_action_result* rsp = (struct etcpmon_rsp_action_result*)(buffer + sizeof(*rsp_hdr));
|
||||
rsp->code = 0;
|
||||
memcpy(buffer + sizeof(*rsp_hdr) + sizeof(*rsp), text, text_len + 1);
|
||||
control_client_send(server, client, buffer, rsp_size);
|
||||
}
|
||||
u_free(text);
|
||||
}
|
||||
} else {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Unknown action received: '%s'", cmd->action);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONTROL, "Unknown action received: '%.32s'", cmd->action);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -821,6 +862,10 @@ static void handle_client_data(struct control_server* server, struct control_cli
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset idle timer on each valid command */
|
||||
if (client->idle_timer) { uasync_cancel_timeout(server->ua, client->idle_timer); }
|
||||
client->idle_timer = uasync_set_timeout(server->ua, 300000, client, control_client_idle_cb, "ctrl_idle");
|
||||
|
||||
/* Remove processed message from buffer */
|
||||
uint16_t msg_size = hdr->size;
|
||||
if (client->recv_len > msg_size) {
|
||||
@@ -946,6 +991,7 @@ static void send_socket_list(struct control_server* server, struct control_clien
|
||||
info[i].ip[sizeof(info[i].ip) - 1] = '\0';
|
||||
info[i].port = 0;
|
||||
}
|
||||
info[i].config_type = sock->type;
|
||||
info[i].nat_type = sock->nat_type;
|
||||
|
||||
/* NAT address from socket (set when INIT_RESPONSE received) */
|
||||
@@ -960,7 +1006,10 @@ static void send_socket_list(struct control_server* server, struct control_clien
|
||||
inet_ntop(AF_INET6, &sin6->sin6_addr, info[i].nat_ip, sizeof(info[i].nat_ip));
|
||||
info[i].nat_port = ntohs(sin6->sin6_port);
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "socket_list to gui: sock=%s config=%d nat_type=%d nat_ip=%s nat_port=%u",
|
||||
sock->name, sock->type, sock->nat_type,
|
||||
info[i].nat_ip[0] ? info[i].nat_ip : "none", info[i].nat_port);
|
||||
|
||||
sock = sock->next;
|
||||
}
|
||||
|
||||
@@ -1139,17 +1188,56 @@ static void send_metrics(struct control_server* server, struct control_client* c
|
||||
}
|
||||
|
||||
/* BGP routing */
|
||||
if (instance->bgp) {
|
||||
rsp->tun.rt_bgp_senders = (uint32_t)queue_entry_count(instance->bgp->senders_list);
|
||||
rsp->tun.rt_bgp_nodes = (uint32_t)queue_entry_count(instance->bgp->nodes);
|
||||
{ struct TOPO_GROUP* g = topo_groups_get_default(instance->topo_groups);
|
||||
if (g) {
|
||||
rsp->tun.rt_bgp_senders = (uint32_t)queue_entry_count(g->senders_list);
|
||||
rsp->tun.rt_bgp_nodes = (uint32_t)queue_entry_count(g->nodes);
|
||||
} else {
|
||||
rsp->tun.rt_bgp_senders = 0;
|
||||
rsp->tun.rt_bgp_nodes = 0;
|
||||
}
|
||||
} }
|
||||
} else {
|
||||
memset(&rsp->tun, 0, sizeof(rsp->tun));
|
||||
}
|
||||
|
||||
/* Fill router congestion metrics — aggregate over all router_conns */
|
||||
memset(&rsp->router, 0, sizeof(rsp->router));
|
||||
if (instance->router_conns) {
|
||||
uint32_t rtt_cnt = 0;
|
||||
struct ll_entry* re = instance->router_conns->head;
|
||||
while (re) {
|
||||
struct ETCP_ROUTER_CONN* rc = (struct ETCP_ROUTER_CONN*)re;
|
||||
rsp->router.total_inflight += (uint32_t)(rc->tx_seq - rc->tx_acked);
|
||||
if (rc->send_q) rsp->router.total_send_q += (uint32_t)queue_entry_count(rc->send_q);
|
||||
if (rc->recv_q) rsp->router.total_recv_q += (uint32_t)queue_entry_count(rc->recv_q);
|
||||
rsp->router.pkts_sent += rc->c_pkts_sent;
|
||||
rsp->router.pkts_send_err += rc->c_pkts_send_err;
|
||||
rsp->router.pkts_rcvd += rc->c_pkts_rcvd;
|
||||
rsp->router.ack_sent += rc->c_ack_sent;
|
||||
rsp->router.ack_recv += rc->c_ack_recv;
|
||||
rsp->router.dup_dropped += rc->c_dup_dropped;
|
||||
rsp->router.oob_dropped += rc->c_oob_dropped;
|
||||
rsp->router.stale_ack += rc->c_stale_ack;
|
||||
rsp->router.sign_fail += rc->c_sign_fail;
|
||||
if (rc->rtt > 0) {
|
||||
if (rc->rtt > rsp->router.rtt_last) rsp->router.rtt_last = rc->rtt;
|
||||
rsp->router.rtt_avg += rc->rtt;
|
||||
rtt_cnt++;
|
||||
}
|
||||
{
|
||||
uint16_t j = (uint16_t)(rc->rtt_jitter / 32768);
|
||||
if (j > rsp->router.jitter) rsp->router.jitter = j;
|
||||
}
|
||||
if (rc->minrtt_window_count > 0) {
|
||||
if (rsp->router.minrtt == 0 || rc->minrtt < rsp->router.minrtt)
|
||||
rsp->router.minrtt = rc->minrtt;
|
||||
}
|
||||
re = re->next;
|
||||
}
|
||||
if (rtt_cnt > 0) rsp->router.rtt_avg /= rtt_cnt;
|
||||
rsp->router.total_conns = (uint32_t)queue_entry_count(instance->router_conns);
|
||||
}
|
||||
|
||||
/* Fill link metrics */
|
||||
struct etcpmon_link_metrics* link_info = (struct etcpmon_link_metrics*)(buffer + sizeof(*hdr) + sizeof(*rsp));
|
||||
link = conn->links;
|
||||
@@ -1298,35 +1386,36 @@ static struct ETCP_CONN* find_connection_by_peer_id(struct UTUN_INSTANCE* instan
|
||||
* Node Info Helpers (send_single_node_info, send_node_list, notify_change)
|
||||
* ============================================================================ */
|
||||
|
||||
static void send_single_node_info(struct control_server* server, struct control_client* client, struct NODEINFO_Q* nq) {
|
||||
int dyn = nodeinfo_dyn_size(&nq->node);
|
||||
size_t np_size = sizeof(struct etcpmon_msg_header) + 2 + sizeof(struct NODEINFO) + dyn;
|
||||
static void send_single_node_info(struct control_server* server, struct control_client* client, struct TOPO_NODEQ* nq) {
|
||||
if (!server || !nq || !nq->node) return;
|
||||
uint8_t buf[8192];
|
||||
int ser_len = topo_node_serialize(topo_groups_get_default(server->instance->topo_groups), nq, buf + 2, sizeof(buf) - 2);
|
||||
if (ser_len < 0) { DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Failed to serialize node info"); return; }
|
||||
buf[0] = CTRL_BGP_CMD; buf[1] = CTRL_BGP_SUBCMD;
|
||||
size_t data_size = 2 + (size_t)ser_len;
|
||||
|
||||
size_t np_size = sizeof(struct etcpmon_msg_header) + data_size;
|
||||
uint8_t* buffer = (uint8_t*)u_malloc(np_size);
|
||||
if (!buffer) { DEBUG_ERROR(DEBUG_CATEGORY_CONTROL, "Failed to allocate node info buffer"); return; }
|
||||
|
||||
struct etcpmon_msg_header* hdr = (struct etcpmon_msg_header*)buffer;
|
||||
etcpmon_build_header(hdr, 2 + sizeof(struct NODEINFO) + dyn, ETCPMON_RSP_NODE_INFO, 0);
|
||||
|
||||
uint8_t* pay = buffer + sizeof(*hdr);
|
||||
pay[0] = CTRL_BGP_CMD; pay[1] = CTRL_BGP_SUBCMD;
|
||||
memcpy(pay + 2, &nq->node, sizeof(struct NODEINFO));
|
||||
memcpy(pay + 2 + sizeof(struct NODEINFO), (uint8_t*)&nq->node + sizeof(struct NODEINFO), dyn);
|
||||
etcpmon_build_header(hdr, (uint16_t)data_size, ETCPMON_RSP_NODE_INFO, 0);
|
||||
memcpy(buffer + sizeof(*hdr), buf, data_size);
|
||||
|
||||
control_client_send(server, client, buffer, (uint16_t)np_size);
|
||||
}
|
||||
|
||||
static void control_server_send_node_list(struct control_server* server, struct control_client* client, uint8_t seq_id) {
|
||||
struct ROUTE_BGP* bgp = server->instance->bgp;
|
||||
if (!bgp) { send_error(client, ETCPMON_ERR_SERVER_BUSY, "BGP not available", seq_id); return; }
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(server->instance->topo_groups);
|
||||
if (!group) { send_error(client, ETCPMON_ERR_SERVER_BUSY, "BGP not available", seq_id); return; }
|
||||
|
||||
/* Send local node first */
|
||||
if (bgp->local_node) send_single_node_info(server, client, bgp->local_node);
|
||||
if (group->local_node) send_single_node_info(server, client, group->local_node);
|
||||
|
||||
/* Send all known remote nodes */
|
||||
struct ll_entry* e = bgp->nodes ? bgp->nodes->head : NULL;
|
||||
struct ll_entry* e = group->nodes ? group->nodes->head : NULL;
|
||||
while (e) {
|
||||
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e;
|
||||
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e;
|
||||
send_single_node_info(server, client, nq);
|
||||
e = e->next;
|
||||
}
|
||||
@@ -1341,7 +1430,7 @@ static void control_server_send_node_list(struct control_server* server, struct
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONTROL, "Sent node list to client (seq=%d)", seq_id);
|
||||
}
|
||||
|
||||
void control_server_notify_node_change(struct control_server* server, struct NODEINFO_Q* node) {
|
||||
void control_server_notify_node_change(struct control_server* server, struct TOPO_NODEQ* node) {
|
||||
if (!server || !node) return;
|
||||
struct control_client* client = server->clients;
|
||||
while (client) {
|
||||
|
||||
@@ -19,7 +19,7 @@ extern "C" {
|
||||
/* Forward declarations */
|
||||
struct UTUN_INSTANCE;
|
||||
struct UASYNC;
|
||||
struct NODEINFO_Q;
|
||||
struct TOPO_NODEQ;
|
||||
struct ll_queue;
|
||||
|
||||
/* Forward declaration */
|
||||
@@ -40,6 +40,7 @@ struct control_client {
|
||||
uint64_t selected_peer_id; /* 0 = none selected */
|
||||
|
||||
struct ll_queue* output_queue; /* Output queue for async sends */
|
||||
void* idle_timer; /* Client idle timeout */
|
||||
uint8_t subscribed_nodes; /* 1 = subscribed to node updates */
|
||||
uint8_t write_registered; /* 1 = write events registered in uasync */
|
||||
};
|
||||
@@ -116,7 +117,7 @@ void control_server_process_updates(struct control_server* server);
|
||||
*/
|
||||
uint32_t control_server_get_client_count(const struct control_server* server);
|
||||
|
||||
void control_server_notify_node_change(struct control_server* server, struct NODEINFO_Q* node);
|
||||
void control_server_notify_node_change(struct control_server* server, struct TOPO_NODEQ* node);
|
||||
void control_server_notify_node_removed(struct control_server* server, uint64_t node_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#ifndef CRC32_H
|
||||
#define CRC32_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -16,4 +21,8 @@ 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);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // CRC32_H
|
||||
+940
@@ -0,0 +1,940 @@
|
||||
// db_sync.c — Distributed content-addressed table with LMDB + peer sync via etcp_router
|
||||
|
||||
#include "db_sync.h"
|
||||
#include "etcp_api.h"
|
||||
#include "etcp.h"
|
||||
#include "etcp_router.h"
|
||||
#include "utun_instance.h"
|
||||
#include "topo_group.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include "../lib/sha256.h"
|
||||
#include "../lib/liblmdb/lmdb.h"
|
||||
#include "../lib/platform_compat.h"
|
||||
#include <string.h>
|
||||
|
||||
#define DEBUG_CATEGORY_DB_SYNC DEBUG_CATEGORY_DEBUG
|
||||
|
||||
// ---- LMDB key/value layout ----
|
||||
// Key: 16 bytes = timestamp:8 BE || datahash:8 BE (sorted by timestamp first)
|
||||
// Value: id:8 || chain_hash:32 || creator:8 || flags:1 || data_len:4 || data:variable
|
||||
#define DB_VAL_OFF_ID 0
|
||||
#define DB_VAL_OFF_CHAIN_HASH 8
|
||||
#define DB_VAL_OFF_CREATOR 40
|
||||
#define DB_VAL_OFF_FLAGS 48
|
||||
#define DB_VAL_OFF_DATA_LEN 49
|
||||
#define DB_VAL_OFF_DATA 53
|
||||
#define DB_VAL_HDR_SIZE 53
|
||||
|
||||
// ---- Module state ----
|
||||
struct DB_SYNC {
|
||||
struct UTUN_INSTANCE* inst;
|
||||
MDB_env* env;
|
||||
MDB_dbi dbi_records;
|
||||
MDB_dbi dbi_meta;
|
||||
uint64_t next_id;
|
||||
uint64_t last_connected_tb; // timebase when any peer was last connected (for TTL)
|
||||
uint64_t last_timestamp_us; // гарантия монотонности timestamp
|
||||
|
||||
struct DB_SYNC_PEER {
|
||||
uint64_t node_id;
|
||||
uint8_t synced; // 0=not, 1=in_progress, 2=synced
|
||||
}* peers;
|
||||
int peer_count;
|
||||
int peer_capacity;
|
||||
|
||||
void* ttl_timer;
|
||||
void* peer_check_timer;
|
||||
uint8_t enabled;
|
||||
};
|
||||
|
||||
|
||||
// ---- Forward declarations ----
|
||||
static void db_sync_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||||
static void db_sync_on_new_conn(struct ETCP_CONN* conn, void* arg);
|
||||
static void db_sync_on_conn_up(struct ETCP_CONN* conn, void* arg);
|
||||
static void db_sync_on_conn_down(struct ETCP_CONN* conn, void* arg);
|
||||
static void db_sync_peer_check_cb(void* arg);
|
||||
static void db_sync_ttl_cleanup_cb(void* arg);
|
||||
static int db_sync_send(struct DB_SYNC* db, uint64_t dst_node_id, const uint8_t* payload, size_t len);
|
||||
static void db_sync_initiate_sync(struct DB_SYNC* db, uint64_t peer_node_id);
|
||||
|
||||
// ---- LMDB helpers ----
|
||||
static int db_lmdb_open(struct DB_SYNC* db, const char* path) {
|
||||
int rc = mdb_env_create(&db->env);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: mdb_env_create failed: %s", mdb_strerror(rc)); return -1; }
|
||||
rc = mdb_env_set_mapsize(db->env, DB_SYNC_DEFAULT_MAPSIZE);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: mdb_env_set_mapsize failed: %s", mdb_strerror(rc)); mdb_env_close(db->env); db->env=NULL; return -1; }
|
||||
rc = mdb_env_set_maxdbs(db->env, 3);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: mdb_env_set_maxdbs failed: %s", mdb_strerror(rc)); mdb_env_close(db->env); db->env=NULL; return -1; }
|
||||
rc = mdb_env_open(db->env, path, 0, 0644);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: mdb_env_open(%s) failed: %s", path, mdb_strerror(rc)); mdb_env_close(db->env); db->env=NULL; return -1; }
|
||||
|
||||
MDB_txn* txn;
|
||||
rc = mdb_txn_begin(db->env, NULL, 0, &txn);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: txn_begin failed: %s", mdb_strerror(rc)); mdb_env_close(db->env); db->env=NULL; return -1; }
|
||||
rc = mdb_dbi_open(txn, "records", MDB_CREATE, &db->dbi_records);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: dbi_open(records) failed: %s", mdb_strerror(rc)); mdb_txn_abort(txn); mdb_env_close(db->env); db->env=NULL; return -1; }
|
||||
rc = mdb_dbi_open(txn, "meta", MDB_CREATE, &db->dbi_meta);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: dbi_open(meta) failed: %s", mdb_strerror(rc)); mdb_txn_abort(txn); mdb_env_close(db->env); db->env=NULL; return -1; }
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: txn_commit failed: %s", mdb_strerror(rc)); mdb_env_close(db->env); db->env=NULL; return -1; }
|
||||
|
||||
// Read next_id from meta
|
||||
MDB_txn* rt;
|
||||
rc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &rt);
|
||||
if (rc == MDB_SUCCESS) {
|
||||
MDB_val key, data; key.mv_size = 7; key.mv_data = (char*)"next_id";
|
||||
rc = mdb_get(rt, db->dbi_meta, &key, &data);
|
||||
if (rc == MDB_SUCCESS && data.mv_size >= 8) db->next_id = *(uint64_t*)data.mv_data;
|
||||
else db->next_id = 1;
|
||||
mdb_txn_abort(rt);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: LMDB opened at %s, next_id=%llu", path, (unsigned long long)db->next_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void db_lmdb_close(struct DB_SYNC* db) {
|
||||
if (!db->env) return;
|
||||
mdb_env_close(db->env); db->env = NULL;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: LMDB closed");
|
||||
}
|
||||
|
||||
// Build 16-byte key: timestamp:8 BE || datahash:8 BE
|
||||
static void db_key_build(uint8_t key[16], uint64_t timestamp, uint64_t datahash) {
|
||||
uint64_t ts_be = htobe64(timestamp), dh_be = htobe64(datahash);
|
||||
memcpy(key, &ts_be, 8); memcpy(key + 8, &dh_be, 8);
|
||||
}
|
||||
|
||||
// Get chain_hash at a given sorted position (0-based) via cursor. Returns 0 on success.
|
||||
static int db_chain_hash_at(struct DB_SYNC* db, uint32_t pos, uint8_t chain_hash_out[32]) {
|
||||
MDB_txn* txn; int rc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: chain_hash_at txn_begin failed: %s", mdb_strerror(rc)); return -1; }
|
||||
MDB_cursor* cursor; rc = mdb_cursor_open(txn, db->dbi_records, &cursor);
|
||||
if (rc) { mdb_txn_abort(txn); return -1; }
|
||||
MDB_val key, val;
|
||||
rc = mdb_cursor_get(cursor, &key, &val, MDB_FIRST);
|
||||
for (uint32_t i = 0; rc == 0 && i < pos; i++) rc = mdb_cursor_get(cursor, &key, &val, MDB_NEXT);
|
||||
if (rc == 0 && val.mv_size >= DB_VAL_HDR_SIZE) memcpy(chain_hash_out, (uint8_t*)val.mv_data + DB_VAL_OFF_CHAIN_HASH, 32);
|
||||
else { mdb_cursor_close(cursor); mdb_txn_abort(txn); return -1; }
|
||||
mdb_cursor_close(cursor); mdb_txn_abort(txn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get count of records
|
||||
static uint32_t db_count(struct DB_SYNC* db) {
|
||||
MDB_txn* txn; int rc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc) return 0;
|
||||
MDB_stat stat; rc = mdb_stat(txn, db->dbi_records, &stat);
|
||||
mdb_txn_abort(txn);
|
||||
return rc == 0 ? (uint32_t)stat.ms_entries : 0;
|
||||
}
|
||||
|
||||
// Get datahash at given sorted position
|
||||
static int db_datahash_at(struct DB_SYNC* db, uint32_t pos, uint64_t* datahash) {
|
||||
MDB_txn* txn; int rc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc) return -1;
|
||||
MDB_cursor* cursor; rc = mdb_cursor_open(txn, db->dbi_records, &cursor);
|
||||
if (rc) { mdb_txn_abort(txn); return -1; }
|
||||
MDB_val key, val; rc = mdb_cursor_get(cursor, &key, &val, MDB_FIRST);
|
||||
for (uint32_t i=0; rc==0 && i<pos; i++) rc = mdb_cursor_get(cursor, &key, &val, MDB_NEXT);
|
||||
if (rc==0) {
|
||||
*datahash = be64toh(*(uint64_t*)((uint8_t*)key.mv_data+8));
|
||||
mdb_cursor_close(cursor); mdb_txn_abort(txn); return 0;
|
||||
}
|
||||
mdb_cursor_close(cursor); mdb_txn_abort(txn); return -1;
|
||||
}
|
||||
|
||||
// ---- SHA256 helpers ----
|
||||
static void db_sha256(const uint8_t* data, size_t len, uint8_t hash[32]) {
|
||||
SC_SHA256_CTX ctx; sc_sha256_init(&ctx); sc_sha256_update(&ctx, data, len); sc_sha256_final(&ctx, hash);
|
||||
}
|
||||
|
||||
// Compute datahash: first 64 bits of SHA256(json_data)
|
||||
static uint64_t db_datahash(const uint8_t* data, size_t len) {
|
||||
uint8_t hash[32]; db_sha256(data, len, hash);
|
||||
uint64_t dh; memcpy(&dh, hash, 8); return dh;
|
||||
}
|
||||
|
||||
// Compute chain_hash for a record
|
||||
static void db_chain_hash_compute(const uint8_t prev_chain_hash[32], uint64_t id, uint64_t timestamp, uint64_t datahash, uint8_t out[32]) {
|
||||
uint8_t buf[32+8+8+8]; // prev_chain_hash:32 + id:8 + timestamp:8 + datahash:8
|
||||
memcpy(buf, prev_chain_hash, 32);
|
||||
memcpy(buf+32, &id, 8); memcpy(buf+40, ×tamp, 8); memcpy(buf+48, &datahash, 8);
|
||||
db_sha256(buf, 56, out);
|
||||
}
|
||||
|
||||
// Get previous record's chain_hash (record before the given key in sorted order)
|
||||
static int db_prev_chain_hash(struct DB_SYNC* db, const uint8_t key[16], uint8_t chain_hash[32]) {
|
||||
MDB_txn* txn; int rc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc) return -1;
|
||||
MDB_cursor* cursor; rc = mdb_cursor_open(txn, db->dbi_records, &cursor);
|
||||
if (rc) { mdb_txn_abort(txn); return -1; }
|
||||
MDB_val search_key, val; search_key.mv_size = 16; search_key.mv_data = (void*)key;
|
||||
rc = mdb_cursor_get(cursor, &search_key, &val, MDB_SET_RANGE);
|
||||
if (rc == 0) {
|
||||
rc = mdb_cursor_get(cursor, &search_key, &val, MDB_PREV);
|
||||
if (rc == 0 && val.mv_size >= DB_VAL_HDR_SIZE) {
|
||||
memcpy(chain_hash, (uint8_t*)val.mv_data + DB_VAL_OFF_CHAIN_HASH, 32);
|
||||
mdb_cursor_close(cursor); mdb_txn_abort(txn); return 0;
|
||||
}
|
||||
}
|
||||
// No previous record → use zero hash
|
||||
memset(chain_hash, 0, 32);
|
||||
mdb_cursor_close(cursor); mdb_txn_abort(txn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---- Insert record ----
|
||||
static int db_record_insert(struct DB_SYNC* db, uint64_t id, uint64_t timestamp, uint64_t datahash, const char* json, size_t json_len) {
|
||||
uint8_t key[16]; db_key_build(key, timestamp, datahash);
|
||||
|
||||
// Check duplicate
|
||||
MDB_txn* txn; int rc = mdb_txn_begin(db->env, NULL, 0, &txn);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: insert txn_begin failed: %s", mdb_strerror(rc)); return -1; }
|
||||
MDB_val dup_key, dup_val; dup_key.mv_size = 16; dup_key.mv_data = key;
|
||||
rc = mdb_get(txn, db->dbi_records, &dup_key, &dup_val);
|
||||
if (rc == MDB_SUCCESS) { mdb_txn_abort(txn); return 1; } // already exists
|
||||
|
||||
// Compute chain_hash
|
||||
uint8_t prev_ch[32]; db_prev_chain_hash(db, key, prev_ch);
|
||||
uint8_t chain_hash[32]; db_chain_hash_compute(prev_ch, id, timestamp, datahash, chain_hash);
|
||||
|
||||
// Build value
|
||||
size_t val_size = DB_VAL_HDR_SIZE + json_len;
|
||||
uint8_t* val_buf = u_malloc(val_size);
|
||||
if (!val_buf) { mdb_txn_abort(txn); return -1; }
|
||||
uint64_t creator = db->inst->node_id;
|
||||
memcpy(val_buf + DB_VAL_OFF_ID, &id, 8);
|
||||
memcpy(val_buf + DB_VAL_OFF_CHAIN_HASH, chain_hash, 32);
|
||||
memcpy(val_buf + DB_VAL_OFF_CREATOR, &creator, 8);
|
||||
val_buf[DB_VAL_OFF_FLAGS] = 0;
|
||||
*(uint32_t*)(val_buf + DB_VAL_OFF_DATA_LEN) = (uint32_t)json_len;
|
||||
if (json_len > 0) memcpy(val_buf + DB_VAL_OFF_DATA, json, json_len);
|
||||
|
||||
MDB_val val; val.mv_size = val_size; val.mv_data = val_buf;
|
||||
MDB_val k; k.mv_size = 16; k.mv_data = key;
|
||||
rc = mdb_put(txn, db->dbi_records, &k, &val, 0);
|
||||
u_free(val_buf);
|
||||
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: mdb_put failed: %s", mdb_strerror(rc)); mdb_txn_abort(txn); return -1; }
|
||||
|
||||
// Update chain_hash for subsequent records (out-of-order insert case)
|
||||
// Check if there are records AFTER this key
|
||||
MDB_cursor* cursor; rc = mdb_cursor_open(txn, db->dbi_records, &cursor);
|
||||
if (rc == 0) {
|
||||
MDB_val ckey, cval; ckey.mv_size = 16; ckey.mv_data = key;
|
||||
rc = mdb_cursor_get(cursor, &ckey, &cval, MDB_SET);
|
||||
if (rc == 0) {
|
||||
uint8_t running_ch[32]; memcpy(running_ch, chain_hash, 32);
|
||||
uint64_t running_id = id;
|
||||
uint64_t running_ts = timestamp;
|
||||
uint64_t running_dh = datahash;
|
||||
// Move to next
|
||||
while (mdb_cursor_get(cursor, &ckey, &cval, MDB_NEXT) == 0) {
|
||||
if (cval.mv_size < DB_VAL_HDR_SIZE) continue;
|
||||
uint64_t n_id = *(uint64_t*)((uint8_t*)cval.mv_data + DB_VAL_OFF_ID);
|
||||
uint64_t n_ts = be64toh(*(uint64_t*)ckey.mv_data);
|
||||
uint64_t n_dh = be64toh(*(uint64_t*)((uint8_t*)ckey.mv_data + 8));
|
||||
uint8_t new_ch[32]; db_chain_hash_compute(running_ch, n_id, n_ts, n_dh, new_ch);
|
||||
memcpy((uint8_t*)cval.mv_data + DB_VAL_OFF_CHAIN_HASH, new_ch, 32);
|
||||
mdb_cursor_put(cursor, &ckey, &cval, MDB_CURRENT);
|
||||
memcpy(running_ch, new_ch, 32);
|
||||
}
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
}
|
||||
|
||||
// Update next_id in meta
|
||||
MDB_val mk, md; mk.mv_size = 7; mk.mv_data = (char*)"next_id"; md.mv_size = 8; md.mv_data = &id; id++;
|
||||
mdb_put(txn, db->dbi_meta, &mk, &md, 0);
|
||||
db->next_id = id;
|
||||
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: insert commit failed: %s", mdb_strerror(rc)); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---- Send ----
|
||||
static int db_sync_send(struct DB_SYNC* db, uint64_t dst_node_id, const uint8_t* payload, size_t len) {
|
||||
struct ll_entry* entry = queue_entry_new(0);
|
||||
if (!entry) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "queue_entry_new failed"); return -1; }
|
||||
uint8_t* buf = u_malloc(len + 1);
|
||||
if (!buf) { queue_entry_free(entry); return -1; }
|
||||
buf[0] = ETCP_RT_ID_DB_SYNC; memcpy(buf + 1, payload, len);
|
||||
entry->dgram = buf; entry->len = len + 1;
|
||||
int ret = etcp_route_send(db->inst, dst_node_id, entry, 0);
|
||||
// etcp_route_send always takes ownership and frees entry in all code paths
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ---- Peer management ----
|
||||
static struct DB_SYNC_PEER* db_peer_find(struct DB_SYNC* db, uint64_t node_id) {
|
||||
for (int i=0; i<db->peer_count; i++) if (db->peers[i].node_id == node_id) return &db->peers[i];
|
||||
return NULL;
|
||||
}
|
||||
static struct DB_SYNC_PEER* db_peer_add(struct DB_SYNC* db, uint64_t node_id) {
|
||||
struct DB_SYNC_PEER* p = db_peer_find(db, node_id);
|
||||
if (p) return p;
|
||||
if (db->peer_count >= db->peer_capacity) {
|
||||
int nc = db->peer_capacity ? db->peer_capacity * 2 : 8;
|
||||
struct DB_SYNC_PEER* np = u_realloc(db->peers, nc * sizeof(struct DB_SYNC_PEER));
|
||||
if (!np) return NULL;
|
||||
db->peers = np; db->peer_capacity = nc;
|
||||
}
|
||||
p = &db->peers[db->peer_count++];
|
||||
p->node_id = node_id; p->synced = 0;
|
||||
return p;
|
||||
}
|
||||
|
||||
// ---- Sync protocol: INIT_SYNC (A→B) ----
|
||||
static void db_handle_init_sync(struct DB_SYNC* db, uint64_t src_node_id, const uint8_t* payload, size_t len) {
|
||||
if (len < 36) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: INIT_SYNC too short %zu from %016llx", len, (unsigned long long)src_node_id); return; }
|
||||
uint32_t peer_count = *(uint32_t*)payload;
|
||||
const uint8_t* peer_last_ch = payload + 4;
|
||||
|
||||
uint32_t my_count = db_count(db);
|
||||
uint32_t test_pos = (peer_count < my_count ? peer_count : my_count);
|
||||
if (test_pos > 0) test_pos--;
|
||||
|
||||
// Send INIT_RESP
|
||||
uint8_t resp[4096];
|
||||
uint32_t resp_off = 0;
|
||||
resp[resp_off++] = DB_MSG_INIT_RESP;
|
||||
memcpy(resp+resp_off, &test_pos, 4); resp_off += 4;
|
||||
|
||||
uint8_t my_ch[32];
|
||||
if (my_count == 0) {
|
||||
memset(my_ch, 0, 32);
|
||||
} else if (db_chain_hash_at(db, test_pos, my_ch) != 0) {
|
||||
memset(my_ch, 0, 32);
|
||||
}
|
||||
memcpy(resp+resp_off, my_ch, 32); resp_off += 32;
|
||||
|
||||
// Always include sparse_count at position 37 (after test_pos + chain_hash)
|
||||
// For complete case: sparse_count=0, no sparse entries follow
|
||||
// For sparse case: sparse entries start at position 38
|
||||
|
||||
// Check if chain_hash matches
|
||||
if (memcmp(my_ch, peer_last_ch, 32) == 0) {
|
||||
resp[resp_off++] = 0; // sparse_count = 0
|
||||
db_sync_send(db, src_node_id, resp, resp_off);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: INIT_RESP complete to %016llx test_pos=%u", (unsigned long long)src_node_id, test_pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip sparse_count slot for now, write it at the end
|
||||
uint32_t sparse_count_pos = resp_off;
|
||||
resp_off++; // placeholder for sparse_count
|
||||
|
||||
// Build sparse hashes: chain_hash at positions test_pos-1, -2, -4, -8, -16, ...
|
||||
int sparse_count = 0;
|
||||
for (uint32_t k = 0; k < 16; k++) {
|
||||
uint32_t step = (uint32_t)(1u << k); // 1, 2, 4, 8, ...
|
||||
if (test_pos < step) break;
|
||||
uint32_t pos = test_pos - step;
|
||||
if (db_chain_hash_at(db, pos, my_ch) != 0) break;
|
||||
if (resp_off + 36 > sizeof(resp)) break;
|
||||
memcpy(resp+resp_off, &pos, 4); resp_off += 4;
|
||||
memcpy(resp+resp_off, my_ch, 32); resp_off += 32;
|
||||
sparse_count++;
|
||||
}
|
||||
resp[sparse_count_pos] = (uint8_t)sparse_count;
|
||||
|
||||
db_sync_send(db, src_node_id, resp, resp_off);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: INIT_RESP to %016llx test_pos=%u sparse=%d", (unsigned long long)src_node_id, test_pos, sparse_count);
|
||||
}
|
||||
|
||||
// ---- Sync protocol: INIT_RESP (B→A) ----
|
||||
static void db_handle_init_resp(struct DB_SYNC* db, uint64_t src_node_id, const uint8_t* payload, size_t len) {
|
||||
if (len < 37) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: INIT_RESP too short %zu from %016llx", len, (unsigned long long)src_node_id); return; }
|
||||
uint32_t test_pos = *(uint32_t*)payload;
|
||||
const uint8_t* peer_ch = payload + 4;
|
||||
uint8_t sparse_count = payload[36];
|
||||
|
||||
uint8_t my_ch[32];
|
||||
if (db_chain_hash_at(db, test_pos, my_ch) != 0) { memset(my_ch, 0, 32); }
|
||||
|
||||
// Check if complete
|
||||
if (sparse_count == 0 && memcmp(my_ch, peer_ch, 32) == 0) {
|
||||
struct DB_SYNC_PEER* p = db_peer_find(db, src_node_id);
|
||||
if (p) p->synced = 2;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: sync complete with %016llx", (unsigned long long)src_node_id);
|
||||
return;
|
||||
}
|
||||
|
||||
// If peer has empty table (chain_hash is all zeros), send everything
|
||||
{
|
||||
uint8_t zero[32]; memset(zero, 0, 32);
|
||||
if (memcmp(peer_ch, zero, 32) == 0 && sparse_count == 0) {
|
||||
uint32_t my_count = db_count(db);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: peer %016llx has empty table, sending all %u records", (unsigned long long)src_node_id, my_count);
|
||||
// Send all records in batches
|
||||
uint32_t sent = 0;
|
||||
while (sent < my_count) {
|
||||
uint32_t batch = my_count - sent;
|
||||
if (batch > DB_SEND_DATA_MAX) batch = DB_SEND_DATA_MAX;
|
||||
uint8_t sdbuf[8192]; uint32_t off=0;
|
||||
sdbuf[off++] = DB_MSG_SEND_DATA; memcpy(sdbuf+off, &sent, 4); off+=4;
|
||||
uint16_t* rcp = (uint16_t*)(sdbuf+off); off+=2; uint16_t rc=0;
|
||||
MDB_txn* txn; int rrc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &txn);
|
||||
if (rrc==0) { MDB_cursor* cursor; rrc=mdb_cursor_open(txn, db->dbi_records, &cursor);
|
||||
if (rrc==0) { MDB_val key,val; rrc=mdb_cursor_get(cursor, &key, &val, MDB_FIRST);
|
||||
uint32_t cur=0; while (rrc==0 && cur < sent) { rrc=mdb_cursor_get(cursor,&key,&val,MDB_NEXT); cur++; }
|
||||
while (rrc==0 && rc < batch && off+val.mv_size+16 < 8000) {
|
||||
uint64_t ts = be64toh(*(uint64_t*)key.mv_data); uint64_t dh = be64toh(*(uint64_t*)((uint8_t*)key.mv_data+8));
|
||||
uint64_t rid = *(uint64_t*)((uint8_t*)val.mv_data+DB_VAL_OFF_ID); uint32_t dlen = *(uint32_t*)((uint8_t*)val.mv_data+DB_VAL_OFF_DATA_LEN);
|
||||
memcpy(sdbuf+off, &rid,8); off+=8; memcpy(sdbuf+off, &ts,8); off+=8; memcpy(sdbuf+off, &dh,8); off+=8;
|
||||
memcpy(sdbuf+off, &dlen,4); off+=4; if (dlen>0) { memcpy(sdbuf+off, (uint8_t*)val.mv_data+DB_VAL_OFF_DATA, dlen); off+=dlen; }
|
||||
rc++; cur++; rrc=mdb_cursor_get(cursor, &key, &val, MDB_NEXT); }
|
||||
mdb_cursor_close(cursor); } mdb_txn_abort(txn); }
|
||||
*rcp = rc; sent += rc;
|
||||
db_sync_send(db, src_node_id, sdbuf, off);
|
||||
if (rc == 0) break; // no more records
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (memcmp(my_ch, peer_ch, 32) == 0) {
|
||||
struct DB_SYNC_PEER* p = db_peer_find(db, src_node_id);
|
||||
if (p) p->synced = 2;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: chain_hash match with %016llx at pos %u — synced", (unsigned long long)src_node_id, test_pos);
|
||||
// Send our extra records if any (beyond test_pos)
|
||||
uint32_t my_count = db_count(db);
|
||||
if (my_count > test_pos + 1) {
|
||||
// We have more records — send them
|
||||
uint32_t from = test_pos + 1;
|
||||
uint32_t remaining = my_count - from;
|
||||
uint32_t batch = remaining < DB_SEND_DATA_MAX ? remaining : DB_SEND_DATA_MAX;
|
||||
// Continue in SEND_DATA handler...
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Find divergence range using sparse hashes
|
||||
uint32_t div_start = 0, div_end = test_pos;
|
||||
const uint8_t* sparse = payload + 37;
|
||||
for (int i = 0; i < sparse_count && sparse + 36 <= payload + len; i++) {
|
||||
uint32_t pos = *(uint32_t*)sparse;
|
||||
const uint8_t* peer_sparse_ch = sparse + 4;
|
||||
uint8_t my_sparse_ch[32];
|
||||
if (db_chain_hash_at(db, pos, my_sparse_ch) == 0) {
|
||||
if (memcmp(my_sparse_ch, peer_sparse_ch, 32) == 0) { if (pos + 1 > div_start) div_start = pos + 1; }
|
||||
else { if (pos < div_end) div_end = pos; }
|
||||
}
|
||||
sparse += 36;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: divergence with %016llx range [%u, %u]", (unsigned long long)src_node_id, div_start, div_end);
|
||||
|
||||
if (div_end - div_start <= 1) {
|
||||
// Divergence is small — request data directly via REFINE round
|
||||
uint8_t ref[512]; uint32_t ref_off = 0;
|
||||
ref[ref_off++] = DB_MSG_REFINE;
|
||||
memcpy(ref+ref_off, &div_start, 4); ref_off += 4;
|
||||
memcpy(ref+ref_off, &div_end, 4); ref_off += 4;
|
||||
uint8_t hcount = 0; ref[ref_off++] = hcount; // no hashes, just requesting data
|
||||
db_sync_send(db, src_node_id, ref, ref_off);
|
||||
} else {
|
||||
// Send REFINE with our hashes
|
||||
uint8_t ref[512]; uint32_t ref_off = 0;
|
||||
ref[ref_off++] = DB_MSG_REFINE;
|
||||
memcpy(ref+ref_off, &div_start, 4); ref_off += 4;
|
||||
memcpy(ref+ref_off, &div_end, 4); ref_off += 4;
|
||||
uint32_t range = div_end - div_start;
|
||||
uint8_t count = range < DB_REFINE_HASHES ? (uint8_t)range : DB_REFINE_HASHES;
|
||||
ref[ref_off++] = count;
|
||||
for (uint8_t i = 0; i < count; i++) {
|
||||
uint32_t pos = div_start + (range * i / count);
|
||||
uint64_t dh;
|
||||
if (db_datahash_at(db, pos, &dh) == 0) {
|
||||
memcpy(ref+ref_off, &pos, 4); ref_off += 4;
|
||||
memcpy(ref+ref_off, &dh, 8); ref_off += 8;
|
||||
}
|
||||
}
|
||||
db_sync_send(db, src_node_id, ref, ref_off);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Sync protocol: REFINE ----
|
||||
static void db_handle_refine(struct DB_SYNC* db, uint64_t src_node_id, const uint8_t* payload, size_t len) {
|
||||
if (len < 9) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "db_sync: REFINE too short %zu", len); return; }
|
||||
uint32_t from = *(uint32_t*)payload;
|
||||
uint32_t to = *(uint32_t*)(payload+4);
|
||||
uint8_t hcount = payload[8];
|
||||
|
||||
if (hcount == 0) {
|
||||
// Peer requests data in range [from, to]
|
||||
uint32_t my_count = db_count(db);
|
||||
uint32_t send_from = from;
|
||||
uint32_t send_count = (to - from + 1) < DB_SEND_DATA_MAX ? (to - from + 1) : DB_SEND_DATA_MAX;
|
||||
if (send_from >= my_count) return; // nothing to send
|
||||
|
||||
// Build SEND_DATA
|
||||
uint8_t* buf = u_malloc(8192);
|
||||
if (!buf) return;
|
||||
uint32_t off = 0; buf[off++] = DB_MSG_SEND_DATA;
|
||||
memcpy(buf+off, &send_from, 4); off += 4;
|
||||
uint16_t rec_count = 0;
|
||||
uint16_t* cnt_ptr = (uint16_t*)(buf+off); off += 2;
|
||||
|
||||
MDB_txn* txn; int rc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &txn);
|
||||
if (rc == 0) {
|
||||
MDB_cursor* cursor; rc = mdb_cursor_open(txn, db->dbi_records, &cursor);
|
||||
if (rc == 0) {
|
||||
MDB_val key, val; rc = mdb_cursor_get(cursor, &key, &val, MDB_FIRST);
|
||||
uint32_t cur = 0;
|
||||
while (rc == 0 && cur < send_from) { rc = mdb_cursor_get(cursor, &key, &val, MDB_NEXT); cur++; }
|
||||
while (rc == 0 && rec_count < send_count && off + val.mv_size + 16 < 8000) {
|
||||
uint64_t ts = be64toh(*(uint64_t*)key.mv_data);
|
||||
uint64_t dh = be64toh(*(uint64_t*)((uint8_t*)key.mv_data + 8));
|
||||
uint64_t rid = *(uint64_t*)((uint8_t*)val.mv_data + DB_VAL_OFF_ID);
|
||||
uint32_t dlen = *(uint32_t*)((uint8_t*)val.mv_data + DB_VAL_OFF_DATA_LEN);
|
||||
const uint8_t* dptr = (uint8_t*)val.mv_data + DB_VAL_OFF_DATA;
|
||||
memcpy(buf+off, &rid, 8); off += 8;
|
||||
memcpy(buf+off, &ts, 8); off += 8;
|
||||
memcpy(buf+off, &dh, 8); off += 8;
|
||||
memcpy(buf+off, &dlen, 4); off += 4;
|
||||
if (dlen > 0) { memcpy(buf+off, dptr, dlen); off += dlen; }
|
||||
rec_count++;
|
||||
rc = mdb_cursor_get(cursor, &key, &val, MDB_NEXT);
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
}
|
||||
mdb_txn_abort(txn);
|
||||
}
|
||||
*cnt_ptr = rec_count;
|
||||
db_sync_send(db, src_node_id, buf, off);
|
||||
u_free(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
// Peer sent hashes at positions — find mismatch
|
||||
uint32_t first_mismatch = to + 1;
|
||||
const uint8_t* hp = payload + 9;
|
||||
for (uint8_t i = 0; i < hcount && hp + 12 <= payload + len; i++) {
|
||||
uint32_t pos = *(uint32_t*)hp;
|
||||
uint64_t peer_dh = *(uint64_t*)(hp + 4);
|
||||
uint64_t my_dh;
|
||||
if (db_datahash_at(db, pos, &my_dh) == 0 && my_dh == peer_dh) {
|
||||
if (pos >= from) { if (pos + 1 < first_mismatch) first_mismatch = pos + 1; }
|
||||
} else {
|
||||
if (pos < first_mismatch) first_mismatch = pos;
|
||||
}
|
||||
hp += 12;
|
||||
}
|
||||
|
||||
// Send SEND_DATA from first_mismatch
|
||||
uint32_t my_count = db_count(db);
|
||||
uint32_t send_count = 4;
|
||||
if (first_mismatch > to || first_mismatch >= my_count) { send_count = 0; }
|
||||
|
||||
uint8_t sdbuf[8192]; uint32_t off = 0;
|
||||
sdbuf[off++] = DB_MSG_SEND_DATA;
|
||||
memcpy(sdbuf+off, &first_mismatch, 4); off += 4;
|
||||
uint16_t rc_count = 0;
|
||||
uint16_t* rcp = (uint16_t*)(sdbuf+off); off += 2;
|
||||
|
||||
if (send_count > 0) {
|
||||
MDB_txn* txn; int rrc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &txn);
|
||||
if (rrc == 0) {
|
||||
MDB_cursor* cursor; rrc = mdb_cursor_open(txn, db->dbi_records, &cursor);
|
||||
if (rrc == 0) {
|
||||
MDB_val key, val; rrc = mdb_cursor_get(cursor, &key, &val, MDB_FIRST);
|
||||
uint32_t cur = 0;
|
||||
while (rrc == 0 && cur < first_mismatch) { rrc = mdb_cursor_get(cursor, &key, &val, MDB_NEXT); cur++; }
|
||||
while (rrc == 0 && rc_count < send_count && off + val.mv_size + 16 < 8000) {
|
||||
uint64_t ts = be64toh(*(uint64_t*)key.mv_data);
|
||||
uint64_t dh = be64toh(*(uint64_t*)((uint8_t*)key.mv_data + 8));
|
||||
uint64_t rid = *(uint64_t*)((uint8_t*)val.mv_data + DB_VAL_OFF_ID);
|
||||
uint32_t dlen = *(uint32_t*)((uint8_t*)val.mv_data + DB_VAL_OFF_DATA_LEN);
|
||||
const uint8_t* dptr = (uint8_t*)val.mv_data + DB_VAL_OFF_DATA;
|
||||
memcpy(sdbuf+off, &rid, 8); off += 8;
|
||||
memcpy(sdbuf+off, &ts, 8); off += 8;
|
||||
memcpy(sdbuf+off, &dh, 8); off += 8;
|
||||
memcpy(sdbuf+off, &dlen, 4); off += 4;
|
||||
if (dlen > 0) { memcpy(sdbuf+off, dptr, dlen); off += dlen; }
|
||||
rc_count++; rrc = mdb_cursor_get(cursor, &key, &val, MDB_NEXT);
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
}
|
||||
mdb_txn_abort(txn);
|
||||
}
|
||||
}
|
||||
*rcp = rc_count;
|
||||
db_sync_send(db, src_node_id, sdbuf, off);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: SEND_DATA to %016llx from=%u count=%u", (unsigned long long)src_node_id, first_mismatch, rc_count);
|
||||
}
|
||||
|
||||
// ---- Sync protocol: SEND_DATA ----
|
||||
static void db_handle_send_data(struct DB_SYNC* db, uint64_t src_node_id, const uint8_t* payload, size_t len) {
|
||||
if (len < 6) return;
|
||||
uint32_t from = *(uint32_t*)payload;
|
||||
uint16_t count = *(uint16_t*)(payload+4);
|
||||
const uint8_t* ptr = payload + 6;
|
||||
|
||||
for (uint16_t i = 0; i < count; i++) {
|
||||
if (ptr + 28 > payload + len) break;
|
||||
uint64_t rid = *(uint64_t*)ptr; ptr += 8;
|
||||
uint64_t ts = *(uint64_t*)ptr; ptr += 8;
|
||||
uint64_t dh = *(uint64_t*)ptr; ptr += 8;
|
||||
uint32_t dlen = *(uint32_t*)ptr; ptr += 4;
|
||||
if (ptr + dlen > payload + len) break;
|
||||
db_record_insert(db, rid, ts, dh, (const char*)ptr, dlen);
|
||||
ptr += dlen;
|
||||
}
|
||||
|
||||
// Send our extra records too (bidirectional)
|
||||
uint32_t my_count = db_count(db);
|
||||
struct DB_SYNC_PEER* p = db_peer_find(db, src_node_id);
|
||||
uint32_t peer_known = from + count;
|
||||
if (my_count > peer_known && p && p->synced == 1) {
|
||||
// Send our records beyond what peer knows
|
||||
uint32_t send_count = my_count - peer_known;
|
||||
if (send_count > DB_SEND_DATA_MAX) send_count = DB_SEND_DATA_MAX;
|
||||
uint8_t sdbuf[8192]; uint32_t off=0;
|
||||
sdbuf[off++] = DB_MSG_SEND_DATA; memcpy(sdbuf+off, &peer_known, 4); off+=4;
|
||||
uint16_t* rcp = (uint16_t*)(sdbuf+off); off+=2;
|
||||
uint16_t rc=0;
|
||||
MDB_txn* txn; int rrc = mdb_txn_begin(db->env, NULL, MDB_RDONLY, &txn);
|
||||
if (rrc==0) {
|
||||
MDB_cursor* cursor; rrc = mdb_cursor_open(txn, db->dbi_records, &cursor);
|
||||
if (rrc==0) {
|
||||
MDB_val key,val; rrc = mdb_cursor_get(cursor, &key, &val, MDB_FIRST);
|
||||
uint32_t cur=0;
|
||||
while (rrc==0 && cur < peer_known) { rrc=mdb_cursor_get(cursor,&key,&val,MDB_NEXT); cur++; }
|
||||
while (rrc==0 && rc<send_count && off+val.mv_size+16 < 8000) {
|
||||
uint64_t rts = be64toh(*(uint64_t*)key.mv_data);
|
||||
uint64_t rdh = be64toh(*(uint64_t*)((uint8_t*)key.mv_data+8));
|
||||
uint64_t rrid = *(uint64_t*)((uint8_t*)val.mv_data+DB_VAL_OFF_ID);
|
||||
uint32_t rdlen = *(uint32_t*)((uint8_t*)val.mv_data+DB_VAL_OFF_DATA_LEN);
|
||||
const uint8_t* rdptr = (uint8_t*)val.mv_data+DB_VAL_OFF_DATA;
|
||||
memcpy(sdbuf+off, &rrid,8); off+=8; memcpy(sdbuf+off, &rts,8); off+=8;
|
||||
memcpy(sdbuf+off, &rdh,8); off+=8; memcpy(sdbuf+off, &rdlen,4); off+=4;
|
||||
if (rdlen>0) { memcpy(sdbuf+off, rdptr, rdlen); off+=rdlen; }
|
||||
rc++; rrc=mdb_cursor_get(cursor, &key, &val, MDB_NEXT);
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
}
|
||||
mdb_txn_abort(txn);
|
||||
}
|
||||
*rcp = rc;
|
||||
db_sync_send(db, src_node_id, sdbuf, off);
|
||||
}
|
||||
|
||||
// Verify sync complete
|
||||
uint32_t new_count = db_count(db);
|
||||
uint8_t my_last_ch[32];
|
||||
if (new_count > 0 && db_chain_hash_at(db, new_count - 1, my_last_ch) == 0) {
|
||||
uint8_t sd[37]; sd[0] = DB_MSG_SYNC_DONE;
|
||||
memcpy(sd+1, &new_count, 4); memcpy(sd+5, my_last_ch, 32);
|
||||
db_sync_send(db, src_node_id, sd, 37);
|
||||
}
|
||||
if (p) p->synced = 2;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: received %u records from %016llx", count, (unsigned long long)src_node_id);
|
||||
}
|
||||
|
||||
// ---- Sync protocol: SYNC_DONE ----
|
||||
static void db_handle_sync_done(struct DB_SYNC* db, uint64_t src_node_id, const uint8_t* payload, size_t len) {
|
||||
if (len < 36) return;
|
||||
uint32_t peer_count = *(uint32_t*)payload;
|
||||
const uint8_t* peer_ch = payload + 4;
|
||||
uint32_t my_count = db_count(db);
|
||||
uint8_t my_ch[32];
|
||||
if (my_count > 0) db_chain_hash_at(db, my_count - 1, my_ch);
|
||||
else memset(my_ch, 0, 32);
|
||||
if (my_count != peer_count || memcmp(my_ch, peer_ch, 32) != 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "db_sync: SYNC_DONE mismatch with %016llx my=%u peer=%u — re-syncing",
|
||||
(unsigned long long)src_node_id, my_count, peer_count);
|
||||
db_sync_initiate_sync(db, src_node_id);
|
||||
return;
|
||||
}
|
||||
struct DB_SYNC_PEER* p = db_peer_find(db, src_node_id);
|
||||
if (p) p->synced = 2;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: sync confirmed with %016llx count=%u", (unsigned long long)src_node_id, my_count);
|
||||
}
|
||||
|
||||
// ---- Sync protocol: ACK_PUSH ----
|
||||
static void db_handle_ack_push(struct DB_SYNC* db, uint64_t src_node_id, const uint8_t* payload, size_t len) {
|
||||
(void)src_node_id;
|
||||
if (len < 16) return;
|
||||
uint64_t dh = *(uint64_t*)payload;
|
||||
uint64_t ts = *(uint64_t*)(payload + 8);
|
||||
uint8_t key[16]; db_key_build(key, ts, dh);
|
||||
MDB_txn* txn; int rc = mdb_txn_begin(db->env, NULL, 0, &txn);
|
||||
if (rc) return;
|
||||
MDB_val k, val; k.mv_size = 16; k.mv_data = key;
|
||||
rc = mdb_get(txn, db->dbi_records, &k, &val);
|
||||
if (rc == 0 && val.mv_size >= DB_VAL_HDR_SIZE) {
|
||||
uint64_t creator = *(uint64_t*)((uint8_t*)val.mv_data + DB_VAL_OFF_CREATOR);
|
||||
uint8_t flags = *(uint8_t*)((uint8_t*)val.mv_data + DB_VAL_OFF_FLAGS);
|
||||
if (creator == db->inst->node_id && !(flags & DB_REC_FLAG_WAS_SENT)) {
|
||||
flags |= DB_REC_FLAG_WAS_SENT;
|
||||
*(uint8_t*)((uint8_t*)val.mv_data + DB_VAL_OFF_FLAGS) = flags;
|
||||
mdb_put(txn, db->dbi_records, &k, &val, 0);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: ACK_PUSH mark sent dh=%016llx ts=%llu from %016llx", (unsigned long long)dh, (unsigned long long)ts, (unsigned long long)src_node_id);
|
||||
}
|
||||
}
|
||||
mdb_txn_commit(txn);
|
||||
}
|
||||
|
||||
// ---- Sync protocol: PUSH ----
|
||||
static void db_handle_push(struct DB_SYNC* db, uint64_t src_node_id, const uint8_t* payload, size_t len) {
|
||||
if (len < 28) return;
|
||||
const uint8_t* ptr = payload;
|
||||
uint64_t rid = *(uint64_t*)ptr; ptr += 8;
|
||||
uint64_t ts = *(uint64_t*)ptr; ptr += 8;
|
||||
uint64_t dh = *(uint64_t*)ptr; ptr += 8;
|
||||
uint32_t dlen = *(uint32_t*)ptr; ptr += 4;
|
||||
if (ptr + dlen > payload + len) return;
|
||||
int ret = db_record_insert(db, rid, ts, dh, (const char*)ptr, dlen);
|
||||
if (ret == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: PUSH inserted from %016llx id=%llu dh=%016llx", (unsigned long long)src_node_id, (unsigned long long)rid, (unsigned long long)dh);
|
||||
uint8_t ack[17]; ack[0] = DB_MSG_ACK_PUSH; memcpy(ack+1, &dh, 8); memcpy(ack+9, &ts, 8);
|
||||
db_sync_send(db, src_node_id, ack, 17);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Main receive callback ----
|
||||
static void db_sync_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (!entry || entry->len < 2) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; }
|
||||
struct DB_SYNC* db = conn ? conn->instance->db_sync : NULL;
|
||||
if (!db || !db->enabled) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
|
||||
uint64_t src_node_id = conn ? conn->peer_node_id : 0;
|
||||
|
||||
uint8_t type = entry->dgram[1];
|
||||
const uint8_t* payload = entry->dgram + 2;
|
||||
size_t plen = entry->len - 2;
|
||||
|
||||
switch (type) {
|
||||
case DB_MSG_INIT_SYNC: db_handle_init_sync(db, src_node_id, payload, plen); break;
|
||||
case DB_MSG_INIT_RESP: db_handle_init_resp(db, src_node_id, payload, plen); break;
|
||||
case DB_MSG_REFINE: db_handle_refine(db, src_node_id, payload, plen); break;
|
||||
case DB_MSG_SEND_DATA: db_handle_send_data(db, src_node_id, payload, plen); break;
|
||||
case DB_MSG_PUSH: db_handle_push(db, src_node_id, payload, plen); break;
|
||||
case DB_MSG_ACK_PUSH: db_handle_ack_push(db, src_node_id, payload, plen); break;
|
||||
case DB_MSG_SYNC_DONE: db_handle_sync_done(db, src_node_id, payload, plen); break;
|
||||
default: DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "db_sync: unknown msg type 0x%02x from %016llx", type, (unsigned long long)src_node_id); break;
|
||||
}
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
}
|
||||
|
||||
// ---- Connection callbacks ----
|
||||
static void db_sync_on_new_conn(struct ETCP_CONN* conn, void* arg) {
|
||||
(void)arg;
|
||||
if (!conn || !conn->instance || !conn->instance->db_sync) return;
|
||||
etcp_conn_add_up_cbk(conn, db_sync_on_conn_up, NULL);
|
||||
etcp_conn_add_down_cbk(conn, db_sync_on_conn_down, NULL);
|
||||
}
|
||||
|
||||
static void db_sync_on_conn_up(struct ETCP_CONN* conn, void* arg) {
|
||||
(void)arg;
|
||||
if (!conn || !conn->instance || !conn->instance->db_sync) return;
|
||||
struct DB_SYNC* db = conn->instance->db_sync;
|
||||
uint64_t peer_id = conn->peer_node_id;
|
||||
if (peer_id == 0 || peer_id == db->inst->node_id) return;
|
||||
db->last_connected_tb = get_time_tb();
|
||||
struct DB_SYNC_PEER* p = db_peer_add(db, peer_id);
|
||||
if (p && p->synced == 0) {
|
||||
p->synced = 1;
|
||||
db_sync_initiate_sync(db, peer_id);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: peer up %016llx", (unsigned long long)peer_id);
|
||||
}
|
||||
|
||||
static void db_sync_on_conn_down(struct ETCP_CONN* conn, void* arg) {
|
||||
(void)arg;
|
||||
if (!conn || !conn->instance || !conn->instance->db_sync) return;
|
||||
struct DB_SYNC* db = conn->instance->db_sync;
|
||||
uint64_t peer_id = conn->peer_node_id;
|
||||
if (peer_id == 0) return;
|
||||
struct DB_SYNC_PEER* p = db_peer_find(db, peer_id);
|
||||
if (p) p->synced = 0;
|
||||
int any_up = 0;
|
||||
for (int i = 0; i < db->peer_count; i++) { if (db->peers[i].synced >= 1) { any_up = 1; break; } }
|
||||
if (!any_up) db->last_connected_tb = 0;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: peer down %016llx", (unsigned long long)peer_id);
|
||||
}
|
||||
|
||||
// ---- Initiate sync ----
|
||||
static void db_sync_initiate_sync(struct DB_SYNC* db, uint64_t peer_node_id) {
|
||||
uint32_t my_count = db_count(db);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: initiate_sync to %016llx my_count=%u", (unsigned long long)peer_node_id, my_count);
|
||||
uint8_t msg[37]; msg[0] = DB_MSG_INIT_SYNC;
|
||||
memcpy(msg+1, &my_count, 4);
|
||||
if (my_count > 0) {
|
||||
if (db_chain_hash_at(db, my_count-1, msg+5) != 0) memset(msg+5, 0, 32);
|
||||
} else {
|
||||
memset(msg+5, 0, 32);
|
||||
}
|
||||
db_sync_send(db, peer_node_id, msg, 37);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: INIT_SYNC → %016llx my_count=%u", (unsigned long long)peer_node_id, my_count);
|
||||
}
|
||||
|
||||
// ---- Periodic timers ----
|
||||
static void db_sync_peer_check_cb(void* arg) {
|
||||
struct DB_SYNC* db = (struct DB_SYNC*)arg;
|
||||
if (!db || !db->enabled) return;
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(db->inst->topo_groups);
|
||||
if (!group) { db->peer_check_timer = uasync_set_timeout(db->inst->ua, DB_SYNC_PEER_CHECK_INTERVAL * 10000u, db, db_sync_peer_check_cb, "db_sync_peer"); return; }
|
||||
|
||||
// Check all known peers with active connections
|
||||
struct ll_entry* e = group->senders_list->head;
|
||||
while (e) {
|
||||
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)e->data;
|
||||
if (item->conn && item->conn->peer_node_id != 0 && item->conn->links_up) {
|
||||
uint64_t pid = item->conn->peer_node_id;
|
||||
if (pid != db->inst->node_id) {
|
||||
struct DB_SYNC_PEER* p = db_peer_add(db, pid);
|
||||
if (p && p->synced == 0) { p->synced = 1; db_sync_initiate_sync(db, pid); }
|
||||
}
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
db->peer_check_timer = uasync_set_timeout(db->inst->ua, DB_SYNC_PEER_CHECK_INTERVAL * 10000u, db, db_sync_peer_check_cb, "db_sync_peer");
|
||||
}
|
||||
|
||||
static void db_sync_ttl_cleanup_cb(void* arg) {
|
||||
struct DB_SYNC* db = (struct DB_SYNC*)arg;
|
||||
if (!db || !db->enabled || !db->env) { if (db) db->ttl_timer = uasync_set_timeout(db->inst->ua, DB_SYNC_TTL_INTERVAL * 10000u, db, db_sync_ttl_cleanup_cb, "db_sync_ttl"); return; }
|
||||
uint64_t my_id = db->inst->node_id;
|
||||
uint64_t now_us = get_time_us();
|
||||
uint64_t ttl_us = (uint64_t)(db->inst->config->global.db_sync_ttl) * 1000000uLL;
|
||||
uint64_t cutoff = now_us - ttl_us;
|
||||
|
||||
MDB_txn* txn; int rc = mdb_txn_begin(db->env, NULL, 0, &txn);
|
||||
if (rc != 0) { db->ttl_timer = uasync_set_timeout(db->inst->ua, DB_SYNC_TTL_INTERVAL * 10000u, db, db_sync_ttl_cleanup_cb, "db_sync_ttl"); return; }
|
||||
|
||||
MDB_cursor* cursor; rc = mdb_cursor_open(txn, db->dbi_records, &cursor);
|
||||
if (rc == 0) {
|
||||
MDB_val key, val; rc = mdb_cursor_get(cursor, &key, &val, MDB_FIRST);
|
||||
while (rc == 0) {
|
||||
if (val.mv_size >= DB_VAL_HDR_SIZE) {
|
||||
uint64_t creator = *(uint64_t*)((uint8_t*)val.mv_data + DB_VAL_OFF_CREATOR);
|
||||
uint8_t flags = *(uint8_t*)((uint8_t*)val.mv_data + DB_VAL_OFF_FLAGS);
|
||||
uint64_t ts = be64toh(*(uint64_t*)key.mv_data);
|
||||
if (creator == my_id && !(flags & DB_REC_FLAG_WAS_SENT) && ts < cutoff) {
|
||||
mdb_cursor_del(cursor, 0);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: TTL deleted unsent record ts=%llu dh=%016llx", (unsigned long long)ts, (unsigned long long)be64toh(*(uint64_t*)((uint8_t*)key.mv_data+8)));
|
||||
}
|
||||
}
|
||||
rc = mdb_cursor_get(cursor, &key, &val, MDB_NEXT);
|
||||
}
|
||||
mdb_cursor_close(cursor);
|
||||
}
|
||||
mdb_txn_commit(txn);
|
||||
db->ttl_timer = uasync_set_timeout(db->inst->ua, DB_SYNC_TTL_INTERVAL * 10000u, db, db_sync_ttl_cleanup_cb, "db_sync_ttl");
|
||||
}
|
||||
|
||||
// ---- Public API ----
|
||||
int db_sync_init(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "NULL instance"); return -1; }
|
||||
struct DB_SYNC* db = u_calloc(1, sizeof(struct DB_SYNC));
|
||||
if (!db) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "u_calloc failed"); return -1; }
|
||||
db->inst = inst; db->last_connected_tb = 0; db->last_timestamp_us = 0;
|
||||
if (!inst->config->global.db_sync_enabled) {
|
||||
db->enabled = 0; inst->db_sync = db;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: disabled by config");
|
||||
return 0;
|
||||
}
|
||||
db->enabled = 1;
|
||||
inst->db_sync = db;
|
||||
|
||||
// Open LMDB
|
||||
const char* db_path = inst->config->global.db_path;
|
||||
char sync_path[512];
|
||||
if (db_path[0]) {
|
||||
snprintf(sync_path, sizeof(sync_path), "%s/sync", db_path);
|
||||
} else {
|
||||
snprintf(sync_path, sizeof(sync_path), "/tmp/utun_db_sync");
|
||||
}
|
||||
if (db_lmdb_open(db, sync_path) != 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DB_SYNC, "LMDB open failed, sync disabled");
|
||||
db->enabled = 0;
|
||||
}
|
||||
|
||||
// Register in etcp_router
|
||||
etcp_router_bind(inst, ETCP_RT_ID_DB_SYNC, db_sync_recv_cb);
|
||||
|
||||
// Subscribe to ETCP connection events
|
||||
etcp_add_new_conn_cbk(inst, db_sync_on_new_conn, NULL);
|
||||
|
||||
// For existing connections: add up/down callbacks
|
||||
{
|
||||
struct ETCP_CONN* conn = inst->connections;
|
||||
while (conn) {
|
||||
etcp_conn_add_up_cbk(conn, db_sync_on_conn_up, NULL);
|
||||
etcp_conn_add_down_cbk(conn, db_sync_on_conn_down, NULL);
|
||||
conn = conn->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Start periodic timers
|
||||
db->peer_check_timer = uasync_set_timeout(inst->ua, DB_SYNC_PEER_CHECK_INTERVAL * 10000u, db, db_sync_peer_check_cb, "db_sync_peer");
|
||||
db->ttl_timer = uasync_set_timeout(inst->ua, DB_SYNC_TTL_INTERVAL * 10000u, db, db_sync_ttl_cleanup_cb, "db_sync_ttl");
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: initialized (enabled=%d)", db->enabled);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void db_sync_destroy(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst || !inst->db_sync) return;
|
||||
struct DB_SYNC* db = inst->db_sync;
|
||||
inst->db_sync = NULL;
|
||||
|
||||
etcp_router_unbind(inst, ETCP_RT_ID_DB_SYNC);
|
||||
|
||||
if (db->peer_check_timer) { uasync_cancel_timeout(inst->ua, db->peer_check_timer); db->peer_check_timer = NULL; }
|
||||
if (db->ttl_timer) { uasync_cancel_timeout(inst->ua, db->ttl_timer); db->ttl_timer = NULL; }
|
||||
|
||||
// Remove up/down callbacks from existing connections
|
||||
{ struct ETCP_CONN* conn = inst->connections; while (conn) { etcp_conn_remove_up_cbk(conn, db_sync_on_conn_up, NULL); etcp_conn_remove_down_cbk(conn, db_sync_on_conn_down, NULL); conn = conn->next; } }
|
||||
|
||||
db_lmdb_close(db);
|
||||
if (db->peers) u_free(db->peers);
|
||||
u_free(db);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: destroyed");
|
||||
}
|
||||
|
||||
int db_sync_insert_len(struct UTUN_INSTANCE* inst, const char* json_data, size_t len) {
|
||||
if (!inst || !inst->db_sync || !json_data || len == 0) { DEBUG_ERROR(DEBUG_CATEGORY_DB_SYNC, "invalid args"); return -1; }
|
||||
struct DB_SYNC* db = inst->db_sync;
|
||||
if (!db->enabled) return -1;
|
||||
|
||||
// Compute datahash and timestamp
|
||||
uint64_t datahash = db_datahash((const uint8_t*)json_data, len);
|
||||
uint64_t now_us = get_time_us();
|
||||
// Guarantee monotonicity
|
||||
if (now_us <= db->last_timestamp_us) now_us = db->last_timestamp_us + 1;
|
||||
db->last_timestamp_us = now_us;
|
||||
uint64_t timestamp = now_us;
|
||||
uint64_t id = db->next_id;
|
||||
|
||||
int ret = db_record_insert(db, id, timestamp, datahash, json_data, len);
|
||||
if (ret != 0) return ret; // 1 = duplicate, -1 = error
|
||||
|
||||
// Broadcast PUSH to all synced peers
|
||||
uint8_t pbuf[2048];
|
||||
uint32_t off = 0; pbuf[off++] = DB_MSG_PUSH;
|
||||
memcpy(pbuf+off, &id, 8); off += 8;
|
||||
memcpy(pbuf+off, ×tamp, 8); off += 8;
|
||||
memcpy(pbuf+off, &datahash, 8); off += 8;
|
||||
memcpy(pbuf+off, &len, 4); off += 4;
|
||||
if (len > 0 && off + len <= sizeof(pbuf)) { memcpy(pbuf+off, json_data, len); off += len; }
|
||||
for (int i = 0; i < db->peer_count; i++) if (db->peers[i].synced >= 1 && db->peers[i].node_id != inst->node_id) db_sync_send(db, db->peers[i].node_id, pbuf, off);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DB_SYNC, "db_sync: insert id=%llu dh=%016llx ts=%llu len=%zu", (unsigned long long)id, (unsigned long long)datahash, (unsigned long long)timestamp, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_sync_insert(struct UTUN_INSTANCE* inst, const char* json_data) {
|
||||
if (!json_data) return -1;
|
||||
return db_sync_insert_len(inst, json_data, strlen(json_data));
|
||||
}
|
||||
|
||||
uint32_t db_sync_count(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst || !inst->db_sync) return 0;
|
||||
return db_count(inst->db_sync);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// db_sync.h — Distributed content-addressed table with LMDB storage and peer sync via etcp_router
|
||||
#ifndef DB_SYNC_H
|
||||
#define DB_SYNC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
|
||||
// etcp_router service ID
|
||||
#define ETCP_RT_ID_DB_SYNC 0x20
|
||||
|
||||
// Message types
|
||||
#define DB_MSG_INIT_SYNC 0x01
|
||||
#define DB_MSG_INIT_RESP 0x02
|
||||
#define DB_MSG_REFINE 0x03
|
||||
#define DB_MSG_SEND_DATA 0x04
|
||||
#define DB_MSG_PUSH 0x05
|
||||
#define DB_MSG_ACK_PUSH 0x06
|
||||
#define DB_MSG_SYNC_DONE 0x07
|
||||
|
||||
// Defaults
|
||||
#define DB_SYNC_DEFAULT_TTL 86400
|
||||
#define DB_SYNC_DEFAULT_MAPSIZE (100UL * 1024 * 1024)
|
||||
#define DB_SYNC_PEER_CHECK_INTERVAL 5
|
||||
#define DB_SYNC_TTL_INTERVAL 3600
|
||||
|
||||
// Record flags
|
||||
#define DB_REC_FLAG_WAS_SENT 0x01
|
||||
|
||||
// Sync protocol constants
|
||||
#define DB_REFINE_HASHES 16
|
||||
#define DB_SEND_DATA_MAX 32
|
||||
|
||||
int db_sync_init(struct UTUN_INSTANCE* inst);
|
||||
void db_sync_destroy(struct UTUN_INSTANCE* inst);
|
||||
int db_sync_insert(struct UTUN_INSTANCE* inst, const char* json_data);
|
||||
int db_sync_insert_len(struct UTUN_INSTANCE* inst, const char* json_data, size_t len);
|
||||
uint32_t db_sync_count(struct UTUN_INSTANCE* inst);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // DB_SYNC_H
|
||||
Binary file not shown.
@@ -21,6 +21,11 @@
|
||||
#ifndef DUMMYNET_H
|
||||
#define DUMMYNET_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -179,4 +184,8 @@ const struct dummynet_stats* dummynet_filter_get_stats(struct dummynet_filter* d
|
||||
void dummynet_filter_block_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
|
||||
void dummynet_filter_unblock_addr(struct dummynet_filter* df, uint32_t ip, uint16_t port);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // DUMMYNET_H
|
||||
@@ -1,6 +1,11 @@
|
||||
#ifndef EIM_NAT_H
|
||||
#define EIM_NAT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -51,4 +56,8 @@ int eim_nat_add_forward(struct eim_nat_ctx* ctx, uint8_t proto,
|
||||
uint32_t internal_ip_host, uint16_t internal_port_net,
|
||||
uint16_t external_port);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+281
-30
@@ -3,8 +3,9 @@
|
||||
#include "etcp.h"
|
||||
#include "etcp_debug.h"
|
||||
#include "etcp_loadbalancer.h"
|
||||
#include "etcp_router.h"
|
||||
#include "routing.h"
|
||||
#include "route_bgp.h"
|
||||
#include "topo_group.h"
|
||||
#include "route_ping.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
@@ -15,6 +16,7 @@
|
||||
#include <sys/time.h>
|
||||
#include <math.h> // For bandwidth calcs
|
||||
#include <limits.h> // For UINT16_MAX
|
||||
#include <time.h> // For strftime in metrics snapshot
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/memory_pool.h"
|
||||
|
||||
@@ -70,7 +72,7 @@ static int inflight_seq_cmp(const void* a, const void* b) {
|
||||
|
||||
static void feed_dgram_to_asm(struct ETCP_CONN* etcp, struct PKTNORM* pn,
|
||||
uint8_t* dgram, uint16_t dgram_len,
|
||||
uint8_t** asm_buf, uint16_t* asm_len, uint16_t* asm_cap,
|
||||
uint8_t** asm_buf, uint16_t* asm_len, uint32_t* asm_cap,
|
||||
uint32_t* returned) {
|
||||
uint32_t need = (uint32_t)*asm_len + dgram_len;
|
||||
if (need >= ASM_BUF_MAX_SIZE) {
|
||||
@@ -78,9 +80,10 @@ static void feed_dgram_to_asm(struct ETCP_CONN* etcp, struct PKTNORM* pn,
|
||||
return;
|
||||
}
|
||||
if (need > *asm_cap) {
|
||||
uint16_t new_cap = *asm_cap ? *asm_cap : 256;
|
||||
uint32_t new_cap = *asm_cap ? *asm_cap : 256;
|
||||
while (new_cap < need) new_cap *= 2;
|
||||
uint8_t* new_buf = u_realloc(*asm_buf, new_cap);
|
||||
if (new_cap > 0xFFFF) new_cap = 0xFFFF;
|
||||
uint8_t* new_buf = u_realloc(*asm_buf, (uint16_t)new_cap);
|
||||
if (!new_buf) return;
|
||||
*asm_buf = new_buf;
|
||||
*asm_cap = new_cap;
|
||||
@@ -93,7 +96,7 @@ static void feed_dgram_to_asm(struct ETCP_CONN* etcp, struct PKTNORM* pn,
|
||||
if (pkt_len == 0 || pkt_len > 16384 || *asm_len < 2 + pkt_len) break;
|
||||
|
||||
const uint8_t* pkt_data = *asm_buf + 2;
|
||||
if (pkt_data[0] != ETCP_ID_ROUTE_ENTRY) {
|
||||
if (pkt_data[0] != ETCP_ID_TOPO_ENTRY) {
|
||||
struct ll_entry* e = ll_alloc_lldgram(pkt_len);
|
||||
if (e) {
|
||||
memcpy(e->dgram, pkt_data, pkt_len);
|
||||
@@ -138,7 +141,7 @@ static void etcp_return_inflight_to_normalizer(struct ETCP_CONN* etcp) {
|
||||
|
||||
uint8_t* asm_buf = NULL;
|
||||
uint16_t asm_len = 0;
|
||||
uint16_t asm_cap = 0;
|
||||
uint32_t asm_cap = 0;
|
||||
uint32_t returned_packets = 0;
|
||||
|
||||
for (int i = 0; i < inflight_count; i++) {
|
||||
@@ -193,7 +196,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
|
||||
|
||||
struct ETCP_CONN* etcp = u_calloc(1, sizeof(struct ETCP_CONN));
|
||||
if (!etcp) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connection_create: creating connection failed for instance %p", instance);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "creating connection failed for instance %p", instance);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -201,6 +204,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
|
||||
etcp->input_queue = queue_new(instance->ua, 0, 0, 0, "ETCP input"); // No hash for input_queue
|
||||
queue_set_threshold(etcp->input_queue, 0, 0); // Backpressure: ждать полного освобождения
|
||||
etcp->output_queue = queue_new(instance->ua, 0, 0, 0, "ETCP output"); // No hash for output_queue
|
||||
etcp->transit_queues = queue_new(instance->ua, TRANSIT_QUEUE_HASH, 0, 16, "transit_q_reg"); // Hash for transit queues
|
||||
etcp->input_send_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, 0, 4, "input_send_q"); // Hash for send_q
|
||||
etcp->input_wait_ack = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, 0, 4, "input_wait_ack"); // Hash for wait_ack
|
||||
etcp->recv_q = queue_new(instance->ua, INFLIGHT_INITIAL_HASH_SIZE, 0, 4, "recv_q"); // Hash for recv_q
|
||||
@@ -216,9 +220,10 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
|
||||
// Initialize log_name with local node_id (peer will be updated later when known)
|
||||
snprintf(etcp->log_name, sizeof(etcp->log_name), "%04X->???? [%s]", (uint16_t)instance->node_id, etcp->name);
|
||||
|
||||
if (!etcp->input_queue || !etcp->output_queue || !etcp->input_send_q || !etcp->recv_q || !etcp->ack_q ||
|
||||
if (!etcp->input_queue || !etcp->output_queue || !etcp->transit_queues ||
|
||||
!etcp->input_send_q || !etcp->recv_q || !etcp->ack_q ||
|
||||
!etcp->input_wait_ack || !etcp->inflight_pool || !etcp->io_pool) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connection_create: error - closing - input:%p output:%p send:%p wait:%x pool:%p",
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "error - closing - input:%p output:%p send:%p wait:%x pool:%p",
|
||||
etcp->input_queue, etcp->output_queue, etcp->input_send_q, etcp->input_wait_ack, etcp->inflight_pool);
|
||||
etcp_connection_close(etcp);
|
||||
return NULL;
|
||||
@@ -239,6 +244,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
|
||||
etcp_connection_close(etcp);
|
||||
return NULL;
|
||||
}
|
||||
etcp->send_input_q = etcp->normalizer->input;
|
||||
|
||||
// Set input queue callback
|
||||
queue_set_callback(etcp->input_queue, input_queue_cb, etcp);
|
||||
@@ -252,8 +258,9 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
|
||||
etcp->log_name, etcp, etcp->mtu, etcp->next_tx_id);
|
||||
|
||||
// Вызываем callback для нового соединения если установлен
|
||||
if (instance && instance->etcp_new_conn_cbk) {
|
||||
instance->etcp_new_conn_cbk(etcp, instance->etcp_new_conn_arg);
|
||||
if (instance) {
|
||||
struct etcp_cbk_entry* cbe = instance->new_conn_cbks;
|
||||
while (cbe) { cbe->fn(etcp, cbe->arg); cbe = cbe->next; }
|
||||
}
|
||||
|
||||
return etcp;
|
||||
@@ -261,14 +268,15 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n
|
||||
|
||||
|
||||
static void etcp_on_up(struct ETCP_CONN* etcp) {
|
||||
// if (!etcp->initialized) return;
|
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "[%s] initialized=%d", etcp->log_name, etcp->initialized);
|
||||
if (etcp->up_cbk) etcp->up_cbk(etcp, etcp->up_arg);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "[%s] UP links_up=%d initialized=%d", etcp->log_name, etcp->links_up, etcp->initialized);
|
||||
struct etcp_cbk_entry* cbe = etcp->up_cbks;
|
||||
while (cbe) { cbe->fn(etcp, cbe->arg); cbe = cbe->next; }
|
||||
}
|
||||
|
||||
static void etcp_on_down(struct ETCP_CONN* etcp) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "[%s]", etcp->log_name);
|
||||
if (etcp->down_cbk) etcp->down_cbk(etcp, etcp->down_arg);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_BGP, "[%s] DOWN links_up=%d", etcp->log_name, etcp->links_up);
|
||||
struct etcp_cbk_entry* cbe = etcp->down_cbks;
|
||||
while (cbe) { cbe->fn(etcp, cbe->arg); cbe = cbe->next; }
|
||||
}
|
||||
|
||||
|
||||
@@ -291,12 +299,16 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
|
||||
uasync_cancel_timeout(etcp->instance->ua, etcp->ack_resp_timer);
|
||||
etcp->ack_resp_timer = NULL;
|
||||
}
|
||||
etcp_metrics_stop_timer(etcp);
|
||||
|
||||
routing_del_conn(etcp);
|
||||
|
||||
// NOTE: route_bgp_remove_conn is already called via down_cbk in etcp_on_down() above
|
||||
// NOTE: topo_bgp_remove_conn is already called via down_cbk in etcp_on_down() above
|
||||
// DO NOT call it again here to avoid double-processing and ref_count corruption
|
||||
|
||||
// Очистить транзитные очереди (до pn_deinit/stcp_link_close — waiter'ы на normalizer->input/tx_queue)
|
||||
etcp_router_transit_queues_destroy(etcp);
|
||||
|
||||
// Deinitialize packet normalizer (this will call routing_del_conn)
|
||||
if (etcp->normalizer) {
|
||||
pn_deinit((struct PKTNORM*)etcp->normalizer);
|
||||
@@ -324,6 +336,11 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
|
||||
drain_and_free_queue(&etcp->recv_q);
|
||||
drain_and_free_queue(&etcp->ack_q);
|
||||
|
||||
// Free callback chains
|
||||
{ struct etcp_cbk_entry* cbe = etcp->ready_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->ready_cbks = NULL; }
|
||||
{ struct etcp_cbk_entry* cbe = etcp->up_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->up_cbks = NULL; }
|
||||
{ struct etcp_cbk_entry* cbe = etcp->down_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; u_free(cbe); cbe = n; } etcp->down_cbks = NULL; }
|
||||
|
||||
// Free memory pools after all elements are returned
|
||||
if (etcp->inflight_pool) {
|
||||
memory_pool_destroy(etcp->inflight_pool);
|
||||
@@ -337,6 +354,14 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
|
||||
|
||||
u_free(etcp->name);
|
||||
|
||||
{ struct UTUN_INSTANCE* inst = etcp->instance;
|
||||
if (inst && inst->connections) {
|
||||
struct ETCP_CONN** pp = &inst->connections;
|
||||
while (*pp && *pp != etcp) pp = &(*pp)->next;
|
||||
if (*pp) { *pp = etcp->next; if (inst->connections_count) inst->connections_count--; }
|
||||
}
|
||||
}
|
||||
|
||||
// Clear next pointer to prevent dangling references
|
||||
etcp->next = NULL;
|
||||
|
||||
@@ -374,7 +399,10 @@ void etcp_conn_reset(struct ETCP_CONN* etcp) {
|
||||
etcp->last_rr_link = NULL;
|
||||
|
||||
// Return unconfirmed inflight data to normalizer input (до нормалайзера)
|
||||
if (etcp->normalizer) etcp_return_inflight_to_normalizer(etcp);
|
||||
if (etcp->normalizer) {
|
||||
etcp->normalizer->input->callback_suspended = 1;
|
||||
etcp_return_inflight_to_normalizer(etcp);
|
||||
}
|
||||
|
||||
// Clear queues (keep queue structures)
|
||||
clear_queue(etcp->input_queue);
|
||||
@@ -478,8 +506,8 @@ void etcp_conn_reinit(struct ETCP_CONN* etcp) {// Если сбой в обме
|
||||
etcp->initialized = 0;// еще раз придёт conn_ready_callback
|
||||
|
||||
// Отменяем висящие NAT-ping'и для этого соединения
|
||||
if (etcp->instance && etcp->instance->bgp) {
|
||||
route_ping_cancel_for_conn(etcp->instance->bgp, etcp);
|
||||
if (etcp->instance && etcp->instance->topo_groups) {
|
||||
route_ping_cancel_for_conn(topo_groups_get_default(etcp->instance->topo_groups), etcp);
|
||||
}
|
||||
|
||||
// Сбрасываем NAT-статус у всех линков этого соединения
|
||||
@@ -507,8 +535,10 @@ void etcp_conn_ready(struct ETCP_CONN* conn) {
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] Connection ready", conn->log_name);
|
||||
|
||||
etcp_metrics_start_timer(conn);
|
||||
|
||||
// Вызываем callback если установлен
|
||||
if (conn->ready_cbk) conn->ready_cbk(conn, conn->ready_arg);
|
||||
{ struct etcp_cbk_entry* cbe = conn->ready_cbks; while (cbe) { cbe->fn(conn, cbe->arg); cbe = cbe->next; } }
|
||||
if (conn->links_up) etcp_on_up(conn);
|
||||
}
|
||||
|
||||
@@ -722,7 +752,7 @@ static void input_queue_cb(struct ll_queue* q, void* arg) {
|
||||
}
|
||||
etcp->unacked_bytes += len;
|
||||
|
||||
// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "input_queue_cb: successfully moved from input_queue to input_send_q");
|
||||
// DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "successfully moved from input_queue to input_send_q");
|
||||
|
||||
// etcp_conn_process_send_queue(etcp);// сразу обработаем этот пакет
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] nextloop, input_queue size=%d ", etcp->log_name, q->count);
|
||||
@@ -762,6 +792,7 @@ static void ack_timeout_check(struct ETCP_CONN* etcp) {
|
||||
|
||||
// Update stats
|
||||
etcp->retransmissions_count++;
|
||||
etcp_metrics_add_loss(etcp, 1);
|
||||
}
|
||||
else {// не надо до конца сканировать - они уже сортированы по таймстемпу т.к. очередь fifo, а timestamp = время добавления в очередь = время отправки
|
||||
// shedule timer
|
||||
@@ -958,6 +989,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
|
||||
inf_pkt->state=INFLIGHT_STATE_WAIT_ACK;
|
||||
|
||||
queue_data_put_with_index(etcp->input_wait_ack, &inf_pkt->ll);// move dgram to wait_ack queue
|
||||
etcp_metrics_add_sent(etcp, inf_pkt->ll.len);
|
||||
}
|
||||
size_t ack_q_size = queue_entry_count(etcp->ack_q);
|
||||
|
||||
@@ -1098,7 +1130,7 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
|
||||
}
|
||||
}
|
||||
// фрейм data (0) обязательно в конец
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] TX DATA: seq=%u len=%u retry=%d; Qlen: in=%d snd=%d wait=%d", etcp->log_name, inf_pkt->seq, inf_pkt->ll.len, inf_pkt->send_count, etcp->input_queue->count, etcp->input_send_q->count, etcp->input_wait_ack->count);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TX DATA: seq=%u len=%u retry=%d; Qlen: in=%d snd=%d wait=%d", etcp->log_name, inf_pkt->seq, inf_pkt->ll.len, inf_pkt->send_count, etcp->input_queue->count, etcp->input_send_q->count, etcp->input_wait_ack->count);
|
||||
|
||||
dgram->data[ptr++]=0;// payload
|
||||
dgram->data[ptr++]=inf_pkt->seq;
|
||||
@@ -1178,8 +1210,9 @@ void etcp_output_try_assembly(struct ETCP_CONN* etcp) {
|
||||
// Add to output_queue using the same ETCP_FRAGMENT structure
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "[%s] moving packet id=%u to output_queue (qlen=%d)", etcp->log_name,
|
||||
next_expected_id, etcp->output_queue->count);
|
||||
uint16_t pkt_len = rx_pkt->ll.len;
|
||||
if (queue_data_put(etcp->output_queue, (struct ll_entry*)rx_pkt) == 0) {
|
||||
delivered_bytes += rx_pkt->ll.len;
|
||||
delivered_bytes += pkt_len;
|
||||
delivered_count++;
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] failed to add packet id=%u to output_queue", etcp->log_name,
|
||||
@@ -1223,7 +1256,7 @@ void etcp_ack_recv(struct ETCP_CONN* etcp, uint32_t seq, uint16_t ts, uint16_t d
|
||||
|
||||
if (!acked_pkt) {
|
||||
// Packet might be already acknowledged or not found
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "etcp_ack_recv: packet seq=%u not found in wait_ack queue", seq);
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "packet seq=%u not found in wait_ack queue", seq);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1302,7 +1335,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "");
|
||||
if (!pkt) return;
|
||||
if (memory_pool_is_freed(pkt->link->etcp->instance->pkt_pool, pkt)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_conn_input: pkt=%p ALREADY FREED in pkt_pool — HALTING", (void*)pkt);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pkt=%p ALREADY FREED in pkt_pool — HALTING", (void*)pkt);
|
||||
volatile int _halt = 1; while (_halt) {}
|
||||
}
|
||||
if (!pkt->data_len) {
|
||||
@@ -1355,6 +1388,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
|
||||
uint16_t ret_ts=data[1] | (data[2]<<8);// cur_ts=ret_ts = RTT
|
||||
uint16_t new_rtt=cur_ts-ret_ts;
|
||||
pkt->link->rtt_last=new_rtt;
|
||||
etcp_metrics_add_rtt(etcp, new_rtt);
|
||||
|
||||
int recv_dt_tx1=data[3] | (data[4]<<8);// localtime удаленной стороны момента принятия пакета - timestamp этого пакета (на стороне отправителя, т.е. у нас)
|
||||
|
||||
@@ -1400,7 +1434,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
|
||||
// формируем ACK
|
||||
uint32_t seq=data[1] | (data[2]<<8) | (data[3]<<16) | (data[4]<<24);
|
||||
if (etcp->got_initial_pkt == 0) {
|
||||
if (seq==1) etcp->got_initial_pkt = 1;
|
||||
if (seq==1) { etcp->got_initial_pkt = 1; DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] Initial packet seq=1 received", etcp->log_name); }
|
||||
else {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "[%s] Waiting for initial packet but recv seq=%d, ignoring", etcp->log_name, seq);
|
||||
len=0;
|
||||
@@ -1474,6 +1508,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
|
||||
memcpy(payload_data, data + 5, pkt_len);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] RX seq=%u need=%u asm_len=%d", etcp->log_name, seq, etcp->last_delivered_id+1, etcp->recv_q->count);
|
||||
queue_data_put_with_index(etcp->recv_q, (struct ll_entry*)rx_pkt);
|
||||
etcp_metrics_add_rcvd(etcp, pkt_len);
|
||||
if ((int32_t)(seq - etcp->last_delivered_id) == 1) etcp_output_try_assembly(etcp);// пробуем собрать выходную очередь из фрагментов
|
||||
} else {
|
||||
etcp->rx_dup_count++;
|
||||
@@ -1499,7 +1534,10 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
|
||||
link->burst_recv_received = 0;
|
||||
}
|
||||
if (link->burst_recv_valid) {
|
||||
if (seq != link->burst_recv_next_seq) link->burst_recv_valid = 0;
|
||||
if (seq != link->burst_recv_next_seq) {
|
||||
if (seq > link->burst_recv_next_seq) etcp_metrics_add_loss(etcp, seq - link->burst_recv_next_seq);
|
||||
link->burst_recv_valid = 0;
|
||||
}
|
||||
if (seq < 16) {
|
||||
link->burst_recv_times[seq] = get_time_us();
|
||||
link->burst_recv_next_seq = seq + 1;
|
||||
@@ -1566,8 +1604,46 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
|
||||
break;
|
||||
}
|
||||
|
||||
case ETCP_SECTION_METRICS: {
|
||||
if (len < 2 + SC_SIGN_SIZE) { len = 0; break; }
|
||||
uint16_t csv_len = data[1] | (data[2] << 8);
|
||||
if ((uint32_t)(2 + SC_SIGN_SIZE + csv_len) > len) { len = 0; break; }
|
||||
uint8_t* sig = data + 2;
|
||||
uint8_t* csv = data + 2 + SC_SIGN_SIZE;
|
||||
uint8_t* peer_pubkey = pkt->link->remote_ed25519_pubkey;
|
||||
struct sc_stream_sign_state verify_state;
|
||||
int sig_ok = (sc_stream_sign_verify_init(&verify_state, peer_pubkey) == SC_OK
|
||||
&& sc_stream_sign_update(&verify_state, csv, csv_len) == SC_OK
|
||||
&& sc_stream_sign_verify(&verify_state, sig, SC_SIGN_SIZE) == SC_OK);
|
||||
if (!sig_ok && verify_state.initialized) /* update failed, then verify didn't run */
|
||||
sc_stream_sign_cleanup(&verify_state);
|
||||
if (sig_ok) {
|
||||
const char* stats_dir = etcp->instance->stats_dir;
|
||||
if (stats_dir[0] && etcp->peer_node_id != 0) {
|
||||
char path[768];
|
||||
snprintf(path, sizeof(path), "%s/from_%016llx.dat", stats_dir, (unsigned long long)etcp->peer_node_id);
|
||||
FILE* f = fopen(path, "a");
|
||||
if (f) {
|
||||
fprintf(f, "%.*s,", csv_len, csv);
|
||||
for (int i = 0; i < SC_SIGN_SIZE; i++) fprintf(f, "%02x", sig[i]);
|
||||
fprintf(f, "\n");
|
||||
fclose(f);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] Received metrics from peer (%u bytes)", etcp->log_name, csv_len);
|
||||
if (etcp->instance->api_bindings.on_metrics_rcvd)
|
||||
etcp->instance->api_bindings.on_metrics_rcvd(etcp->instance->api_bindings.metrics_user_ptr, etcp, csv, csv_len, sig);
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] Cannot open from_metrics file: %s", etcp->log_name, path);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "[%s] Metrics signature verification FAILED", etcp->log_name);
|
||||
}
|
||||
data += 2 + SC_SIGN_SIZE + csv_len; len -= 2 + SC_SIGN_SIZE + csv_len;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "etcp_conn_input: unknown section type=0x%02x", type);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "unknown section type=0x%02x", type);
|
||||
len=0;
|
||||
break;
|
||||
}
|
||||
@@ -1575,7 +1651,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) {
|
||||
}
|
||||
|
||||
if (memory_pool_is_freed(pkt->link->etcp->instance->pkt_pool, pkt)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_conn_input: pkt=%p ALREADY FREED in pkt_pool — HALTING", (void*)pkt);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pkt=%p ALREADY FREED in pkt_pool — HALTING", (void*)pkt);
|
||||
volatile int _halt = 1; while (_halt) {}
|
||||
}
|
||||
|
||||
@@ -1600,3 +1676,178 @@ void etcp_update_mtu(struct ETCP_CONN* etcp) {
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================== Метрики: гистограммы RTT/потерь + тотальные счётчики
|
||||
|
||||
static const uint16_t etcp_metrics_rtt_bounds[] = { 10, 20, 30, 50, 80, 130, 210, 340, 550, 900, 1500 };
|
||||
#define ETCP_METRICS_RTT_BOUNDS_COUNT (sizeof(etcp_metrics_rtt_bounds) / sizeof(etcp_metrics_rtt_bounds[0]))
|
||||
|
||||
void etcp_metrics_init(struct etcp_metrics* m) {
|
||||
if (!m) return;
|
||||
memset(m, 0, sizeof(*m));
|
||||
}
|
||||
|
||||
void etcp_metrics_add_rtt(struct ETCP_CONN* etcp, uint16_t rtt_tb) {
|
||||
if (!etcp) return;
|
||||
struct etcp_metrics* m = &etcp->metrics;
|
||||
int i;
|
||||
for (i = 0; i < (int)ETCP_METRICS_RTT_BOUNDS_COUNT; i++) {
|
||||
if (rtt_tb < etcp_metrics_rtt_bounds[i]) break;
|
||||
}
|
||||
if (i >= ETCP_METRICS_RTT_BUCKETS) i = ETCP_METRICS_RTT_BUCKETS - 1;
|
||||
m->rtt_hist[i]++;
|
||||
m->work_samples++;
|
||||
}
|
||||
|
||||
void etcp_metrics_add_sent(struct ETCP_CONN* etcp, uint32_t len) {
|
||||
if (!etcp) return;
|
||||
struct etcp_metrics* m = &etcp->metrics;
|
||||
m->work_sent++;
|
||||
m->work_bytes_sent += len;
|
||||
m->total_sent++;
|
||||
m->total_bytes_sent += len;
|
||||
}
|
||||
|
||||
void etcp_metrics_add_rcvd(struct ETCP_CONN* etcp, uint32_t len) {
|
||||
if (!etcp) return;
|
||||
struct etcp_metrics* m = &etcp->metrics;
|
||||
m->work_rcvd++;
|
||||
m->work_bytes_rcvd += len;
|
||||
m->total_rcvd++;
|
||||
m->total_bytes_rcvd += len;
|
||||
}
|
||||
|
||||
void etcp_metrics_add_loss(struct ETCP_CONN* etcp, uint32_t count) {
|
||||
if (!etcp) return;
|
||||
struct etcp_metrics* m = &etcp->metrics;
|
||||
m->work_lost += count;
|
||||
m->total_lost += count;
|
||||
}
|
||||
|
||||
static void etcp_metrics_do_snapshot(struct ETCP_CONN* etcp) {
|
||||
if (!etcp || !etcp->instance) return;
|
||||
struct etcp_metrics* m = &etcp->metrics;
|
||||
|
||||
// Копируем working → final
|
||||
memcpy(m->rtt_hist_final, m->rtt_hist, sizeof(m->rtt_hist_final));
|
||||
memcpy(m->loss_hist_final, m->loss_hist, sizeof(m->loss_hist_final));
|
||||
m->samples_final = m->work_samples;
|
||||
m->sent_final = m->work_sent;
|
||||
m->rcvd_final = m->work_rcvd;
|
||||
m->bytes_sent_final = m->work_bytes_sent;
|
||||
m->bytes_rcvd_final = m->work_bytes_rcvd;
|
||||
m->lost_final = m->work_lost;
|
||||
|
||||
// Рассчитываем loss-гистограмму
|
||||
uint32_t total = m->work_sent + m->work_lost;
|
||||
if (total > 0) {
|
||||
uint32_t loss_pct = (m->work_lost * 100 + total / 2) / total;
|
||||
int bucket = (int)loss_pct;
|
||||
if (bucket >= ETCP_METRICS_LOSS_BUCKETS) bucket = ETCP_METRICS_LOSS_BUCKETS - 1;
|
||||
m->loss_hist[bucket]++;
|
||||
}
|
||||
|
||||
// Обнуляем рабочую копию
|
||||
memset(m->rtt_hist, 0, sizeof(m->rtt_hist));
|
||||
memset(m->loss_hist, 0, sizeof(m->loss_hist));
|
||||
m->work_samples = 0;
|
||||
m->work_sent = 0; m->work_rcvd = 0;
|
||||
m->work_bytes_sent = 0; m->work_bytes_rcvd = 0;
|
||||
m->work_lost = 0;
|
||||
|
||||
m->last_snapshot_tb = get_time_tb();
|
||||
|
||||
// Строим CSV строку
|
||||
char csv_buf[4096];
|
||||
int pos = 0;
|
||||
time_t now = time(NULL);
|
||||
struct tm* tm = localtime(&now);
|
||||
char date_str[16], time_str[16];
|
||||
if (tm) { strftime(date_str, sizeof(date_str), "%Y-%m-%d", tm); strftime(time_str, sizeof(time_str), "%H:%M:%S", tm); }
|
||||
else { strcpy(date_str, "----"); strcpy(time_str, "----"); }
|
||||
pos += snprintf(csv_buf + pos, sizeof(csv_buf) - pos, "%s,%s", date_str, time_str);
|
||||
for (int i = 0; i < ETCP_METRICS_RTT_BUCKETS; i++) pos += snprintf(csv_buf + pos, sizeof(csv_buf) - pos, ",%u", m->rtt_hist_final[i]);
|
||||
for (int i = 0; i < ETCP_METRICS_LOSS_BUCKETS; i++) pos += snprintf(csv_buf + pos, sizeof(csv_buf) - pos, ",%u", m->loss_hist_final[i]);
|
||||
pos += snprintf(csv_buf + pos, sizeof(csv_buf) - pos, ",%u,%u,%u,%u,%u,%u", m->samples_final, m->sent_final, m->rcvd_final, m->bytes_sent_final, m->bytes_rcvd_final, m->lost_final);
|
||||
pos += snprintf(csv_buf + pos, sizeof(csv_buf) - pos, ",%llu,%llu,%llu,%llu,%llu", (unsigned long long)m->total_sent, (unsigned long long)m->total_rcvd, (unsigned long long)m->total_bytes_sent, (unsigned long long)m->total_bytes_rcvd, (unsigned long long)m->total_lost);
|
||||
uint32_t csv_len = (uint32_t)pos;
|
||||
|
||||
// Запись локального файла
|
||||
const char* stats_dir = etcp->instance->stats_dir;
|
||||
if (stats_dir[0] && etcp->peer_node_id != 0) {
|
||||
char path[768];
|
||||
snprintf(path, sizeof(path), "%s/%016llx.dat", stats_dir, (unsigned long long)etcp->peer_node_id);
|
||||
FILE* f = fopen(path, "a");
|
||||
if (f) { fprintf(f, "%s\n", csv_buf); fclose(f); }
|
||||
else DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] Cannot open metrics file: %s", etcp->log_name, path);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] Metrics snapshot: samples=%u sent=%u rcvd=%u lost=%u", etcp->log_name, m->samples_final, m->sent_final, m->rcvd_final, m->lost_final);
|
||||
|
||||
// Подпись Ed25519 и отправка пиру
|
||||
if (etcp->crypto_ctx.initialized && etcp->initialized) {
|
||||
struct sc_stream_sign_state sign_state;
|
||||
if (sc_stream_sign_init(&etcp->crypto_ctx, &sign_state) == SC_OK) {
|
||||
uint8_t sig[SC_SIGN_SIZE];
|
||||
size_t sig_len = sizeof(sig);
|
||||
if (sc_stream_sign_update(&sign_state, (uint8_t*)csv_buf, csv_len) == SC_OK
|
||||
&& sc_stream_sign_final(&sign_state, sig, &sig_len) == SC_OK) {
|
||||
// Построить и отправить дграмму с секцией METRICS
|
||||
struct ETCP_LINK* link = etcp_loadbalancer_select_link(etcp);
|
||||
if (link) {
|
||||
struct ETCP_DGRAM* dgram = memory_pool_alloc(etcp->instance->pkt_pool);
|
||||
if (dgram) {
|
||||
dgram->link = link;
|
||||
dgram->noencrypt_len = 0;
|
||||
dgram->timestamp = get_current_timestamp();
|
||||
int ptr = 0;
|
||||
// Минимальная ACK-секция
|
||||
dgram->data[ptr++] = 1; dgram->data[ptr++] = 0;
|
||||
dgram->data[ptr++] = etcp->last_delivered_id; dgram->data[ptr++] = etcp->last_delivered_id>>8;
|
||||
dgram->data[ptr++] = etcp->last_delivered_id>>16; dgram->data[ptr++] = etcp->last_delivered_id>>24;
|
||||
dgram->data[ptr++] = etcp->rx_dup_count; dgram->data[ptr++] = etcp->rx_dup_count>>8;
|
||||
// METRICS секция
|
||||
dgram->data[ptr++] = ETCP_SECTION_METRICS;
|
||||
uint16_t csv16 = (uint16_t)csv_len;
|
||||
dgram->data[ptr++] = csv16 & 0xFF; dgram->data[ptr++] = (csv16 >> 8) & 0xFF;
|
||||
memcpy(dgram->data + ptr, sig, SC_SIGN_SIZE); ptr += SC_SIGN_SIZE;
|
||||
memcpy(dgram->data + ptr, csv_buf, csv_len); ptr += csv_len;
|
||||
dgram->data_len = ptr;
|
||||
etcp_encrypt_send(dgram);
|
||||
memory_pool_free(etcp->instance->pkt_pool, dgram);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] Metrics sent to peer (%u bytes)", etcp->log_name, csv_len);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sc_stream_sign_cleanup(&sign_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void metrics_snapshot_timer_cb(void* arg) {
|
||||
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
|
||||
if (!etcp) return;
|
||||
etcp->metrics.timer = NULL;
|
||||
|
||||
if (etcp->metrics.work_sent + etcp->metrics.work_rcvd >= ETCP_METRICS_MIN_PACKETS)
|
||||
etcp_metrics_do_snapshot(etcp);
|
||||
|
||||
// Перезапускаем таймер
|
||||
etcp->metrics.timer = uasync_set_timeout(etcp->instance->ua, ETCP_METRICS_INTERVAL_TB, etcp, metrics_snapshot_timer_cb, "etcp_metrics");
|
||||
}
|
||||
|
||||
void etcp_metrics_start_timer(struct ETCP_CONN* etcp) {
|
||||
if (!etcp || etcp->metrics.timer) return;
|
||||
etcp_metrics_init(&etcp->metrics);
|
||||
etcp->metrics.timer = uasync_set_timeout(etcp->instance->ua, ETCP_METRICS_INTERVAL_TB, etcp, metrics_snapshot_timer_cb, "etcp_metrics");
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] Metrics timer started (interval=%ums)", etcp->log_name, ETCP_METRICS_INTERVAL_TB / 10);
|
||||
}
|
||||
|
||||
void etcp_metrics_stop_timer(struct ETCP_CONN* etcp) {
|
||||
if (!etcp) return;
|
||||
if (etcp->metrics.timer && etcp->instance) {
|
||||
uasync_cancel_timeout(etcp->instance->ua, etcp->metrics.timer);
|
||||
etcp->metrics.timer = NULL;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] Metrics timer stopped", etcp->log_name);
|
||||
}
|
||||
}
|
||||
|
||||
+60
-8
@@ -21,6 +21,11 @@ struct stcp_link; // forward declaration
|
||||
struct UTUN_INSTANCE;
|
||||
struct ETCP_CONN;
|
||||
typedef void (*etcp_on_conn_ready)(struct ETCP_CONN* conn, void* arg);
|
||||
struct etcp_cbk_entry {
|
||||
etcp_on_conn_ready fn;
|
||||
void* arg;
|
||||
struct etcp_cbk_entry* next;
|
||||
};
|
||||
struct UASYNC;
|
||||
|
||||
uint16_t get_current_timestamp(void);
|
||||
@@ -32,6 +37,7 @@ uint16_t get_current_timestamp(void);
|
||||
#define ETCP_SECTION_MEAS_TS 0x07 // Measurement timestamp for bandwidth (burst packet)
|
||||
#define ETCP_SECTION_MEAS_RESP 0x08 // Measurement response (burst result)
|
||||
#define ETCP_SECTION_FILLER 0x09 // Filler/dummy data (discarded by receiver)
|
||||
#define ETCP_SECTION_METRICS 0x0A // Metrics CSV exchange (ed25519 signed)
|
||||
|
||||
// Burst measurement constants
|
||||
#define BURST_PACKET_COUNT 12 // число пакетов в burst
|
||||
@@ -55,6 +61,41 @@ uint16_t get_current_timestamp(void);
|
||||
|
||||
// MEAS_RESP valid
|
||||
#define MEAS_RESP_VALID 1
|
||||
// Метрики: гистограммы RTT и потерь с 10-мин снимками
|
||||
#define ETCP_METRICS_RTT_BUCKETS 12 // 0-1,1-2,2-3,3-5,5-8,8-13,13-21,21-34,34-55,55-90,90-150,150+ ms
|
||||
#define ETCP_METRICS_LOSS_BUCKETS 11 // 0%,1%,...,10%+
|
||||
#define ETCP_METRICS_INTERVAL_TB 6000000 // 10 мин в 0.1ms
|
||||
#define ETCP_METRICS_MIN_PACKETS 200 // мин пакетов для обновления гистограмм
|
||||
|
||||
struct etcp_metrics {
|
||||
uint32_t rtt_hist[ETCP_METRICS_RTT_BUCKETS]; // рабочая RTT-гистограмма
|
||||
uint32_t loss_hist[ETCP_METRICS_LOSS_BUCKETS]; // рабочая loss-гистограмма
|
||||
uint32_t work_samples; // RTT замеров в окне
|
||||
uint32_t work_sent; // пакетов отправлено в окне
|
||||
uint32_t work_rcvd; // пакетов принято в окне
|
||||
uint32_t work_bytes_sent; // байт отправлено в окне
|
||||
uint32_t work_bytes_rcvd; // байт принято в окне
|
||||
uint32_t work_lost; // потеряно в окне
|
||||
|
||||
uint32_t rtt_hist_final[ETCP_METRICS_RTT_BUCKETS]; // финальная RTT-гистограмма
|
||||
uint32_t loss_hist_final[ETCP_METRICS_LOSS_BUCKETS]; // финальная loss-гистограмма
|
||||
uint32_t samples_final;
|
||||
uint32_t sent_final;
|
||||
uint32_t rcvd_final;
|
||||
uint32_t bytes_sent_final;
|
||||
uint32_t bytes_rcvd_final;
|
||||
uint32_t lost_final;
|
||||
|
||||
uint64_t total_sent;
|
||||
uint64_t total_rcvd;
|
||||
uint64_t total_bytes_sent;
|
||||
uint64_t total_bytes_rcvd;
|
||||
uint64_t total_lost;
|
||||
|
||||
void* timer;
|
||||
uint64_t last_snapshot_tb;
|
||||
};
|
||||
|
||||
#define INFLIGHT_STATE_WAIT_ACK 0
|
||||
#define INFLIGHT_STATE_WAIT_SEND 1
|
||||
#define INFLIGHT_INITIAL_HASH_SIZE 1024
|
||||
@@ -113,6 +154,7 @@ struct ETCP_CONN {
|
||||
|
||||
// Peer info
|
||||
uint64_t peer_node_id; // Peer node ID
|
||||
uint8_t peer_ed25519_pubkey[SC_PUBKEY_SIZE]; // Ed25519 pubkey пира (из INIT)
|
||||
|
||||
// ============ Processing incoming data to be sent by ETCP
|
||||
struct ll_queue* input_queue; // Incoming packets to send (rx_pool -> ETCP_FRAGMENT)
|
||||
@@ -130,6 +172,8 @@ struct ETCP_CONN {
|
||||
void (*link_ready_for_send_fn)(struct ETCP_CONN*);// функцию которую должен вызвать драйвер линка при готовности линка принимать данные
|
||||
|
||||
struct ll_queue* output_queue; // Assembled outgoing packets (storage: ETCP_FRAGMENT / rx_pool)
|
||||
struct ll_queue* transit_queues; // hash по (src_node_id:8, dst_node_id:8) — транзитные очереди
|
||||
struct ll_queue* send_input_q; // единая входная очередь отправки (normalizer->input или tx_queue)
|
||||
|
||||
|
||||
// IDs and state
|
||||
@@ -175,20 +219,18 @@ struct ETCP_CONN {
|
||||
// uint32_t total_packets_sent; // Total packets sent counter - Not used
|
||||
|
||||
// Flags
|
||||
uint8_t routing_exchange_active; // 0 - не активен, 1 - надо инициировать обмен маршрутами (клиент), 2 - обмен маршрутами активен
|
||||
uint8_t routing_exchange_active; // 0-не активен, 1-надо инициировать (клиент), 2-обмен идёт, 3-завершён, 4-пропущен (нет BGP)
|
||||
uint8_t got_initial_pkt; //
|
||||
uint8_t initialized; // 0 - только созданный ETCP, 1 - хотя бы один линк проинициалзирован (обмен ключами произведен)
|
||||
uint32_t session_id; // случайный ID сессии (генерируется клиентом) для защиты от ложного reinit
|
||||
uint8_t tx_state; // 0 - n/a, 1 - data_wait (queues empty), 2 - link_wait (link busy)
|
||||
uint8_t links_up; // 0 - канал не готов для передачи, 1 - канал готов для передачи (хотя бы один линк не down)
|
||||
|
||||
// Callback for ready notification
|
||||
etcp_on_conn_ready ready_cbk; // callback при готовности соединения
|
||||
void* ready_arg; // аргумент для ready_cbk
|
||||
etcp_on_conn_ready up_cbk; // callback при готовности соединения
|
||||
void* up_arg; // аргумент для ready_cbk
|
||||
etcp_on_conn_ready down_cbk; // callback при готовности соединения
|
||||
void* down_arg; // аргумент для ready_cbk
|
||||
// Callback chains for ready/up/down notifications
|
||||
struct etcp_cbk_entry* ready_cbks; // цепочка callback'ов при готовности соединения
|
||||
struct etcp_cbk_entry* up_cbks; // цепочка callback'ов при поднятии канала
|
||||
struct etcp_cbk_entry* down_cbks; // цепочка callback'ов при падении канала
|
||||
void (*bgp_ready_cbk)(struct ETCP_CONN* conn); // вызывается когда BGP готов (завершён или пропущен)
|
||||
|
||||
uint32_t cnt_ack_hit_inf; // счетчик удлений из inflight
|
||||
uint32_t cnt_ack_hit_sndq; // счетчик удалений inflight пакетов из sndq
|
||||
@@ -198,6 +240,8 @@ struct ETCP_CONN {
|
||||
uint32_t tx_dup_count; // счетчик дубликатов удаленной стороны (из ACK)
|
||||
uint32_t debug[8]; // 8 значений для дебага (live watch)
|
||||
|
||||
struct etcp_metrics metrics; // гистограммы RTT/потерь, тотальные счётчики
|
||||
|
||||
// Logging identifier (format: "XXXX→XXXX [name]" - last 4 digits of local and peer node_id + optional name)
|
||||
char log_name[256];
|
||||
char* name; // Connection name from config (e.g., "client_test1"), or NULL/empty
|
||||
@@ -245,6 +289,14 @@ void etcp_on_link_down(struct ETCP_CONN* etcp);
|
||||
|
||||
void etcp_update_mtu(struct ETCP_CONN* etcp);
|
||||
|
||||
void etcp_metrics_init(struct etcp_metrics* m);
|
||||
void etcp_metrics_add_rtt(struct ETCP_CONN* etcp, uint16_t rtt_tb);
|
||||
void etcp_metrics_add_sent(struct ETCP_CONN* etcp, uint32_t len);
|
||||
void etcp_metrics_add_rcvd(struct ETCP_CONN* etcp, uint32_t len);
|
||||
void etcp_metrics_add_loss(struct ETCP_CONN* etcp, uint32_t count);
|
||||
void etcp_metrics_start_timer(struct ETCP_CONN* etcp);
|
||||
void etcp_metrics_stop_timer(struct ETCP_CONN* etcp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+61
-15
@@ -6,23 +6,74 @@
|
||||
#include "etcp.h"
|
||||
#include "pkt_normalizer.h"
|
||||
#include "utun_instance.h"
|
||||
#include "stcp_link.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include <string.h>
|
||||
|
||||
#define DEBUG_CATEGORY_ETCP_API DEBUG_CATEGORY_ETCP
|
||||
|
||||
void etcp_conn_set_ready_cbk(struct ETCP_CONN* e, etcp_cbk_fn fn, void* arg) {
|
||||
if (e) { e->ready_cbk = fn; e->ready_arg = arg; }
|
||||
if (!e) return;
|
||||
struct etcp_cbk_entry* entry = e->ready_cbks;
|
||||
while (entry) { struct etcp_cbk_entry* next = entry->next; u_free(entry); entry = next; }
|
||||
e->ready_cbks = NULL;
|
||||
if (fn) etcp_conn_add_ready_cbk(e, fn, arg);
|
||||
}
|
||||
void etcp_conn_set_up_cbk(struct ETCP_CONN* e, etcp_cbk_fn fn, void* arg) {
|
||||
if (e) { e->up_cbk = fn; e->up_arg = arg; }
|
||||
if (!e) return;
|
||||
struct etcp_cbk_entry* entry = e->up_cbks;
|
||||
while (entry) { struct etcp_cbk_entry* next = entry->next; u_free(entry); entry = next; }
|
||||
e->up_cbks = NULL;
|
||||
if (fn) etcp_conn_add_up_cbk(e, fn, arg);
|
||||
}
|
||||
void etcp_conn_set_down_cbk(struct ETCP_CONN* e, etcp_cbk_fn fn, void* arg) {
|
||||
if (e) { e->down_cbk = fn; e->down_arg = arg; }
|
||||
if (!e) return;
|
||||
struct etcp_cbk_entry* entry = e->down_cbks;
|
||||
while (entry) { struct etcp_cbk_entry* next = entry->next; u_free(entry); entry = next; }
|
||||
e->down_cbks = NULL;
|
||||
if (fn) etcp_conn_add_down_cbk(e, fn, arg);
|
||||
}
|
||||
void etcp_set_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_cbk_fn fn, void* arg) {
|
||||
if (inst) { inst->etcp_new_conn_cbk = fn; inst->etcp_new_conn_arg = arg; }
|
||||
if (!inst) return;
|
||||
struct etcp_cbk_entry* entry = inst->new_conn_cbks;
|
||||
while (entry) { struct etcp_cbk_entry* next = entry->next; u_free(entry); entry = next; }
|
||||
inst->new_conn_cbks = NULL;
|
||||
if (fn) etcp_add_new_conn_cbk(inst, fn, arg);
|
||||
}
|
||||
|
||||
static void etcp_cbk_add_to_chain(struct etcp_cbk_entry** head, etcp_cbk_fn fn, void* arg) {
|
||||
if (!head || !fn) return;
|
||||
struct etcp_cbk_entry* e = u_malloc(sizeof(struct etcp_cbk_entry));
|
||||
if (!e) return;
|
||||
e->fn = fn; e->arg = arg; e->next = *head;
|
||||
*head = e;
|
||||
}
|
||||
static void etcp_cbk_remove_from_chain(struct etcp_cbk_entry** head, etcp_cbk_fn fn, void* arg) {
|
||||
if (!head || !fn) return;
|
||||
struct etcp_cbk_entry** p = head;
|
||||
while (*p) {
|
||||
if ((*p)->fn == fn && (*p)->arg == arg) {
|
||||
struct etcp_cbk_entry* rm = *p;
|
||||
*p = rm->next; u_free(rm); return;
|
||||
}
|
||||
p = &(*p)->next;
|
||||
}
|
||||
}
|
||||
|
||||
void etcp_conn_add_ready_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_add_to_chain(&conn->ready_cbks, fn, arg); }
|
||||
void etcp_conn_remove_ready_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_remove_from_chain(&conn->ready_cbks, fn, arg); }
|
||||
void etcp_conn_add_up_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_add_to_chain(&conn->up_cbks, fn, arg); }
|
||||
void etcp_conn_remove_up_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_remove_from_chain(&conn->up_cbks, fn, arg); }
|
||||
void etcp_conn_add_down_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_add_to_chain(&conn->down_cbks, fn, arg); }
|
||||
void etcp_conn_remove_down_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg) { if (conn) etcp_cbk_remove_from_chain(&conn->down_cbks, fn, arg); }
|
||||
void etcp_add_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_cbk_fn fn, void* arg) { if (inst) etcp_cbk_add_to_chain(&inst->new_conn_cbks, fn, arg); }
|
||||
void etcp_remove_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_cbk_fn fn, void* arg) { if (inst) etcp_cbk_remove_from_chain(&inst->new_conn_cbks, fn, arg); }
|
||||
|
||||
void etcp_set_routing_exchange_state(struct ETCP_CONN* conn, uint8_t new_state) {
|
||||
if (!conn) return;
|
||||
conn->routing_exchange_active = new_state;
|
||||
if (new_state >= 3 && conn->bgp_ready_cbk)
|
||||
conn->bgp_ready_cbk(conn);
|
||||
}
|
||||
|
||||
int etcp_bind(struct UTUN_INSTANCE* inst, uint8_t id, etcp_recv_fn callback) {
|
||||
@@ -38,13 +89,8 @@ int etcp_unbind(struct UTUN_INSTANCE* inst, uint8_t id) {
|
||||
}
|
||||
|
||||
int etcp_send(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (!conn || !entry) return -1;
|
||||
// TCP (stcp_link) — send directly
|
||||
if (conn->transport_link)
|
||||
return stcp_link_send((struct stcp_link *)conn->transport_link, entry->dgram, entry->len);
|
||||
// UDP — через normalizer
|
||||
if (!conn->normalizer || !conn->normalizer->input) return -1;
|
||||
return queue_data_put(conn->normalizer->input, entry);
|
||||
if (!conn || !entry || !conn->send_input_q) return -1;
|
||||
return queue_data_put(conn->send_input_q, entry);
|
||||
}
|
||||
|
||||
void etcp_int_recv(struct ll_queue* queue, void* arg) {
|
||||
@@ -52,14 +98,14 @@ void etcp_int_recv(struct ll_queue* queue, void* arg) {
|
||||
if (!queue || !conn) return;
|
||||
struct ll_entry* e = queue_data_get(queue);
|
||||
if (!e) { queue_resume_callback(queue); return; }
|
||||
if (!e->dgram || e->len == 0) { queue_entry_free(e); queue_dgram_free(e); queue_resume_callback(queue); return; }
|
||||
if (!e->dgram || e->len == 0) { queue_dgram_free(e); queue_entry_free(e); queue_resume_callback(queue); return; }
|
||||
uint8_t id = e->dgram[0];
|
||||
struct UTUN_INSTANCE* inst = conn->instance;
|
||||
if (!inst) { queue_entry_free(e); queue_dgram_free(e); queue_resume_callback(queue); return; }
|
||||
if (!inst) { queue_dgram_free(e); queue_entry_free(e); queue_resume_callback(queue); return; }
|
||||
if (inst->api_bindings.callbacks[id])
|
||||
inst->api_bindings.callbacks[id](conn, e);
|
||||
else if (inst->api_bindings.callbacks[0])
|
||||
inst->api_bindings.callbacks[0](conn, e);
|
||||
else { queue_entry_free(e); queue_dgram_free(e); }
|
||||
else { queue_dgram_free(e); queue_entry_free(e); }
|
||||
queue_resume_callback(queue);
|
||||
}
|
||||
+58
-10
@@ -17,27 +17,47 @@
|
||||
#ifndef ETCP_API_H
|
||||
#define ETCP_API_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../lib/ll_queue.h"
|
||||
|
||||
#define ETCP_MAX_BINDINGS 256
|
||||
|
||||
// ETCP packet IDs
|
||||
// === Raw ETCP packet IDs (etcp_bind/etcp_send) ===
|
||||
#define ETCP_ID_DATA 0x00 // Пакет для передачи адресату
|
||||
#define ETCP_ID_ROUTE_ENTRY 0x01 // Элемент роутинг-таблицы
|
||||
#define ETCP_ID_NAT 0x02 // NAT трафик между узлами
|
||||
#define ETCP_ID_SVC_ROUTE 0x03 // Маршрутизируемые сервисные пакеты (etcp_router)
|
||||
#define ETCP_ID_TCP_PROXY 0x04 // TCP proxy exit node (server)
|
||||
#define ETCP_ID_UDP_PROXY 0x05 // UDP datagram прокси (client ↔ exit)
|
||||
#define ETCP_ID_ICMP_PROXY 0x06 // ICMP echo прокси (ping через exit)
|
||||
#define ETCP_ID_TCP_PROXY_CLIENT 0x07 // TCP proxy client (клиент)
|
||||
#define ETCP_ID_MSG_TRANSPORT 0x10 // msg_transport — локальный IPC транспорт сообщений
|
||||
#define ETCP_ID_CONN_MGR 0x11 // Connection Manager — management connections
|
||||
#define ETCP_ID_ROUTE_ENTRY 0x01 // Элемент роутинг-таблицы (BGP)
|
||||
#define ETCP_ID_SVC_ROUTE 0x03 // Транспорт роутера (etcp_router)
|
||||
|
||||
// === Router-сервисы (etcp_router_bind/etcp_route_send) ===
|
||||
#define ETCP_RT_ID_DATA 0x00 // routing.c — маршрутизация данных
|
||||
#define ETCP_RT_ID_NAT 0x02 // NAT трафик между узлами
|
||||
#define ETCP_RT_ID_SVC_ROUTE 0x03 // etcp_router — транспорт роутера
|
||||
#define ETCP_RT_ID_TCP_PROXY 0x04 // TCP proxy (клиент ↔ exit)
|
||||
#define ETCP_RT_ID_UDP_PROXY 0x05 // UDP datagram прокси (client ↔ exit)
|
||||
#define ETCP_RT_ID_ICMP_PROXY 0x06 // ICMP echo прокси (ping через exit)
|
||||
#define ETCP_RT_ID_MSG_TRANSPORT 0x10 // msg_transport — локальный IPC транспорт сообщений
|
||||
#define ETCP_RT_ID_CONN_MGR 0x11 // Connection Manager — management connections
|
||||
|
||||
// Forward declarations
|
||||
struct ETCP_CONN;
|
||||
struct UTUN_INSTANCE;
|
||||
|
||||
/**
|
||||
* @brief Тип коллбэка для приёма метрик от пира
|
||||
* @param user_ptr пользовательский указатель
|
||||
* @param conn ETCP соединение от которого получены метрики
|
||||
* @param csv CSV-строка метрик
|
||||
* @param csv_len длина строки
|
||||
* @param sig Ed25519 подпись (64 байта) или NULL если ключ неизвестен
|
||||
*/
|
||||
typedef void (*etcp_metrics_fn)(void* user_ptr, struct ETCP_CONN* conn,
|
||||
const uint8_t* csv, uint32_t csv_len,
|
||||
const uint8_t* sig);
|
||||
|
||||
/**
|
||||
* @brief Тип коллбэка для приёма пакетов
|
||||
* @param conn ETCP соединение от которого получен пакет
|
||||
@@ -51,8 +71,19 @@ typedef void (*etcp_recv_fn)(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||||
// Forward declaration
|
||||
struct ETCP_CONN;
|
||||
struct UTUN_INSTANCE;
|
||||
struct TOPO_NODEQ;
|
||||
typedef void (*etcp_cbk_fn)(struct ETCP_CONN* conn, void* arg);
|
||||
|
||||
// ---- Background connection initialization ----
|
||||
#define ETCP_CONNECT_EARLY 1
|
||||
#define ETCP_CONNECT_LATE 2
|
||||
#define ETCP_CONNECT_BGP_READY 4 // BGP-синхронизация завершена
|
||||
|
||||
typedef void (*etcp_connect_callback_t)(void* arg, struct ETCP_CONN* conn, int type);
|
||||
|
||||
int etcp_connect(struct UTUN_INSTANCE* instance, struct TOPO_NODEQ* node,
|
||||
etcp_connect_callback_t cb, void* arg, uint8_t flags);
|
||||
|
||||
/**
|
||||
* @brief Установить callback при создании нового ETCP соединения
|
||||
*
|
||||
@@ -67,6 +98,8 @@ void etcp_set_new_conn_cbk(struct UTUN_INSTANCE* instance, etcp_cbk_fn callback_
|
||||
*/
|
||||
struct ETCP_BINDINGS {
|
||||
etcp_recv_fn callbacks[ETCP_MAX_BINDINGS]; /**< Массив callbacks, NULL = не установлен */
|
||||
etcp_metrics_fn on_metrics_rcvd; /**< Callback приёма метрик от пира */
|
||||
void* metrics_user_ptr; /**< Аргумент для on_metrics_rcvd */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -118,6 +151,17 @@ void etcp_conn_set_ready_cbk(struct ETCP_CONN* conn, etcp_cbk_fn callback_fn, vo
|
||||
void etcp_conn_set_up_cbk (struct ETCP_CONN* conn, etcp_cbk_fn callback_fn, void* arg);
|
||||
void etcp_conn_set_down_cbk (struct ETCP_CONN* conn, etcp_cbk_fn callback_fn, void* arg);
|
||||
|
||||
void etcp_conn_add_ready_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg);
|
||||
void etcp_conn_remove_ready_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg);
|
||||
void etcp_conn_add_up_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg);
|
||||
void etcp_conn_remove_up_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg);
|
||||
void etcp_conn_add_down_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg);
|
||||
void etcp_conn_remove_down_cbk(struct ETCP_CONN* conn, etcp_cbk_fn fn, void* arg);
|
||||
void etcp_add_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_cbk_fn fn, void* arg);
|
||||
void etcp_remove_new_conn_cbk(struct UTUN_INSTANCE* inst, etcp_cbk_fn fn, void* arg);
|
||||
|
||||
void etcp_set_routing_exchange_state(struct ETCP_CONN* conn, uint8_t new_state);
|
||||
|
||||
/**
|
||||
* @brief Внутренняя функция etcp: Коллбэк для очередей output ll_queue normalizer
|
||||
*
|
||||
@@ -132,4 +176,8 @@ void etcp_conn_set_down_cbk (struct ETCP_CONN* conn, etcp_cbk_fn callback_fn, vo
|
||||
*/
|
||||
void etcp_int_recv(struct ll_queue* queue, void* arg);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // ETCP_API_H
|
||||
@@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BBR_SCALE 8
|
||||
#define BBR_UNIT (1 << BBR_SCALE)
|
||||
#define BW_SCALE 24
|
||||
@@ -110,3 +114,7 @@ void bbr_main(struct bbr* bbr, const struct bbr_rate_sample* rs,
|
||||
uint32_t mss, uint32_t inflight_bytes, int is_cwnd_limited);
|
||||
void bbr_note_loss(struct bbr* bbr);
|
||||
void bbr_tx_start(struct bbr* bbr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,283 @@
|
||||
#include "etcp_api.h"
|
||||
#include "etcp.h"
|
||||
#include "utun_instance.h"
|
||||
#include "topo_node.h"
|
||||
#include "stcp_link.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include "../lib/socket_compat.h"
|
||||
#include <string.h>
|
||||
|
||||
#define DEBUG_CATEGORY_ETCP_CONNECT DEBUG_CATEGORY_ETCP
|
||||
|
||||
struct etcp_connect_cb_node {
|
||||
etcp_connect_callback_t cb;
|
||||
void* arg;
|
||||
uint8_t flags;
|
||||
struct etcp_connect_cb_node* next;
|
||||
};
|
||||
|
||||
struct ETCP_CONNECT {
|
||||
struct ETCP_CONNECT* next;
|
||||
struct UTUN_INSTANCE* instance;
|
||||
uint64_t node_id;
|
||||
struct ETCP_CONN* conn;
|
||||
struct etcp_connect_cb_node* cb_list;
|
||||
void* initial_timer;
|
||||
void* settle_timer;
|
||||
struct stcp_link* tcp_link;
|
||||
uint16_t min_rtt;
|
||||
uint8_t early_delivered : 1;
|
||||
uint8_t done : 1;
|
||||
uint8_t tcp_ready : 1;
|
||||
};
|
||||
static void tcp_link_ready_cb(struct stcp_link* link, void* arg);
|
||||
static void tcp_link_close_cb(struct stcp_link* link, int err, void* arg);
|
||||
static void connect_ready_cb(struct ETCP_CONN* conn, void* arg);
|
||||
|
||||
static struct ETCP_CONNECT* connect_find(struct UTUN_INSTANCE* inst, uint64_t node_id) {
|
||||
struct ETCP_CONNECT* ctx = inst->pending_connects;
|
||||
while (ctx) { if (ctx->node_id == node_id) return ctx; ctx = ctx->next; }
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void connect_deliver(struct ETCP_CONNECT* ctx, int type) {
|
||||
struct etcp_connect_cb_node** pp = &ctx->cb_list;
|
||||
while (*pp) {
|
||||
struct etcp_connect_cb_node* node = *pp;
|
||||
if (type == 0 || (node->flags & (uint8_t)type)) {
|
||||
*pp = node->next;
|
||||
node->cb(node->arg, (type == 0) ? NULL : ctx->conn, type);
|
||||
u_free(node);
|
||||
} else {
|
||||
pp = &node->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void connect_cancel(struct ETCP_CONNECT* ctx) {
|
||||
if (!ctx) return;
|
||||
struct UTUN_INSTANCE* inst = ctx->instance;
|
||||
if (inst) {
|
||||
struct ETCP_CONNECT** pp = &inst->pending_connects;
|
||||
while (*pp && *pp != ctx) pp = &(*pp)->next;
|
||||
if (*pp) *pp = ctx->next;
|
||||
if (ctx->initial_timer) { uasync_cancel_timeout(inst->ua, ctx->initial_timer); ctx->initial_timer = NULL; }
|
||||
if (ctx->settle_timer) { uasync_cancel_timeout(inst->ua, ctx->settle_timer); ctx->settle_timer = NULL; }
|
||||
}
|
||||
u_free(ctx);
|
||||
}
|
||||
|
||||
static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct TOPO_NODEQ* node) {
|
||||
if (!node || !node->node) return;
|
||||
for (const struct TOPO_ADDR4* a = node->node->v4_addrs; a; a = a->next) {
|
||||
if (a->port == 0) continue;
|
||||
{ int zero = 1; for (int j = 0; j < 4; j++) if (a->addr[j] != 0) { zero = 0; break; } if (zero) continue; }
|
||||
struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET;
|
||||
memcpy(&sin.sin_addr.s_addr, a->addr, 4); sin.sin_port = htons(a->port);
|
||||
struct sockaddr_storage sa; memcpy(&sa, &sin, sizeof(sin));
|
||||
if (!(a->protocol & TOPO_PROTO_TCP)) {
|
||||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
|
||||
while (s) { if (s->local_addr.ss_family == AF_INET) etcp_link_new(ctx->conn, s, &sa, 0); s = s->next; }
|
||||
}
|
||||
if ((a->protocol & TOPO_PROTO_TCP) && !ctx->tcp_link) {
|
||||
struct stcp_link_config tcp_cfg = {.ua = ctx->instance->ua, .my_keys = &ctx->instance->my_keys, .inst = ctx->instance,
|
||||
.peer_pubkey = ctx->conn->crypto_ctx.peer_public_key, .peer_pubkey_mode = 0,
|
||||
.remote_addr = &sa, .remote_port = a->port};
|
||||
ctx->tcp_link = stcp_link_connect(&tcp_cfg);
|
||||
if (ctx->tcp_link) { ctx->conn->transport_link = (void*)ctx->tcp_link; stcp_link_set_on_ready(ctx->tcp_link, tcp_link_ready_cb, ctx); stcp_link_set_on_close(ctx->tcp_link, tcp_link_close_cb, ctx); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void connect_create_links_v6(struct ETCP_CONNECT* ctx, struct TOPO_NODEQ* node) {
|
||||
if (!node || !node->node) return;
|
||||
for (const struct TOPO_ADDR6* a = node->node->v6_addrs; a; a = a->next) {
|
||||
if (a->port == 0) continue;
|
||||
int zero = 1; for (int j = 0; j < 16; j++) if (a->addr[j] != 0) { zero = 0; break; } if (zero) continue;
|
||||
struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6;
|
||||
memcpy(&sin6.sin6_addr, a->addr, 16); sin6.sin6_port = htons(a->port);
|
||||
struct sockaddr_storage sa; memcpy(&sa, &sin6, sizeof(sin6));
|
||||
if (!(a->protocol & TOPO_PROTO_TCP)) {
|
||||
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
|
||||
while (s) { if (s->local_addr.ss_family == AF_INET6) etcp_link_new(ctx->conn, s, &sa, 0); s = s->next; }
|
||||
}
|
||||
if ((a->protocol & TOPO_PROTO_TCP) && !ctx->tcp_link) {
|
||||
struct stcp_link_config tcp_cfg = {.ua = ctx->instance->ua, .my_keys = &ctx->instance->my_keys, .inst = ctx->instance,
|
||||
.peer_pubkey = ctx->conn->crypto_ctx.peer_public_key, .peer_pubkey_mode = 0,
|
||||
.remote_addr = &sa, .remote_port = a->port};
|
||||
ctx->tcp_link = stcp_link_connect(&tcp_cfg);
|
||||
if (ctx->tcp_link) {
|
||||
ctx->conn->transport_link = (void*)ctx->tcp_link;
|
||||
stcp_link_set_on_ready(ctx->tcp_link, tcp_link_ready_cb, ctx);
|
||||
stcp_link_set_on_close(ctx->tcp_link, tcp_link_close_cb, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_link_ready_cb(struct stcp_link* link, void* arg) {
|
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||||
if (!ctx || ctx->done || ctx->tcp_ready) return;
|
||||
ctx->tcp_ready = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] TCP link ready for node 0x%016llx", (unsigned long long)ctx->node_id);
|
||||
connect_ready_cb(ctx->conn, ctx);
|
||||
}
|
||||
|
||||
static void tcp_link_close_cb(struct stcp_link* link, int err, void* arg) {
|
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||||
if (!ctx || ctx->done) return;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] TCP link closed node 0x%016llx err=%d",
|
||||
(unsigned long long)ctx->node_id, err);
|
||||
ctx->tcp_link = NULL;
|
||||
}
|
||||
|
||||
static void connect_bgp_ready_cb(struct ETCP_CONN* conn) {
|
||||
struct ETCP_CONNECT* ctx = connect_find(conn->instance, conn->peer_node_id);
|
||||
if (!ctx || ctx->done) return;
|
||||
connect_deliver(ctx, ETCP_CONNECT_BGP_READY);
|
||||
}
|
||||
|
||||
static void connect_initial_timeout_cb(void* arg) {
|
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||||
if (!ctx || ctx->done) return;
|
||||
ctx->done = 1;
|
||||
ctx->initial_timer = NULL;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] timeout for node 0x%016llx", (unsigned long long)ctx->node_id);
|
||||
struct ETCP_CONN* conn = ctx->conn; ctx->conn = NULL;
|
||||
if (ctx->tcp_link) { stcp_link_close(ctx->tcp_link); ctx->tcp_link = NULL; }
|
||||
connect_deliver(ctx, 0);
|
||||
if (conn) etcp_connection_close(conn);
|
||||
connect_cancel(ctx);
|
||||
}
|
||||
|
||||
static void connect_settle_timeout_cb(void* arg) {
|
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||||
if (!ctx || ctx->done) return;
|
||||
ctx->done = 1;
|
||||
ctx->settle_timer = NULL;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] settle for node 0x%016llx, min_rtt=%u",
|
||||
(unsigned long long)ctx->node_id, ctx->min_rtt);
|
||||
struct ETCP_LINK* link = ctx->conn->links;
|
||||
while (link) {
|
||||
struct ETCP_LINK* next = link->next;
|
||||
if (!link->initialized) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] removing failed link=%p for node 0x%016llx",
|
||||
(void*)link, (unsigned long long)ctx->node_id);
|
||||
etcp_link_close(link);
|
||||
}
|
||||
link = next;
|
||||
}
|
||||
if (ctx->tcp_link && !ctx->tcp_ready) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] removing failed TCP link for node 0x%016llx",
|
||||
(unsigned long long)ctx->node_id);
|
||||
stcp_link_close(ctx->tcp_link); ctx->tcp_link = NULL;
|
||||
}
|
||||
etcp_conn_remove_ready_cbk(ctx->conn, connect_ready_cb, ctx);
|
||||
connect_deliver(ctx, ETCP_CONNECT_LATE);
|
||||
connect_cancel(ctx);
|
||||
}
|
||||
|
||||
static void connect_ready_cb(struct ETCP_CONN* conn, void* arg) {
|
||||
struct ETCP_CONNECT* ctx = (struct ETCP_CONNECT*)arg;
|
||||
if (!ctx || ctx->done) return;
|
||||
struct ETCP_LINK* link = conn->links;
|
||||
while (link) {
|
||||
if (link->initialized && link->rtt_last && link->rtt_last < ctx->min_rtt)
|
||||
ctx->min_rtt = link->rtt_last;
|
||||
link = link->next;
|
||||
}
|
||||
if (ctx->early_delivered) return;
|
||||
ctx->early_delivered = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] early ready for node 0x%016llx, min_rtt=%u",
|
||||
(unsigned long long)ctx->node_id, ctx->min_rtt);
|
||||
connect_deliver(ctx, ETCP_CONNECT_EARLY);
|
||||
if (ctx->initial_timer) { uasync_cancel_timeout(ctx->instance->ua, ctx->initial_timer); ctx->initial_timer = NULL; }
|
||||
uint32_t settle_tb;
|
||||
if (ctx->min_rtt == 0xFFFF) settle_tb = 20000;
|
||||
else {
|
||||
settle_tb = (uint32_t)ctx->min_rtt * 8;
|
||||
if (settle_tb < 5000) settle_tb = 5000;
|
||||
if (settle_tb > 20000) settle_tb = 20000;
|
||||
}
|
||||
ctx->settle_timer = uasync_set_timeout(ctx->instance->ua, (int)settle_tb, ctx,
|
||||
connect_settle_timeout_cb, "etcp_connect_settle");
|
||||
}
|
||||
|
||||
int etcp_connect(struct UTUN_INSTANCE* inst, struct TOPO_NODEQ* node,
|
||||
etcp_connect_callback_t cb, void* arg, uint8_t flags) {
|
||||
if (!inst || !node || !cb) return -1;
|
||||
uint64_t node_id = node->node->node_id;
|
||||
|
||||
struct ETCP_CONN* existing = inst->connections;
|
||||
while (existing) {
|
||||
if (existing->peer_node_id == node_id) {
|
||||
struct ETCP_LINK* l = existing->links;
|
||||
while (l) { if (l->link_state == 3) break; l = l->next; }
|
||||
if (l) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx already connected, delivering immediately",
|
||||
(unsigned long long)node_id);
|
||||
if (flags & ETCP_CONNECT_EARLY) cb(arg, existing, ETCP_CONNECT_EARLY);
|
||||
if (flags & ETCP_CONNECT_LATE) cb(arg, existing, ETCP_CONNECT_LATE);
|
||||
if ((flags & ETCP_CONNECT_BGP_READY) && existing->routing_exchange_active >= 3)
|
||||
cb(arg, existing, ETCP_CONNECT_BGP_READY);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
existing = existing->next;
|
||||
}
|
||||
|
||||
struct ETCP_CONNECT* ctx = connect_find(inst, node_id);
|
||||
if (ctx) {
|
||||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||||
if (!cn) return -1;
|
||||
cn->cb = cb; cn->arg = arg; cn->flags = flags; cn->next = ctx->cb_list; ctx->cb_list = cn;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] node 0x%016llx already connecting, added cb",
|
||||
(unsigned long long)node_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ETCP_CONN* conn = etcp_connection_create(inst, NULL);
|
||||
if (!conn) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] failed to create ETCP_CONN for node 0x%016llx",
|
||||
(unsigned long long)node_id); cb(arg, NULL, 0); return -1; }
|
||||
conn->next = inst->connections; inst->connections = conn; inst->connections_count++;
|
||||
if (sc_init_ctx(&conn->crypto_ctx, &inst->my_keys) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_init_ctx failed for node 0x%016llx", (unsigned long long)node_id);
|
||||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1;
|
||||
}
|
||||
if (sc_set_peer_public_key(&conn->crypto_ctx, node->node->public_key, 0) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] sc_set_peer_public_key failed for node 0x%016llx", (unsigned long long)node_id);
|
||||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1;
|
||||
}
|
||||
|
||||
ctx = u_calloc(1, sizeof(struct ETCP_CONNECT));
|
||||
if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] alloc failed");
|
||||
etcp_connection_close(conn); cb(arg, NULL, 0); return -1; }
|
||||
ctx->instance = inst; ctx->node_id = node_id; ctx->conn = conn;
|
||||
ctx->min_rtt = 0xFFFF;
|
||||
{
|
||||
struct etcp_connect_cb_node* cn = u_calloc(1, sizeof(struct etcp_connect_cb_node));
|
||||
if (!cn) { u_free(ctx); etcp_connection_close(conn); cb(arg, NULL, 0); return -1; }
|
||||
cn->cb = cb; cn->arg = arg; cn->flags = flags; ctx->cb_list = cn;
|
||||
}
|
||||
|
||||
etcp_conn_add_ready_cbk(conn, connect_ready_cb, ctx);
|
||||
conn->bgp_ready_cbk = connect_bgp_ready_cb;
|
||||
|
||||
connect_create_links_v4(ctx, node);
|
||||
connect_create_links_v6(ctx, node);
|
||||
|
||||
if (!conn->links && !conn->transport_link) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] no links created for node 0x%016llx",
|
||||
(unsigned long long)node_id);
|
||||
if (ctx->tcp_link) { stcp_link_close(ctx->tcp_link); ctx->tcp_link = NULL; }
|
||||
connect_deliver(ctx, 0);
|
||||
etcp_connection_close(conn); u_free(ctx); return -1;
|
||||
}
|
||||
|
||||
ctx->initial_timer = uasync_set_timeout(inst->ua, (int)inst->etcp_connect_timeout_tb, ctx, connect_initial_timeout_cb, "etcp_connect_init");
|
||||
ctx->next = inst->pending_connects; inst->pending_connects = ctx;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] started for node 0x%016llx", (unsigned long long)node_id);
|
||||
return 0;
|
||||
}
|
||||
+134
-64
@@ -1,4 +1,5 @@
|
||||
#include "etcp_connections.h"
|
||||
#include "etcp_api.h"
|
||||
#include "../lib/socket_compat.h"
|
||||
#include "../lib/platform_compat.h"
|
||||
#include "../lib/getmyip.h"
|
||||
@@ -16,8 +17,8 @@
|
||||
#include "crc32.h"
|
||||
#include "etcp.h"
|
||||
#include "stcp_link.h"
|
||||
#include "route_node.h"
|
||||
#include "route_bgp.h"
|
||||
#include "topo_node.h"
|
||||
#include "topo_group.h"
|
||||
#include "route_ping.h"
|
||||
#include "../lib/memory_pool.h"
|
||||
#include "../lib/u_async.h"
|
||||
@@ -39,8 +40,9 @@ static void tcp_server_on_link(struct stcp_link *link, void *arg) {
|
||||
conn->next = inst->connections;
|
||||
inst->connections = conn;
|
||||
inst->connections_count++;
|
||||
if (inst->etcp_new_conn_cbk) inst->etcp_new_conn_cbk(conn, inst->etcp_new_conn_arg);
|
||||
if (conn->ready_cbk) conn->ready_cbk(conn, conn->ready_arg);
|
||||
struct etcp_cbk_entry* cbe = inst->new_conn_cbks;
|
||||
while (cbe) { cbe->fn(conn, cbe->arg); cbe = cbe->next; }
|
||||
{ struct etcp_cbk_entry* rcb = conn->ready_cbks; while (rcb) { rcb->fn(conn, rcb->arg); rcb = rcb->next; } }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server new conn=%p total=%d", (void*)conn, inst->connections_count);
|
||||
}
|
||||
|
||||
@@ -132,7 +134,8 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset) {
|
||||
memset(req->src_ipv4, 0, 4);
|
||||
memset(req->src_port, 0, 2);
|
||||
}
|
||||
size_t offset = ETCP_INIT_REQ_V2_SIZE;
|
||||
memcpy(req->ed25519_pubkey, link->etcp->instance->my_ed25519_pubkey, SC_PUBKEY_SIZE);
|
||||
size_t offset = ETCP_INIT_REQ_SIZE;
|
||||
|
||||
// padding
|
||||
int s = rand() % (link->handshake_maxsize - link->handshake_minsize) + link->handshake_minsize;
|
||||
@@ -237,7 +240,7 @@ static void etcp_link_send_keepalive(struct ETCP_LINK* link) {
|
||||
|
||||
struct ETCP_DGRAM* dgram = u_malloc(sizeof(struct ETCP_DGRAM) + 4);
|
||||
if (!dgram) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_KEEPALIVE, "etcp_link_send_keepalive: malloc failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_KEEPALIVE, "malloc failed");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -324,10 +327,11 @@ static void keepalive_timer_cb(void* arg) {
|
||||
}
|
||||
}
|
||||
|
||||
// Adaptive keepalive period
|
||||
// Adaptive keepalive period (only if adaptive enabled)
|
||||
if (link->pkt_sent_since_keepalive)
|
||||
link->ka_period_ms = KA_PERIOD_MIN_MS; // data flowing → reset to 200ms
|
||||
else {
|
||||
link->ka_period_ms = KA_PERIOD_MIN_MS;
|
||||
else if (link->etcp->instance && link->etcp->instance->config &&
|
||||
link->etcp->instance->config->global.keepalive_adaptive) {
|
||||
uint32_t next = (uint32_t)link->ka_period_ms * 105 / 100 + 1;
|
||||
link->ka_period_ms = next > KA_PERIOD_MAX_MS ? KA_PERIOD_MAX_MS : (uint16_t)next;
|
||||
}
|
||||
@@ -375,7 +379,7 @@ static int realloc_links(struct ETCP_SOCKET* e_sock) {
|
||||
size_t new_max = e_sock->max_channels == 0 ? 8 : e_sock->max_channels * 2;
|
||||
struct ETCP_LINK** new_links = u_realloc(e_sock->links, new_max * sizeof(struct ETCP_LINK*));
|
||||
if (!new_links) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "realloc_links: realloc failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "realloc failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -698,7 +702,7 @@ struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_S
|
||||
e_sock->pkt_format_errors = 0;
|
||||
e_sock->type = type;
|
||||
e_sock->sock_id = instance->next_socket_id++;
|
||||
e_sock->nat_type = type; // initial type from config, updated after NAT detection
|
||||
e_sock->nat_type = NAT_TYPE_UNKNOWN; // только результат детекции NAT, серверные сокеты стартуют с unknown
|
||||
e_sock->mtu = mtu;
|
||||
e_sock->only_local = only_local;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Add Socket type=%d", type);
|
||||
@@ -739,9 +743,7 @@ void etcp_socket_remove(struct ETCP_SOCKET* conn) {
|
||||
size_t i = 0;
|
||||
while (i < conn->num_channels) {
|
||||
struct ETCP_LINK* l = conn->links[i];
|
||||
etcp_link_close(l);
|
||||
conn->links[i] = NULL; // обнулить после — etcp_link_close обращается к массиву через remove_link
|
||||
i++;
|
||||
etcp_link_close(l); // remove_link inside shifts elements left → next at same i
|
||||
}
|
||||
u_free(conn->links);
|
||||
|
||||
@@ -755,7 +757,7 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn
|
||||
|
||||
struct ETCP_LINK* link = u_calloc(1, sizeof(struct ETCP_LINK));
|
||||
if (!link) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_link_new: calloc failed - out of memory or pool exhausted");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "calloc failed - out of memory or pool exhausted");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -813,7 +815,7 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn
|
||||
// Выделяем свободный local_link_id
|
||||
int free_id = etcp_find_free_local_link_id(etcp);
|
||||
if (free_id <= 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_link_new: no free local_link_id available");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "no free local_link_id available");
|
||||
u_free(link);
|
||||
return NULL;
|
||||
}
|
||||
@@ -843,7 +845,7 @@ struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "NEW link initialized on etcp=[%s] link=%p socket=%s id=%d is_server=%d mtu=%d", etcp->log_name, link, conn->name, link->local_link_id, link->is_server, link->mtu);
|
||||
|
||||
if (is_server == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "etcp_link_new: client link, calling etcp_link_send_init");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "client link, calling etcp_link_send_init");
|
||||
etcp_link_enter_init(link);
|
||||
}
|
||||
|
||||
@@ -1134,13 +1136,15 @@ int etcp_send_ping(struct UTUN_INSTANCE* instance, const uint8_t* peer_pubkey_bi
|
||||
etcp_ping_callback_t cb, void* user_arg,
|
||||
const uint8_t* user_data, size_t user_data_len) {
|
||||
if (!instance || !peer_pubkey_bin || !addr || timeout_ms <= 0 || !cb) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "bad args");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "bad args: inst=%p pubkey=%p addr=%p timeout=%d cb=%p udata=%p udata_len=%zu",
|
||||
(void*)instance, (void*)peer_pubkey_bin, (void*)addr, timeout_ms,
|
||||
(void*)(uintptr_t)cb, (void*)user_data, user_data_len);
|
||||
return -1;
|
||||
}
|
||||
struct ETCP_SOCKET* e_sock = instance->etcp_sockets;
|
||||
while (e_sock && e_sock->local_addr.ss_family != addr->ss_family) e_sock = e_sock->next;
|
||||
if (!e_sock) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "no socket");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "no socket for addr_family=%d", addr->ss_family);
|
||||
return -2;
|
||||
}
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "ping N1 [%s]", sockaddr_storage_to_str(addr).str);
|
||||
@@ -1279,15 +1283,15 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk
|
||||
link->nat_port = 0;
|
||||
}
|
||||
// DIRECT detection: if client reports its own address and it matches observed source → real public IP
|
||||
if (pkt_len >= ETCP_INIT_REQ_V2_SIZE && addr->ss_family == AF_INET && link->nat_ip != 0) {
|
||||
if (pkt_len >= ETCP_INIT_REQ_SIZE && addr->ss_family == AF_INET && link->nat_ip != 0) {
|
||||
if (req_src_ip != 0 && req_src_ip == link->nat_ip
|
||||
&& req_src_port == link->nat_port
|
||||
&& !is_local_subnet(link->nat_ip))
|
||||
{
|
||||
link->nat_type = NAT_TYPE_DIRECT;
|
||||
link->nat_check_status = NAT_CHECK_EIM;
|
||||
if (link->etcp->instance->bgp) {
|
||||
route_bgp_send_nat_info(link->etcp, link->remote_socket_id,
|
||||
if (link->etcp->instance->topo_groups) {
|
||||
topo_group_send_nat_info(link->etcp, link->remote_socket_id,
|
||||
link->nat_ip, link->nat_port, NAT_TYPE_DIRECT);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "DIRECT IP: %s:%u for %s",
|
||||
@@ -1295,6 +1299,40 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk
|
||||
link->etcp->log_name);
|
||||
}
|
||||
}
|
||||
/* Self-detection: if my IP is non-local, I'm on a public IP */
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "self-detect check: socket=%s type=%d nat_type=%d if_addr=%s local_addr=%s",
|
||||
e_sock->name, e_sock->type, e_sock->nat_type,
|
||||
sockaddr_storage_to_str(&e_sock->interface_addr).str,
|
||||
sockaddr_storage_to_str(&e_sock->local_addr).str);
|
||||
if (e_sock->nat_type != NAT_VERIFIED_DIRECT && e_sock->interface_addr.ss_family == AF_INET) {
|
||||
uint32_t my_ip = ((struct sockaddr_in*)&e_sock->interface_addr)->sin_addr.s_addr;
|
||||
if (my_ip != 0 && !is_local_subnet(my_ip)) {
|
||||
e_sock->nat_type = NAT_VERIFIED_DIRECT;
|
||||
struct sockaddr_in* nat_sin = (struct sockaddr_in*)&e_sock->nat_addr;
|
||||
nat_sin->sin_family = AF_INET;
|
||||
nat_sin->sin_addr.s_addr = my_ip;
|
||||
nat_sin->sin_port = ((struct sockaddr_in*)&e_sock->interface_addr)->sin_port;
|
||||
{ struct TOPO_GROUP* g = topo_groups_get_default(link->etcp->instance->topo_groups);
|
||||
if (g) {
|
||||
topo_group_update_my_nodeinfo(g->instance, g);
|
||||
if (g->local_node && g->senders_list) {
|
||||
g->local_node->node->ver = (g->local_node->node->ver % 255) + 1;
|
||||
g->local_node->dirty = 1;
|
||||
struct ll_entry* se = g->senders_list->head;
|
||||
while (se) {
|
||||
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)se->data;
|
||||
if (item && item->conn) topo_group_send_nodeinfo(g, g->local_node, item->conn);
|
||||
se = se->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "Self-detected PUBLIC: socket=%s ip=%s port=%u sock_id=%d",
|
||||
e_sock->name, ip_to_str(&my_ip, AF_INET).str,
|
||||
ntohs(nat_sin->sin_port), e_sock->sock_id);
|
||||
}
|
||||
}
|
||||
memcpy(resp->ed25519_pubkey, e_sock->instance->my_ed25519_pubkey, SC_PUBKEY_SIZE);
|
||||
pkt->noencrypt_len=0;
|
||||
pkt->link=link;
|
||||
link->recv_keepalive = 1;
|
||||
@@ -1333,13 +1371,12 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk
|
||||
start_keepalive_timer(link);
|
||||
loadbalancer_link_ready(link);
|
||||
// Restart NAT check after link is up (e.g. after address change or reinit)
|
||||
if (link->etcp->instance->bgp) {
|
||||
if (link->nat_check_status < NAT_CHECK_IN_PROGRESS) route_bgp_start_link_nat_check(link->etcp->instance->bgp, link);
|
||||
}
|
||||
{ struct TOPO_GROUP* g = topo_groups_get_default(link->etcp->instance->topo_groups);
|
||||
if (g && link->nat_check_status < NAT_CHECK_IN_PROGRESS) topo_group_start_link_nat_check(g, link); }
|
||||
}
|
||||
|
||||
static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt, struct ETCP_LINK* link, uint8_t pkt_code, size_t pkt_len) {
|
||||
if (pkt_len < ETCP_INIT_RESP_V1_SIZE) {
|
||||
if (pkt_len < ETCP_INIT_RESP_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "INIT_RESPONSE too short: pkt_len=%zu", pkt_len);
|
||||
return 46;
|
||||
}
|
||||
@@ -1366,7 +1403,7 @@ static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_D
|
||||
link->remote_type = resp->type;
|
||||
|
||||
// Parse NAT IP:port from response (new format includes 4+2 bytes)
|
||||
if (pkt_len >= ETCP_INIT_RESP_V2_SIZE) {
|
||||
if (pkt_len >= ETCP_INIT_RESP_SIZE) {
|
||||
uint32_t new_nat_ip;
|
||||
memcpy(&new_nat_ip, resp->peer_ipv4, 4);
|
||||
uint16_t new_nat_port = be16toh(*(uint16_t*)resp->peer_port);
|
||||
@@ -1395,19 +1432,31 @@ static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_D
|
||||
link->nat_changes_count);
|
||||
}
|
||||
|
||||
// Update socket NAT address
|
||||
// Update socket NAT address (only if not already verified)
|
||||
{
|
||||
struct sockaddr_in* sin = (struct sockaddr_in*)&e_sock->nat_addr;
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr.s_addr = new_nat_ip;
|
||||
sin->sin_port = htons(new_nat_port);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "init_resp nat: socket=%s nat_type=%d new_ip=%s new_port=%u",
|
||||
e_sock->name, e_sock->nat_type,
|
||||
ip_to_str(&new_nat_ip, AF_INET).str, new_nat_port);
|
||||
if (e_sock->nat_type < NAT_VERIFIED_UNKNOWN) {
|
||||
struct sockaddr_in* sin = (struct sockaddr_in*)&e_sock->nat_addr;
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr.s_addr = new_nat_ip;
|
||||
sin->sin_port = htons(new_nat_port);
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_NAT, "init_resp nat update SKIPPED (already verified): socket=%s nat_type=%d",
|
||||
e_sock->name, e_sock->nat_type);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Legacy format without NAT info
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] Received legacy INIT_RESPONSE without NAT info",
|
||||
link->etcp->log_name);
|
||||
}
|
||||
|
||||
|
||||
memcpy(link->remote_ed25519_pubkey, resp->ed25519_pubkey, SC_PUBKEY_SIZE);
|
||||
memcpy(link->etcp->peer_ed25519_pubkey, resp->ed25519_pubkey, SC_PUBKEY_SIZE);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "[%s] Received Ed25519 pubkey from peer", link->etcp->log_name);
|
||||
|
||||
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Received INIT_RESPONSE from server_node_id=%llu, mtu=%d", (unsigned long long)server_node_id, link->mtu);
|
||||
|
||||
@@ -1477,7 +1526,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
ssize_t recv_len = socket_recvfrom(sock, data, PACKET_DATA_SIZE, (struct sockaddr*)&addr, &addr_len);
|
||||
|
||||
if (recv_len <= 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: recvfrom failed, error=%zd, sock_err=%d", recv_len, socket_get_error());
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "recvfrom failed, error=%zd, sock_err=%d", recv_len, socket_get_error());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1504,7 +1553,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
goto process_decrypted;
|
||||
}
|
||||
// Normal decryption failed - might be INIT packet, fall through to INIT handling
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Normal decryption failed, trying INIT decryption");
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "Normal decryption failed, trying INIT decryption");
|
||||
}
|
||||
|
||||
// Try INIT decryption (for incoming connection requests)
|
||||
@@ -1529,6 +1578,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
errorcode=2;
|
||||
goto ec_fr;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "X25519 decrypt OK from %s", sockaddr_storage_to_str(&addr).str);
|
||||
if (sc_decrypt(&sc, data, recv_len - SC_PUBKEY_ENC_SIZE, (uint8_t*)&pkt->timestamp, &pkt_len)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "failed to decrypt init packet, from %s", sockaddr_storage_to_str(&addr).str);
|
||||
errorcode=3;
|
||||
@@ -1552,11 +1602,15 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
uint8_t code = pkt->data[0];
|
||||
uint64_t peer_id = be64toh(*(uint64_t*)(pkt->data + 1));
|
||||
if (code == ETCP_PING) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "X25519 decrypted: PING from peer=0x%016llx src=%s",
|
||||
(unsigned long long)peer_id, sockaddr_storage_to_str(&addr).str);
|
||||
int ret = handle_ping(e_sock, pkt, &addr, decrypted_pubkey, pkt_len);
|
||||
if (ret) { errorcode = ret; goto ec_fr; }
|
||||
return;
|
||||
}
|
||||
if (code == ETCP_PONG) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "X25519 decrypted: PONG from peer=0x%016llx src=%s",
|
||||
(unsigned long long)peer_id, sockaddr_storage_to_str(&addr).str);
|
||||
int ret = handle_pong(e_sock, pkt, &addr, pkt_len);
|
||||
if (ret) { errorcode = ret; goto ec_fr; }
|
||||
return;
|
||||
@@ -1566,7 +1620,12 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
errorcode=4;
|
||||
goto ec_fr;
|
||||
}// не init
|
||||
if (pkt_len < ETCP_INIT_REQ_V1_SIZE) {
|
||||
if (e_sock->type == CFG_SERVER_TYPE_PRIVATE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "INIT rejected: socket %s (type=PRIVATE) does not accept incoming connections", e_sock->name);
|
||||
errorcode=8;
|
||||
goto ec_fr;
|
||||
}
|
||||
if (pkt_len < ETCP_INIT_REQ_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "INIT REQUEST too short: pkt_len=%zu from %s", pkt_len, sockaddr_storage_to_str(&addr).str);
|
||||
errorcode=7;
|
||||
goto ec_fr;
|
||||
@@ -1598,7 +1657,15 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
|
||||
struct ETCP_INIT_REQUEST_PKT* req = (struct ETCP_INIT_REQUEST_PKT*)pkt->data;
|
||||
uint32_t session_id = be32toh(*(uint32_t*)req->session_id);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "INIT request session_id=%08x", session_id);
|
||||
uint16_t mtu = be16toh(*(uint16_t*)req->mtu);
|
||||
uint16_t src_port = be16toh(*(uint16_t*)req->src_port);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION,
|
||||
"INIT accepted: peer=0x%016llx mtu=%u link=%u sock=%u type=%d local=%d"
|
||||
" src_ip=%d.%d.%d.%d:%u session=%08x src=%s",
|
||||
(unsigned long long)peer_id, mtu, req->link_id, req->socket_id,
|
||||
req->type, req->only_local,
|
||||
req->src_ipv4[0], req->src_ipv4[1], req->src_ipv4[2], req->src_ipv4[3],
|
||||
src_port, session_id, sockaddr_storage_to_str(&addr).str);
|
||||
|
||||
struct ETCP_CONN* conn=e_sock->instance->connections;
|
||||
while (conn) {// ищем есть ли подключение к этому пиру
|
||||
@@ -1645,9 +1712,8 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
goto ec_fr;
|
||||
}
|
||||
// Cancel pending NAT check and reset status
|
||||
if (link->etcp->instance->bgp) {
|
||||
route_ping_cancel_for_conn(link->etcp->instance->bgp, link->etcp);
|
||||
}
|
||||
{ struct TOPO_GROUP* g = topo_groups_get_default(link->etcp->instance->topo_groups);
|
||||
if (g) route_ping_cancel_for_conn(g, link->etcp); }
|
||||
link->nat_check_status = NAT_CHECK_NONE;
|
||||
link->nat_type = NAT_TYPE_UNKNOWN;
|
||||
}
|
||||
@@ -1661,14 +1727,14 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
// For CHANNEL_INIT (0x04): if link already initialized - no reset, otherwise reset
|
||||
// For INIT_REQUEST (0x02): always reset
|
||||
// Check session_id: if same - no reinit, if different - client restarted, do reinit
|
||||
if (conn->session_id == session_id) {
|
||||
send_reset = 0; // Same session, no reinit needed
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "same session_id, skip reinit");
|
||||
} else {
|
||||
send_reset = 1; // New session or uninitialized link - send reset
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "new session_id %08x (was %08x), do reinit", session_id, conn->session_id);
|
||||
if (code == ETCP_INIT_REQUEST || conn->session_id != session_id || !conn->got_initial_pkt) {
|
||||
send_reset = 1; // Client explicitly requested reset, or new session, or server waiting for first packet
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "do reinit (code=%02x session was %08x now %08x got_init=%d)", code, conn->session_id, session_id, conn->got_initial_pkt);
|
||||
conn->session_id = session_id;
|
||||
etcp_conn_reinit(conn);
|
||||
} else {
|
||||
send_reset = 0; // Same session, no reinit needed
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "same session_id, skip reinit");
|
||||
}
|
||||
|
||||
// Cancel existing timers
|
||||
@@ -1680,16 +1746,16 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] NO existing link for id=%d, socket=[%s]", conn->log_name, req->link_id, e_sock->name);
|
||||
// Create new link
|
||||
link = etcp_link_new(conn, e_sock, &addr, 1);
|
||||
if (!link) { if (new_conn) etcp_connection_close(conn); errorcode=66; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "etcp_connections_read_callback: failed to create link for connection"); goto ec_fr; }// облом
|
||||
if (!link) { if (new_conn) etcp_connection_close(conn); errorcode=66; DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "failed to create link for connection"); goto ec_fr; }// облом
|
||||
link->remote_link_id = req->link_id;
|
||||
link->remote_socket_id = req->socket_id;
|
||||
link->remote_only_local = req->only_local;
|
||||
link->remote_type = req->type;
|
||||
|
||||
// For new links: check session_id to avoid false reinit
|
||||
if (conn->session_id != session_id) {
|
||||
send_reset = 1; // New session or uninitialized link - send reset
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "new session_id %08x (was %08x) for new link, do reinit", session_id, conn->session_id);
|
||||
// For new links: reset if client requested or session changed
|
||||
if (code == ETCP_INIT_REQUEST || conn->session_id != session_id || !conn->got_initial_pkt) {
|
||||
send_reset = 1;
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "do reinit for new link (code=%02x session was %08x now %08x got_init=%d)", code, conn->session_id, session_id, conn->got_initial_pkt);
|
||||
conn->session_id = session_id;
|
||||
etcp_conn_reinit(conn);
|
||||
}
|
||||
@@ -1707,13 +1773,15 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
uint32_t req_src_ip; memcpy(&req_src_ip, req->src_ipv4, 4);
|
||||
uint16_t req_src_port = be16toh(*(uint16_t*)req->src_port);
|
||||
|
||||
memcpy(conn->peer_ed25519_pubkey, req->ed25519_pubkey, SC_PUBKEY_SIZE);
|
||||
|
||||
send_init_response(e_sock, pkt, link, conn, req, &addr, send_reset, req_src_ip, req_src_port, pkt_len);
|
||||
return;
|
||||
|
||||
|
||||
process_decrypted:
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "Decrypt ok - normal pkt");
|
||||
if (pkt_len<3) { errorcode=46; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: decrypted packet too small, size=%zu", pkt_len); goto ec_fr; }
|
||||
if (pkt_len<3) { errorcode=46; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "decrypted packet too small, size=%zu", pkt_len); goto ec_fr; }
|
||||
pkt->data_len=pkt_len-3;
|
||||
pkt->noencrypt_len=0;
|
||||
pkt->link=link;
|
||||
@@ -1769,7 +1837,7 @@ process_decrypted:
|
||||
|
||||
if (link->link_state == 3) {
|
||||
if (memory_pool_is_freed(e_sock->instance->pkt_pool, pkt)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_conn_input: pkt=%p ALREADY FREED in pkt_pool — HALTING", (void*)pkt);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pkt=%p ALREADY FREED in pkt_pool — HALTING", (void*)pkt);
|
||||
volatile int _halt = 1; while (_halt) {}
|
||||
}
|
||||
etcp_conn_input(pkt);
|
||||
@@ -1777,7 +1845,7 @@ process_decrypted:
|
||||
return;
|
||||
|
||||
ec_fr:
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "etcp_connections_read_callback: error %d, from %s", errorcode, sockaddr_storage_to_str(&addr).str);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "error %d, from %s", errorcode, sockaddr_storage_to_str(&addr).str);
|
||||
e_sock->pkt_format_errors++;
|
||||
e_sock->errorcode=errorcode;
|
||||
memory_pool_free(e_sock->instance->pkt_pool, pkt);
|
||||
@@ -1785,12 +1853,12 @@ ec_fr:
|
||||
}
|
||||
|
||||
// Initialize only sockets (servers for incoming connections)
|
||||
// Called before route_bgp_init() to populate etcp_sockets for nodeinfo
|
||||
// Called before topo_group_init() to populate etcp_sockets for nodeinfo
|
||||
// Returns: 0 = all OK, 1 = partial success (some sockets failed), -1 = fatal error (no sockets)
|
||||
int init_sockets(struct UTUN_INSTANCE* instance) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "");
|
||||
if (!instance || !instance->config) return -1;
|
||||
if (instance->etcp_sockets) {
|
||||
if (instance->etcp_sockets || instance->stcp_server) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "Sockets already initialized, skipping");
|
||||
return 0;
|
||||
}
|
||||
@@ -1851,10 +1919,12 @@ int init_sockets(struct UTUN_INSTANCE* instance) {
|
||||
if (server->transport) {
|
||||
uint16_t port = ntohs(((struct sockaddr_in*)&server->ip)->sin_port);
|
||||
struct stcp_link_config scfg = {.ua = instance->ua, .my_keys = &instance->my_keys, .inst = instance};
|
||||
if (!stcp_server_listen(&scfg, port, tcp_server_on_link, instance)) {
|
||||
struct stcp_server *tsrv = stcp_server_listen(&scfg, port, tcp_server_on_link, instance);
|
||||
if (!tsrv) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create TCP server for %s", server->name);
|
||||
fail_count++;
|
||||
} else {
|
||||
instance->stcp_server = tsrv;
|
||||
success_count++;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server %s on port %u", server->name, port);
|
||||
}
|
||||
@@ -1900,7 +1970,7 @@ int init_connections(struct UTUN_INSTANCE* instance) {
|
||||
int socket_result = 0;
|
||||
|
||||
// If sockets already exist (created by init_sockets), check stored status
|
||||
if (instance->etcp_sockets) {
|
||||
if (instance->etcp_sockets || instance->stcp_server) {
|
||||
if (instance->socket_init_status == 1) {
|
||||
socket_result = 1;
|
||||
}
|
||||
@@ -1943,7 +2013,7 @@ int init_connections(struct UTUN_INSTANCE* instance) {
|
||||
|
||||
// Initialize crypto context for this connection
|
||||
if (sc_init_ctx(&etcp_conn->crypto_ctx, &instance->my_keys) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "init_connections: failed to initialize crypto context for client %s", client->name);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "failed to initialize crypto context for client %s", client->name);
|
||||
etcp_connection_close(etcp_conn);
|
||||
client = client->next;
|
||||
continue;
|
||||
@@ -1955,18 +2025,18 @@ int init_connections(struct UTUN_INSTANCE* instance) {
|
||||
etcp_conn->peer_node_id = 1; // Simple indicator
|
||||
etcp_update_log_name(etcp_conn); // Update log_name with peer_node_id
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "init_connections: setting peer public key for client %s", client->name);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "setting peer public key for client %s", client->name);
|
||||
// Set peer public key (assuming hex format)
|
||||
if (sc_set_peer_public_key(&etcp_conn->crypto_ctx, (const uint8_t*)client->peer_public_key_hex, 1) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "init_connections: failed to set peer public key for client %s", client->name);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "failed to set peer public key for client %s", client->name);
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "init_connections: successfully set peer public key for client %s", client->name);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "successfully set peer public key for client %s", client->name);
|
||||
}
|
||||
} else {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "init_connections: no peer public key configured for client %s", client->name);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "no peer public key configured for client %s", client->name);
|
||||
}
|
||||
|
||||
etcp_conn->routing_exchange_active=1;// инициируем обмен маршрутами
|
||||
etcp_set_routing_exchange_state(etcp_conn, 1); // инициируем обмен маршрутами
|
||||
|
||||
// Create links for this client
|
||||
struct CFG_CLIENT_LINK* client_link = client->links;
|
||||
|
||||
+16
-5
@@ -1,6 +1,11 @@
|
||||
#ifndef ETCP_CONNECTIONS_H
|
||||
#define ETCP_CONNECTIONS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// подмодуль ETCP который обслуживает сокеты ETCP для приёма-передачи пакетов и одно ETCP подключение через несколько каналов связи (failover)
|
||||
|
||||
#include "secure_channel.h"
|
||||
@@ -71,9 +76,10 @@ struct ETCP_INIT_REQUEST_PKT {
|
||||
// V2 fields (NAT_DIRECT detection):
|
||||
uint8_t src_ipv4[4]; // 23: client interface_addr IPv4 (big-endian, 0 if N/A)
|
||||
uint8_t src_port[2]; // 27: client interface_addr port (big-endian)
|
||||
// V3 fields:
|
||||
uint8_t ed25519_pubkey[SC_PUBKEY_SIZE]; // 29: client Ed25519 pubkey (32 bytes)
|
||||
} __attribute__((packed));
|
||||
#define ETCP_INIT_REQ_V1_SIZE 23 // size without src_ipv4/src_port (backward compat)
|
||||
#define ETCP_INIT_REQ_V2_SIZE 29 // size with src_ipv4/src_port
|
||||
#define ETCP_INIT_REQ_SIZE sizeof(struct ETCP_INIT_REQUEST_PKT)
|
||||
|
||||
// INIT RESPONSE: server → client
|
||||
struct ETCP_INIT_RESPONSE_PKT {
|
||||
@@ -88,10 +94,10 @@ struct ETCP_INIT_RESPONSE_PKT {
|
||||
// V2 fields:
|
||||
uint8_t peer_ipv4[4]; // 19: client's external NAT address (big-endian)
|
||||
uint8_t peer_port[2]; // 23: client's external NAT port (big-endian)
|
||||
// V3 fields:
|
||||
uint8_t ed25519_pubkey[SC_PUBKEY_SIZE]; // 25: server Ed25519 pubkey (32 bytes)
|
||||
} __attribute__((packed));
|
||||
#define ETCP_INIT_RESP_V1_SIZE 19 // size without peer_ipv4/peer_port
|
||||
#define ETCP_INIT_RESP_V2_SIZE 25 // size with peer_ipv4/peer_port
|
||||
#define ETCP_INIT_RESP_MIN_SIZE ETCP_INIT_RESP_V1_SIZE
|
||||
#define ETCP_INIT_RESP_SIZE sizeof(struct ETCP_INIT_RESPONSE_PKT)
|
||||
|
||||
typedef void (*etcp_ping_callback_t)(int success, uint16_t rtt, void* arg, uint64_t nonce,
|
||||
const uint8_t* resp_data, size_t resp_data_len);
|
||||
@@ -187,6 +193,7 @@ struct ETCP_LINK {
|
||||
uint8_t remote_only_local; // only_local flag from peer
|
||||
uint8_t remote_type; // CFG_SERVER_TYPE_* (type of peer's socket)
|
||||
uint8_t nat_type; // NAT_TYPE_* (detected for this client link)
|
||||
uint8_t remote_ed25519_pubkey[SC_PUBKEY_SIZE]; // Ed25519 pubkey пира (из INIT handshake)
|
||||
uint8_t recv_keepalive; // 1 - up, 0 - down (принимаются ли пакеты)
|
||||
uint8_t remote_keepalive; // 1 - up, 0 - down (удаленная сторона сообщает - принимаются ли у нее пакеты)
|
||||
uint8_t link_status; // 1 - up, 0 - down (итоговый статус - если есть проблемы на любой стороне - линк down)
|
||||
@@ -334,4 +341,8 @@ int etcp_send_ping_to_socket(struct UTUN_INSTANCE* instance, struct ETCP_SOCKET*
|
||||
|
||||
void etcp_connections_read_callback_socket(socket_t sock, void* arg);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // ETCP_CONNECTIONS_H
|
||||
@@ -1,6 +1,11 @@
|
||||
#ifndef ETCP_DEBUG_H
|
||||
#define ETCP_DEBUG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include "etcp_connections.h"
|
||||
|
||||
typedef struct { char a[16]; } IP_STR;
|
||||
@@ -9,4 +14,8 @@ IP_STR ip_to_string(uint32_t ip);
|
||||
|
||||
void etcp_dump_pkt_sections(struct ETCP_DGRAM* pkt, struct ETCP_LINK* link, int is_send);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,10 +1,19 @@
|
||||
#ifndef ETCP_DUMP_H
|
||||
#define ETCP_DUMP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include "etcp.h"
|
||||
#include "utun_instance.h"
|
||||
|
||||
void etcp_dump_conn_state(struct ETCP_CONN* conn);
|
||||
void etcp_dump_all_conns(struct UTUN_INSTANCE* instance);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+839
-279
File diff suppressed because it is too large.
Load diff
+105
-6
@@ -9,35 +9,73 @@
|
||||
#ifndef ETCP_ROUTER_H
|
||||
#define ETCP_ROUTER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "etcp_api.h"
|
||||
|
||||
#define TRANSIT_QUEUE_HASH 256
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct SVC_ROUTE_HDR {
|
||||
uint8_t cmd; // ETCP_ID_SVC_ROUTE (0x03)
|
||||
uint8_t cmd; // ETCP_ID_SVC_ROUTE (0x03) / ETCP_RT_ID_SVC_ROUTE
|
||||
uint64_t dst_node_id;
|
||||
uint64_t src_node_id;
|
||||
uint32_t seq; // data: tx_seq; ACK: rx_seq (ожидаемый seq)
|
||||
uint8_t svc_id; // идентификатор сервиса
|
||||
uint8_t flags; // bit7=START, bit6=RST, bit5-4=sess_id
|
||||
uint16_t timestamp; // часы отправителя (0.1ms), для RTT; ACK: эхо последнего data-пакета
|
||||
};
|
||||
#pragma pack(pop)
|
||||
#define SVC_ROUTE_HDR_SIZE sizeof(struct SVC_ROUTE_HDR) // 23
|
||||
#define SVC_ROUTE_HDR_SIZE sizeof(struct SVC_ROUTE_HDR) // 25
|
||||
#define SVC_ROUTE_MAX_BINDINGS 256
|
||||
|
||||
// Биты в flags
|
||||
#define ROUTER_FLAG_START 0x80
|
||||
#define ROUTER_FLAG_RST 0x40
|
||||
#define ROUTER_FLAG_SIGNED 0x08 // пакет содержит Ed25519 подпись (64 байта после payload)
|
||||
#define ROUTER_FLAG_CLOSE 0x02 // нормальное закрытие conn
|
||||
#define ROUTER_SESS_ID_SHIFT 4
|
||||
#define ROUTER_SESS_ID_MASK 0x30
|
||||
|
||||
// Очередь транзитных пакетов для пары (src_node_id, dst_node_id)
|
||||
// Создаётся когда не получается отправить пакет в ETCP напрямую,
|
||||
// удаляется когда очередь становится пустой.
|
||||
// ll.data[0..15] = src:8 + dst:8 — совпадает с полями ниже по памяти
|
||||
struct TRANSIT_QUEUE {
|
||||
struct ll_entry ll; // data[0..15] = src_node_id:8 + dst_node_id:8 (16-byte hash index)
|
||||
uint64_t src_node_id; // = data[0..7]
|
||||
uint64_t dst_node_id; // = data[8..15]
|
||||
struct ll_queue* q; // FIFO транзитных пакетов (ll_entry с dgram), начиная с data[16]
|
||||
struct queue_waiter_handle waiter; // backpressure waiter на bp-очередь (normalizer->input или tx_queue)
|
||||
struct ETCP_CONN* conn; // next_hop (для etcp_send в drain_cb)
|
||||
};
|
||||
|
||||
// Состояние одного логического подключения (remote_node_id + svc_id)
|
||||
struct ETCP_ROUTER_CONN {
|
||||
struct ll_entry ll; // data[0..7]=remote_node_id, data[8]=svc_id — хеш-индекс
|
||||
uint64_t remote_node_id; // = data[0..7]
|
||||
uint8_t svc_id; // = data[8]
|
||||
uint16_t last_dgram_ts; // последний timestamp датаграммы (rx или tx, timebase 0.1ms)
|
||||
uint16_t rtt; // измеренный RTT (0.1ms)
|
||||
int32_t rtt_jitter; // EMA jitter, fixed-point *65536 (как в ETCP)
|
||||
uint16_t max_inflight; // настраиваемый рабочий максимум (default=ROUTER_MAX_INFLIGHT)
|
||||
uint16_t inflight_limit; // текущий эффективный лимит (=max_inflight или MINRTT_PROBE_MAX_INFLIGHT)
|
||||
uint16_t minrtt; // скользящее среднее minRTT (0.1ms), default=100 (10ms)
|
||||
uint16_t minrtt_window[10]; // окно 10 последних замеров minRTT
|
||||
uint64_t minrtt_window_tb[10];// таймстемпы замеров (timebase 0.1ms)
|
||||
uint8_t minrtt_window_idx; // cyclic index 0..9
|
||||
uint8_t minrtt_window_count; // активных записей в окне (0..10)
|
||||
uint8_t minrtt_probe; // режим измерения (inflight capped at 4)
|
||||
uint64_t last_inflight_loaded_tb; // когда inflight >= threshold
|
||||
uint64_t last_inflight_unloaded_tb; // когда inflight < threshold
|
||||
uint8_t inflight_was_loaded; // 1=канал загружен
|
||||
uint16_t last_recv_pkt_ts; // timestamp пира из последнего data-пакета
|
||||
uint64_t last_recv_pkt_local_tb; // наше время приёма последнего data-пакета (0.1ms timebase)
|
||||
uint8_t last_recv_updated; // флаг: есть данные для эха в ACK
|
||||
|
||||
uint32_t tx_seq; // следующий seq для отправки
|
||||
uint32_t rx_seq; // ожидаемый seq для сборки (next expected)
|
||||
@@ -53,9 +91,36 @@ struct ETCP_ROUTER_CONN {
|
||||
struct ll_queue* send_q; // очередь ожидающих отправки (inflight полон)
|
||||
void* send_resume_timer; // таймер возобновления отправки
|
||||
uint8_t send_blocked; // 1 = inflight полон, ждём ack/таймер
|
||||
uint8_t no_route; // 1 = нет BGP-маршрута, передача приостановлена
|
||||
void* no_route_timer; // таймер 20ms проверки появления маршрута
|
||||
uint8_t no_ack_count; // счётчик последовательных ретрансмиссий без ACK
|
||||
uint8_t closed; // 1 = в процессе закрытия, таймеры игнорируют
|
||||
void* close_callback; // пользовательский коллбэк завершения закрытия
|
||||
void* close_callback_arg; // аргумент для close_callback
|
||||
uint8_t sess_id; // наш session id (0-3)
|
||||
uint8_t peer_sess_id; // последний sess_id от peer'а
|
||||
uint8_t start_sent; // 0 = нужно отправить START в первом data
|
||||
uint8_t peer_sync_done; // 0 = rx_seq ещё не синхронизирован с удалённым seq (авто-создание/рестарт)
|
||||
|
||||
// Ретрансмиты: inflight очередь
|
||||
struct ll_queue* inflight_q; // хеш по seq (4 байта), непрерывный блок без SACK
|
||||
void* retrans_timer; // таймер проверки застоя ACK
|
||||
uint64_t last_ack_changed_tb; // когда tx_acked последний раз менялся (0.1ms)
|
||||
|
||||
// Очередь приёма входящего трафика (между сетью и recv_q)
|
||||
struct ll_queue* incoming_q; // FIFO очередь входящих пакетов
|
||||
uint8_t incoming_data_ready; // 1 = данные в incoming_q и callback был вызван
|
||||
|
||||
uint32_t c_pkts_sent; // успешные отправки данных
|
||||
uint32_t c_pkts_send_err; // ошибки отправки
|
||||
uint32_t c_pkts_rcvd; // получено и доставлено данных
|
||||
uint32_t c_ack_sent; // отправлено ACK
|
||||
uint32_t c_ack_recv; // получено ACK (последовательных)
|
||||
uint32_t c_retrans_done; // число выполненых ретрансмитов
|
||||
uint32_t c_dup_dropped; // дропнуто дубликатов seq
|
||||
uint32_t c_oob_dropped; // дропнуто out-of-bounds seq
|
||||
uint32_t c_stale_ack; // устаревших ACK
|
||||
uint32_t c_sign_fail; // signature verification failures
|
||||
};
|
||||
|
||||
#define ROUTER_CONN_HASH_SIZE 256
|
||||
@@ -64,11 +129,30 @@ struct ETCP_ROUTER_CONN {
|
||||
#define ROUTER_ACK_INTERVAL_TB 100 // интервал ACK: 10ms в timebase (0.1ms)
|
||||
#define ROUTER_ACK_IDLE_TB 5000 // idle таймаут: 500ms
|
||||
#define ROUTER_SEND_RESUME_TB 500 // retry интервал send_q: 50ms
|
||||
#define ROUTER_MAX_SEND_Q_PACKETS 4 // порог backpressure на send_q
|
||||
#define ROUTER_MAX_SEND_Q_PACKETS 64 // порог backpressure на send_q
|
||||
|
||||
// Флаги в старших битах seq (взаимоисключающие)
|
||||
#define ROUTER_SEQ_CLOSE_FLAG 0x80000000 // нормальное закрытие conn
|
||||
#define ROUTER_SEQ_RST_FLAG 0x40000000 // conn не найден (reset)
|
||||
// Ретрансмиты
|
||||
#define ROUTER_RETRANS_TIMEOUT_TB 3000 // 300ms в timebase (0.1ms)
|
||||
#define ROUTER_INFLIGHT_HASH_SIZE 1024
|
||||
#define ROUTER_NO_ROUTE_RETRY_TB 200 // 20ms проверка появления маршрута
|
||||
#define ROUTER_NO_ACK_MAX_RETRANS 17 // 17 × 300ms ≈ 5s таймаут без ACK
|
||||
|
||||
// minRTT measurement
|
||||
#define MINRTT_UNLOADED_MIN_TB 500 // 50ms минимальное время разгрузки канала
|
||||
#define MINRTT_PROBE_TIMEOUT_TB 100000 // 10 сек без свежих замеров → probe
|
||||
#define MINRTT_PROBE_MAX_INFLIGHT 4 // лимит inflight в режиме probe
|
||||
#define MINRTT_WINDOW_SIZE 10
|
||||
#define MINRTT_DEFAULT_TB 100 // default minRTT = 10ms (заменяется первым RTT)
|
||||
|
||||
// Inflight запись — копия отправленного пакета для возможного ретрансмита
|
||||
struct ROUTER_INFLIGHT {
|
||||
struct ll_entry ll; // индекс по seq (4 байта, offset 0)
|
||||
uint32_t seq;
|
||||
uint64_t last_sent_tb; // время последней отправки (0.1ms)
|
||||
uint8_t send_count; // число переотправок
|
||||
uint8_t* payload; // копия данных
|
||||
size_t payload_len;
|
||||
};
|
||||
|
||||
// Bindings для сервисов внутри etcp_router (аналогично ETCP_BINDINGS)
|
||||
struct ETCP_ROUTER_BINDINGS {
|
||||
@@ -104,6 +188,14 @@ int etcp_router_conn_send_signed(struct ETCP_ROUTER_CONN* rconn,
|
||||
// Закрыть seq-подключение
|
||||
void etcp_router_conn_close(struct ETCP_ROUTER_CONN* rconn);
|
||||
|
||||
// Установить рабочий max_inflight, пересчитывает inflight_limit и state machine
|
||||
void router_set_max_inflight(struct ETCP_ROUTER_CONN* rconn, uint16_t new_max);
|
||||
|
||||
// Асинхронное закрытие: коллбэк вызывается после освобождения памяти rconn
|
||||
void etcp_router_conn_close_async(struct ETCP_ROUTER_CONN* rconn,
|
||||
void (*on_close_done)(void* arg),
|
||||
void* close_arg);
|
||||
|
||||
// Закрыть все router_conn для указанного remote_node_id (peer умер)
|
||||
void etcp_router_conn_close_all_for_node(struct UTUN_INSTANCE* inst, uint64_t remote_node_id);
|
||||
|
||||
@@ -129,4 +221,11 @@ void etcp_router_cancel_send_ready(struct UTUN_INSTANCE* inst, uint64_t node_id,
|
||||
// Очищает send_q/recv_q, уведомляет сервис через cb(NULL, entry) с remote_node_id.
|
||||
void etcp_router_conn_restart(struct UTUN_INSTANCE* inst, uint64_t remote_node_id, uint8_t svc_id);
|
||||
|
||||
// Удалить все транзитные очереди соединения (вызывается при закрытии ETCP_CONN)
|
||||
void etcp_router_transit_queues_destroy(struct ETCP_CONN* conn);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // ETCP_ROUTER_H
|
||||
+1
-1
@@ -32,7 +32,7 @@ int fw_load_rules(struct firewall_ctx *ctx, const struct global_config *cfg) {
|
||||
if (cfg->firewall_rule_count > 0) {
|
||||
ctx->rules = u_calloc(cfg->firewall_rule_count, sizeof(struct CFG_FIREWALL_RULE));
|
||||
if (!ctx->rules) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "fw_load_rules: failed to allocate rules");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "failed to allocate rules");
|
||||
return -1;
|
||||
}
|
||||
memcpy(ctx->rules, cfg->firewall_rules,
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef LWIP_PBUF_H
|
||||
#define LWIP_PBUF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -38,4 +43,8 @@ int pbuf_take(struct pbuf *p, const void *data, uint16_t len);
|
||||
int pbuf_take_at(struct pbuf *p, const void *data, uint16_t len, uint16_t offset);
|
||||
void* pbuf_get_contiguous(const struct pbuf *p, void *buf, size_t len, size_t offset);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -458,7 +458,7 @@ struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)
|
||||
lpcb = (struct tcp_pcb_listen *)memory_pool_alloc(pcb->ctx->pcb_listen_pool);
|
||||
if (lpcb == NULL) {
|
||||
pcb->ctx->stats.pcb_listen_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_listen: out of memory");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "out of memory");
|
||||
return NULL;
|
||||
}
|
||||
memset(lpcb, 0, sizeof(*lpcb));
|
||||
@@ -1097,7 +1097,7 @@ struct tcp_pcb *tcp_alloc(struct lwip_tcp_ctx *ctx, uint8_t prio)
|
||||
pcb = (struct tcp_pcb *)memory_pool_alloc(ctx->pcb_pool);
|
||||
if (pcb == NULL) {
|
||||
ctx->stats.pcb_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_alloc: out of memory after all retries");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "out of memory after all retries");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1116,7 +1116,7 @@ struct tcp_pcb *tcp_alloc(struct lwip_tcp_ctx *ctx, uint8_t prio)
|
||||
pcb->sv = (int16_t)(TCP_RTO_MIN_MS / TCP_SLOW_INTERVAL);
|
||||
pcb->rtime = -1;
|
||||
pcb->cwnd = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "tcp_alloc: cwnd=%u snd_buf=%u mss=%u snd_wnd=%u", pcb->cwnd, pcb->snd_buf, pcb->mss, pcb->snd_wnd);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "cwnd=%u snd_buf=%u mss=%u snd_wnd=%u", pcb->cwnd, pcb->snd_buf, pcb->mss, pcb->snd_wnd);
|
||||
pcb->tmr = ctx->ticks;
|
||||
pcb->last_timer = ctx->slowtmr_ctr;
|
||||
pcb->ssthresh = TCP_SND_BUF;
|
||||
@@ -1167,7 +1167,7 @@ void tcp_accept(struct tcp_pcb *pcb, tcp_accept_fn accept)
|
||||
void tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, uint8_t interval)
|
||||
{
|
||||
if (!pcb || pcb->state == LISTEN) return;
|
||||
pcb->poll = poll;
|
||||
pcb->pollfn = poll;
|
||||
pcb->pollinterval = interval;
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -2,6 +2,11 @@
|
||||
#ifndef LWIP_TCP_H
|
||||
#define LWIP_TCP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "lwip_pbuf.h"
|
||||
@@ -164,7 +169,7 @@ struct tcp_pcb {
|
||||
tcp_sent_fn sent;
|
||||
tcp_recv_fn recv;
|
||||
tcp_connected_fn connected;
|
||||
tcp_poll_fn poll;
|
||||
tcp_poll_fn pollfn;
|
||||
tcp_err_fn errf;
|
||||
uint32_t keep_idle;
|
||||
uint8_t persist_cnt;
|
||||
@@ -270,4 +275,8 @@ void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, uint8_t interval);
|
||||
#define tcp_clear_flags(pcb, clr_flags) ((pcb)->flags &= (tcpflags_t)~(clr_flags))
|
||||
#define tcp_is_flag_set(pcb, flag) (((pcb)->flags & (flag)) != 0)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -67,7 +67,7 @@ static struct tcp_seg *tcp_seg_copy_local(struct tcp_pcb *pcb, struct tcp_seg *s
|
||||
struct tcp_seg *cseg;
|
||||
if (!seg || !pcb || !pcb->ctx) return NULL;
|
||||
cseg = (struct tcp_seg *)memory_pool_alloc(pcb->ctx->seg_pool);
|
||||
if (!cseg) { pcb->ctx->stats.seg_fails++; DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_seg_copy_local: no memory"); return NULL; }
|
||||
if (!cseg) { pcb->ctx->stats.seg_fails++; DEBUG_ERROR(DEBUG_CATEGORY_ALL, "no memory"); return NULL; }
|
||||
memcpy(cseg, seg, sizeof(struct tcp_seg));
|
||||
pbuf_ref(cseg->p);
|
||||
return cseg;
|
||||
@@ -139,14 +139,14 @@ void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p,
|
||||
uint16_t wire_dst_port = tcphdr->dest;
|
||||
|
||||
if (p->len < TCP_HLEN) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_input: short packet (%u bytes) discarded", p->tot_len);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "short packet (%u bytes) discarded", p->tot_len);
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
|
||||
hdrlen_bytes = TCPH_HDRLEN_BYTES(tcphdr);
|
||||
if ((hdrlen_bytes < TCP_HLEN) || (hdrlen_bytes > p->tot_len)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_input: invalid header length (%u)", (unsigned)hdrlen_bytes);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "invalid header length (%u)", (unsigned)hdrlen_bytes);
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
@@ -163,7 +163,7 @@ void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p,
|
||||
opt2len = (uint16_t)(tcphdr_optlen - tcphdr_opt1len);
|
||||
pbuf_header(p, -(int16_t)tcphdr_opt1len);
|
||||
if (opt2len > p->next->len) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_input: options overflow second pbuf");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "options overflow second pbuf");
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
@@ -184,7 +184,7 @@ void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p,
|
||||
if (flags & (TCP_FIN | TCP_SYN)) {
|
||||
tcplen++;
|
||||
if (tcplen < p->tot_len) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_input: length overflow");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "length overflow");
|
||||
pbuf_free(p);
|
||||
return;
|
||||
}
|
||||
@@ -314,7 +314,7 @@ void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p,
|
||||
if (err == LERR_ABRT) goto aborted;
|
||||
if (err != LERR_OK) {
|
||||
pcb->refused_data = recv_data;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ALL, "tcp_input: keep incoming packet, pcb is full");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ALL, "keep incoming packet, pcb is full");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ static void tcp_listen_input(struct tcp_pcb_listen *pcb)
|
||||
(unsigned)tcphdr->src, (unsigned)tcphdr->dest);
|
||||
npcb = tcp_alloc(pcb->ctx, pcb->prio);
|
||||
if (npcb == NULL) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_listen_input: could not allocate PCB");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "could not allocate PCB");
|
||||
if (pcb->accept) pcb->accept(pcb->callback_arg, NULL, LERR_MEM);
|
||||
return;
|
||||
}
|
||||
@@ -1043,7 +1043,7 @@ static void tcp_parseopt(struct tcp_pcb *pcb)
|
||||
case TCP_OPT_MSS:
|
||||
if (tcp_get_next_optbyte() != TCP_OPT_MSS_LEN ||
|
||||
(tcp_optidx - 2 + TCP_OPT_MSS_LEN) > tcphdr_optlen) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_parseopt: bad MSS option length");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "bad MSS option length");
|
||||
return;
|
||||
}
|
||||
mss = (uint16_t)(tcp_get_next_optbyte() << 8);
|
||||
@@ -1053,7 +1053,7 @@ static void tcp_parseopt(struct tcp_pcb *pcb)
|
||||
default:
|
||||
data = tcp_get_next_optbyte();
|
||||
if (data < 2) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_parseopt: bad option length");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "bad option length");
|
||||
return;
|
||||
}
|
||||
tcp_optidx += data - 2;
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef LWIP_TCP_OPTS_H
|
||||
#define LWIP_TCP_OPTS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#define TCP_MSS 1460
|
||||
#define TCP_SND_BUF (16 * TCP_MSS)
|
||||
#define TCP_WND (8 * TCP_MSS)
|
||||
@@ -47,4 +52,8 @@
|
||||
#define TCP_PRIO_NORMAL 64
|
||||
#define TCP_PRIO_MAX 127
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+19
-19
@@ -80,7 +80,7 @@ static struct pbuf *tcp_pbuf_alloc(struct tcp_pcb *pcb, uint16_t layer, uint16_t
|
||||
struct pbuf *p = pbuf_alloc(layer, length);
|
||||
if (!p && pcb && pcb->ctx) {
|
||||
pcb->ctx->stats.pbuf_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_pbuf_alloc: out of memory (layer=%u len=%u)", layer, length);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "out of memory (layer=%u len=%u)", layer, length);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@@ -101,7 +101,7 @@ tcp_create_segment(const struct tcp_pcb *pcb, struct pbuf *p,
|
||||
seg = (struct tcp_seg *)memory_pool_alloc(pcb->ctx->seg_pool);
|
||||
if (seg == NULL) {
|
||||
pcb->ctx->stats.seg_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_create_segment: no memory for seg");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "no memory for seg");
|
||||
pbuf_free(p);
|
||||
return NULL;
|
||||
}
|
||||
@@ -111,7 +111,7 @@ tcp_create_segment(const struct tcp_pcb *pcb, struct pbuf *p,
|
||||
seg->len = (uint16_t)(p->tot_len - optlen);
|
||||
|
||||
if (pbuf_header(p, (int16_t)TCP_HLEN)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_create_segment: no room for TCP header");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "no room for TCP header");
|
||||
tcp_seg_free_local((struct tcp_pcb *)pcb, seg);
|
||||
return NULL;
|
||||
}
|
||||
@@ -139,14 +139,14 @@ tcp_write_checks(struct tcp_pcb *pcb, uint16_t len)
|
||||
}
|
||||
if (len == 0) return LERR_OK;
|
||||
if (len > pcb->snd_buf) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: too much data (len=%u > snd_buf=%u)", len, pcb->snd_buf);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "too much data (len=%u > snd_buf=%u)", len, pcb->snd_buf);
|
||||
pcb->ctx->stats.write_fails++;
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
return LERR_MEM;
|
||||
}
|
||||
if (pcb->snd_queuelen >= TCP_SND_QUEUELEN) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: too long queue (queuelen=%u max=%u)", pcb->snd_queuelen, TCP_SND_QUEUELEN);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "too long queue (queuelen=%u max=%u)", pcb->snd_queuelen, TCP_SND_QUEUELEN);
|
||||
pcb->ctx->stats.write_fails++;
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
@@ -209,7 +209,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, uint16_t len, uint8_t apiflags)
|
||||
if (apiflags & TCP_WRITE_FLAG_COPY) {
|
||||
concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, NULL, pcb, apiflags, 1);
|
||||
if (concat_p == NULL) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: could not allocate memory for copy");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "could not allocate memory for copy");
|
||||
goto memerr;
|
||||
}
|
||||
TCP_DATA_COPY2(concat_p->payload, (const uint8_t *)arg + pos, seglen, 0, 0);
|
||||
@@ -222,7 +222,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, uint16_t len, uint8_t apiflags)
|
||||
} else {
|
||||
concat_p = pbuf_alloc(PBUF_RAW, seglen);
|
||||
if (concat_p == NULL) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: could not allocate memory for zero-copy pbuf");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "could not allocate memory for zero-copy pbuf");
|
||||
goto memerr;
|
||||
}
|
||||
if (pbuf_take(concat_p, (const uint8_t *)arg + pos, seglen) != 0) {
|
||||
@@ -248,7 +248,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, uint16_t len, uint8_t apiflags)
|
||||
if (apiflags & TCP_WRITE_FLAG_COPY) {
|
||||
p = tcp_pbuf_prealloc(PBUF_TRANSPORT, (uint16_t)(seglen + optlen), mss_local, NULL, pcb, apiflags, (queue == NULL));
|
||||
if (p == NULL) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: could not allocate memory for pbuf copy");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "could not allocate memory for pbuf copy");
|
||||
goto memerr;
|
||||
}
|
||||
memcpy((uint8_t *)p->payload + optlen, (const uint8_t *)arg + pos, seglen);
|
||||
@@ -256,7 +256,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, uint16_t len, uint8_t apiflags)
|
||||
struct pbuf *p2;
|
||||
p2 = pbuf_alloc(PBUF_TRANSPORT, seglen);
|
||||
if (p2 == NULL) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: could not allocate memory for zero-copy pbuf");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "could not allocate memory for zero-copy pbuf");
|
||||
goto memerr;
|
||||
}
|
||||
if (pbuf_take(p2, (const uint8_t *)arg + pos, seglen) != 0) {
|
||||
@@ -266,7 +266,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, uint16_t len, uint8_t apiflags)
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, optlen);
|
||||
if (p == NULL) {
|
||||
pbuf_free(p2);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: could not allocate memory for header pbuf");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "could not allocate memory for header pbuf");
|
||||
goto memerr;
|
||||
}
|
||||
pbuf_cat(p, p2);
|
||||
@@ -275,7 +275,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, uint16_t len, uint8_t apiflags)
|
||||
queuelen = (uint16_t)(queuelen + pbuf_clen(p));
|
||||
|
||||
if (queuelen > TCP_SND_QUEUELEN) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_write: queue too long");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "queue too long");
|
||||
pbuf_free(p);
|
||||
goto memerr;
|
||||
}
|
||||
@@ -358,13 +358,13 @@ tcp_split_unsent_seg(struct tcp_pcb *pcb, uint16_t split)
|
||||
p = pbuf_alloc(PBUF_TRANSPORT, (uint16_t)(remainder + optlen));
|
||||
if (p == NULL) {
|
||||
pcb->ctx->stats.split_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_split_unsent_seg: could not allocate pbuf");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "could not allocate pbuf");
|
||||
goto memerr;
|
||||
}
|
||||
|
||||
offset = (uint16_t)(useg->p->tot_len - useg->len + split);
|
||||
if (pbuf_copy_partial(useg->p, (uint8_t *)p->payload + optlen, remainder, offset) != remainder) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_split_unsent_seg: could not copy pbuf remainder");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "could not copy pbuf remainder");
|
||||
goto memerr;
|
||||
}
|
||||
|
||||
@@ -377,7 +377,7 @@ tcp_split_unsent_seg(struct tcp_pcb *pcb, uint16_t split)
|
||||
ntohl(useg->tcphdr->seqno) + split, optflags);
|
||||
if (seg == NULL) {
|
||||
p = NULL;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_split_unsent_seg: could not create new segment");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "could not create new segment");
|
||||
goto memerr;
|
||||
}
|
||||
|
||||
@@ -441,7 +441,7 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, uint8_t flags)
|
||||
p = tcp_pbuf_alloc(pcb, PBUF_TRANSPORT, optlen);
|
||||
if (p == NULL) {
|
||||
pcb->ctx->stats.enq_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_enqueue_flags: pbuf_alloc failed (optlen=%u)", optlen);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "pbuf_alloc failed (optlen=%u)", optlen);
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
return LERR_MEM;
|
||||
}
|
||||
@@ -449,7 +449,7 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, uint8_t flags)
|
||||
seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags);
|
||||
if (seg == NULL) {
|
||||
pcb->ctx->stats.enq_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_enqueue_flags: tcp_create_segment failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_create_segment failed");
|
||||
tcp_set_flags(pcb, TF_NAGLEMEMERR);
|
||||
return LERR_MEM;
|
||||
}
|
||||
@@ -946,7 +946,7 @@ tcp_rst(struct tcp_pcb *pcb, uint32_t seqno, uint32_t ackno,
|
||||
local_port, remote_port, TCP_RST | TCP_ACK, wnd);
|
||||
if (p == NULL) {
|
||||
if (pcb) pcb->ctx->stats.rst_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_rst: could not allocate memory for pbuf");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "could not allocate memory for pbuf");
|
||||
return;
|
||||
}
|
||||
tcp_output_fill_options(pcb, p, 0, 0);
|
||||
@@ -971,7 +971,7 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
|
||||
if (p == NULL) {
|
||||
pcb->ctx->stats.ack_fails++;
|
||||
tcp_set_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_output: (ACK) could not allocate pbuf");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "(ACK) could not allocate pbuf");
|
||||
return LERR_BUF;
|
||||
}
|
||||
tcp_output_fill_options(pcb, p, optflags, 0);
|
||||
@@ -1025,7 +1025,7 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
|
||||
p = tcp_output_alloc_header(pcb, optlen, len, seg->tcphdr->seqno);
|
||||
if (p == NULL) {
|
||||
pcb->ctx->stats.probe_fails++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_zero_window_probe: no memory for pbuf");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ALL, "no memory for pbuf");
|
||||
return LERR_MEM;
|
||||
}
|
||||
tcphdr = (struct tcp_hdr *)p->payload;
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef LWIP_TCP_PRIV_H
|
||||
#define LWIP_TCP_PRIV_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include "lwip_tcp.h"
|
||||
#include "lwip_tcp_opts.h"
|
||||
#include <string.h>
|
||||
@@ -27,10 +32,7 @@
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#else
|
||||
#define htons(x) ((((x) & 0xFF) << 8) | (((x) >> 8) & 0xFF))
|
||||
#define ntohs(x) htons(x)
|
||||
#define htonl(x) ((((x) & 0xFF) << 24) | (((x) & 0xFF00) << 8) | (((x) >> 8) & 0xFF00) | (((x) >> 24) & 0xFF))
|
||||
#define ntohl(x) htonl(x)
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
// TCP header (wire format)
|
||||
@@ -138,7 +140,7 @@ struct tcp_seg {
|
||||
do { if((pcb)->connected != NULL) (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); else (ret) = LERR_OK; } while(0)
|
||||
|
||||
#define TCP_EVENT_POLL(pcb,ret) \
|
||||
do { if((pcb)->poll != NULL) (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); else (ret) = LERR_OK; } while(0)
|
||||
do { if((pcb)->pollfn != NULL) (ret) = (pcb)->pollfn((pcb)->callback_arg,(pcb)); else (ret) = LERR_OK; } while(0)
|
||||
|
||||
#define TCP_EVENT_ERR(last_state,errf,arg,err) \
|
||||
do { (void)(last_state); if((errf) != NULL) (errf)((arg),(err)); } while(0)
|
||||
@@ -248,4 +250,8 @@ void lwip_tcp_stats_clear(struct lwip_tcp_ctx *ctx);
|
||||
#define TCP_REG_ACTIVE(ctx, npcb) TCP_REG(&(ctx)->active_pcbs, npcb)
|
||||
#define TCP_RMV_ACTIVE(ctx, npcb) TCP_RMV(&(ctx)->active_pcbs, npcb)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+13
-13
@@ -10,7 +10,7 @@
|
||||
#include "etcp_router.h"
|
||||
#include "etcp.h"
|
||||
#include "secure_channel.h"
|
||||
#include "route_bgp.h"
|
||||
#include "topo_group.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
@@ -91,19 +91,19 @@ int msg_transport_init(struct msg_transport** out,
|
||||
uint32_t max_clients,
|
||||
uint8_t svc_id) {
|
||||
if (!out || !instance || !ua || !bind_addr) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MSGTRANSPORT, "msg_transport_init: invalid args out=%p inst=%p ua=%p addr=%p",
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MSGTRANSPORT, "invalid args out=%p inst=%p ua=%p addr=%p",
|
||||
(void*)out, (void*)instance, (void*)ua, (void*)bind_addr);
|
||||
return -1;
|
||||
}
|
||||
if (bind_addr->ss_family == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MSGTRANSPORT, "msg_transport_init: bind_addr has no family, skipping");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MSGTRANSPORT, "bind_addr has no family, skipping");
|
||||
*out = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct msg_transport* t = u_calloc(1, sizeof(*t));
|
||||
if (!t) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MSGTRANSPORT, "msg_transport_init: u_calloc failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MSGTRANSPORT, "u_calloc failed");
|
||||
return -1;
|
||||
}
|
||||
t->instance = instance;
|
||||
@@ -457,12 +457,12 @@ static void handle_client_data(struct msg_transport* t, struct msg_client* clien
|
||||
|
||||
case MSG_CTL_GET_PEER_INFO: {
|
||||
uint64_t peer_id = hdr->id;
|
||||
struct ROUTE_BGP* bgp = t->instance->bgp;
|
||||
if (!bgp) {
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(t->instance->topo_groups);
|
||||
if (!group) {
|
||||
send_error(client, MSG_ERR_PEER_NOT_FOUND, "BGP not initialized");
|
||||
break;
|
||||
}
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(bgp, peer_id);
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(group, peer_id);
|
||||
if (!nq) {
|
||||
send_error(client, MSG_ERR_PEER_NOT_FOUND, "peer not found");
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MSGTRANSPORT, "GET_PEER_INFO: peer=%016llx not found",
|
||||
@@ -470,8 +470,8 @@ static void handle_client_data(struct msg_transport* t, struct msg_client* clien
|
||||
break;
|
||||
}
|
||||
uint8_t buf[SC_PUBKEY_SIZE * 2];
|
||||
memcpy(buf, nq->node.public_key, SC_PUBKEY_SIZE);
|
||||
memcpy(buf + SC_PUBKEY_SIZE, nq->node.ed25519_public_key, SC_PUBKEY_SIZE);
|
||||
memcpy(buf, nq->node->public_key, SC_PUBKEY_SIZE);
|
||||
memcpy(buf + SC_PUBKEY_SIZE, nq->node->ed25519_public_key, SC_PUBKEY_SIZE);
|
||||
send_response(client, MSG_RSP_PEER_INFO, peer_id, buf, SC_PUBKEY_SIZE * 2);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_MSGTRANSPORT, "GET_PEER_INFO: peer=%016llx sent pubkey+edpub",
|
||||
(unsigned long long)peer_id);
|
||||
@@ -544,7 +544,7 @@ static void on_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
|
||||
struct msg_transport* t = inst->msg_t;
|
||||
if (!t) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MSGTRANSPORT, "on_etcp_recv: msg_transport not initialized on instance");
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MSGTRANSPORT, "msg_transport not initialized on instance");
|
||||
queue_dgram_free(entry); queue_entry_free(entry); return;
|
||||
}
|
||||
|
||||
@@ -552,7 +552,7 @@ static void on_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
size_t payload_len = entry->len - 1;
|
||||
uint64_t src_node_id = conn->peer_node_id;
|
||||
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_MSGTRANSPORT, "on_etcp_recv: from %016llx len=%zu",
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_MSGTRANSPORT, "from %016llx len=%zu",
|
||||
(unsigned long long)src_node_id, payload_len);
|
||||
|
||||
/* Ищем клиента привязанного к этому пиру */
|
||||
@@ -563,7 +563,7 @@ static void on_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
}
|
||||
|
||||
if (!client) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MSGTRANSPORT, "on_etcp_recv: no client bound to %016llx, dropping",
|
||||
DEBUG_WARN(DEBUG_CATEGORY_MSGTRANSPORT, "no client bound to %016llx, dropping",
|
||||
(unsigned long long)src_node_id);
|
||||
t->counters.drops_no_client++;
|
||||
queue_dgram_free(entry);
|
||||
@@ -577,7 +577,7 @@ static void on_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
size_t total = MSG_HDR_SIZE + payload_len;
|
||||
uint8_t* buf = u_malloc(total);
|
||||
if (!buf) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MSGTRANSPORT, "on_etcp_recv: u_malloc failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_MSGTRANSPORT, "u_malloc failed");
|
||||
queue_dgram_free(entry); queue_entry_free(entry); return;
|
||||
}
|
||||
struct msg_header* hdr = (struct msg_header*)buf;
|
||||
|
||||
+12
-12
@@ -20,7 +20,7 @@ static ip_str_t ip_host_to_str(uint32_t ip_host) {
|
||||
|
||||
// ==================== Callbacks ====================
|
||||
|
||||
// CLIENT: NAT TUN output → encapsulate in ETCP_ID_NAT → send to provider via etcp_router
|
||||
// CLIENT: NAT TUN output → encapsulate in ETCP_RT_ID_NAT → send to provider via etcp_router
|
||||
static void nat_transport_client_tun_out_cb(struct ll_queue* q, void* arg) {
|
||||
struct UTUN_INSTANCE* inst = (struct UTUN_INSTANCE*)arg;
|
||||
if (!inst) { queue_resume_callback(q); return; }
|
||||
@@ -35,7 +35,7 @@ static void nat_transport_client_tun_out_cb(struct ll_queue* q, void* arg) {
|
||||
uint8_t* new_dgram = u_malloc(total_len);
|
||||
if (!new_dgram) { queue_dgram_free(pkt); queue_entry_free(pkt); queue_resume_callback(q); return; }
|
||||
|
||||
new_dgram[0] = ETCP_ID_NAT;
|
||||
new_dgram[0] = ETCP_RT_ID_NAT;
|
||||
memcpy(new_dgram + 1, &tr->self_node_id, 8);
|
||||
memcpy(new_dgram + 9, pkt->dgram + 1, ip_len);
|
||||
|
||||
@@ -51,7 +51,7 @@ static void nat_transport_client_tun_out_cb(struct ll_queue* q, void* arg) {
|
||||
if (ret != 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT client: etcp_route_send to provider %016llx failed",
|
||||
(unsigned long long)tr->nat_via_node_id);
|
||||
queue_entry_free(new_entry); queue_dgram_free(new_entry);
|
||||
queue_dgram_free(new_entry); queue_entry_free(new_entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ static void nat_transport_provider_tun_out_cb(struct ll_queue* q, void* arg) {
|
||||
uint8_t* new_dgram = u_malloc(total_len);
|
||||
if (!new_dgram) { queue_dgram_free(pkt); queue_entry_free(pkt); queue_resume_callback(q); return; }
|
||||
|
||||
new_dgram[0] = ETCP_ID_NAT;
|
||||
new_dgram[0] = ETCP_RT_ID_NAT;
|
||||
memcpy(new_dgram + 1, &inst->nat_tr.self_node_id, 8);
|
||||
memcpy(new_dgram + 9, pkt->dgram + 1, ip_len);
|
||||
|
||||
@@ -94,21 +94,21 @@ static void nat_transport_provider_tun_out_cb(struct ll_queue* q, void* arg) {
|
||||
if (send_ret != 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_NAT, "NAT provider: etcp_route_send back to node %016llx failed",
|
||||
(unsigned long long)entry->src_node_id);
|
||||
queue_entry_free(new_entry); queue_dgram_free(new_entry);
|
||||
queue_dgram_free(new_entry); queue_entry_free(new_entry);
|
||||
}
|
||||
}
|
||||
|
||||
// ETCP_ID_NAT receive via etcp_router: CLIENT gets response, PROVIDER gets request
|
||||
// ETCP_RT_ID_NAT receive via etcp_router: CLIENT gets response, PROVIDER gets request
|
||||
static void nat_transport_etcp_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (!conn || !entry || !entry->dgram || entry->len < NAT_SVC_HDR_SIZE) {
|
||||
if (entry) { queue_entry_free(entry); queue_dgram_free(entry); }
|
||||
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
|
||||
return;
|
||||
}
|
||||
struct UTUN_INSTANCE* inst = conn->instance;
|
||||
if (!inst) { queue_entry_free(entry); queue_dgram_free(entry); return; }
|
||||
if (!inst) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
struct nat_transport_ctx* tr = &inst->nat_tr;
|
||||
struct eim_nat_ctx* ctx = &inst->nat;
|
||||
if (!ctx->initialized) { queue_entry_free(entry); queue_dgram_free(entry); return; }
|
||||
if (!ctx->initialized) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
|
||||
uint64_t src_node_id;
|
||||
memcpy(&src_node_id, entry->dgram + 1, 8);
|
||||
@@ -173,8 +173,8 @@ int nat_transport_init(struct UTUN_INSTANCE* inst) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (etcp_router_bind(inst, ETCP_ID_NAT, nat_transport_etcp_recv_cb) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Failed to bind ETCP_ID_NAT via etcp_router");
|
||||
if (etcp_router_bind(inst, ETCP_RT_ID_NAT, nat_transport_etcp_recv_cb) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NAT, "Failed to bind ETCP_RT_ID_NAT via etcp_router");
|
||||
tun_close(tr->nat_tun); tr->nat_tun = NULL;
|
||||
eim_nat_destroy_ctx(&inst->nat);
|
||||
return -1;
|
||||
@@ -200,7 +200,7 @@ void nat_transport_destroy(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst || !inst->nat_tr.initialized) return;
|
||||
struct nat_transport_ctx* tr = &inst->nat_tr;
|
||||
|
||||
etcp_router_unbind(inst, ETCP_ID_NAT);
|
||||
etcp_router_unbind(inst, ETCP_RT_ID_NAT);
|
||||
|
||||
if (tr->nat_tun) { tun_close(tr->nat_tun); tr->nat_tun = NULL; }
|
||||
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef NAT_TRANSPORT_H
|
||||
#define NAT_TRANSPORT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct tun_if;
|
||||
@@ -19,4 +24,8 @@ struct UTUN_INSTANCE;
|
||||
int nat_transport_init(struct UTUN_INSTANCE* inst);
|
||||
void nat_transport_destroy(struct UTUN_INSTANCE* inst);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,6 +1,19 @@
|
||||
#ifndef PACKET_DUMP_H
|
||||
#define PACKET_DUMP_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "../lib/platform_compat.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char* dump_ip_packet_to_buffer(const uint8_t* data, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // PACKET_DUMP_H
|
||||
+20
-17
@@ -25,12 +25,12 @@ struct PKTNORM* pn_init(struct ETCP_CONN* etcp) {
|
||||
if (!etcp) return NULL;
|
||||
|
||||
if (etcp->mtu<200) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "pn_init: ETCP MTU error=%d", etcp->mtu);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "ETCP MTU error=%d", etcp->mtu);
|
||||
}
|
||||
|
||||
struct PKTNORM* pn = u_calloc(1, sizeof(struct PKTNORM));
|
||||
if (!pn) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "pn_init: calloc failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "calloc failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -42,21 +42,23 @@ struct PKTNORM* pn_init(struct ETCP_CONN* etcp) {
|
||||
pn->frag_size = etcp->mtu - ACK_REZERV - UDP_HDR_SIZE - UDP_SC_HDR_SIZE;
|
||||
int min_frag = UDP_HDR_SIZE + UDP_SC_HDR_SIZE + ACK_REZERV;
|
||||
if (etcp->mtu < min_frag || pn->frag_size > etcp->mtu) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "pn_init: MTU %d too small (min %d)", etcp->mtu, min_frag);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "MTU %d too small (min %d)", etcp->mtu, min_frag);
|
||||
pn->frag_size = etcp->mtu;
|
||||
}
|
||||
pn->tx_wait_time = 10;
|
||||
|
||||
pn->input = queue_new(pn->ua, 0, 0, 0, "pn_input"); // No hash needed
|
||||
if (!pn->input) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "pn_init: queue_new(input) failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "queue_new(input) failed");
|
||||
pn_deinit(pn);
|
||||
return NULL;
|
||||
}
|
||||
queue_set_threshold(pn->input, 0, 0); // транзитные очереди: ждать полного освобождения
|
||||
queue_set_waiter_defer(pn->input, 1); // defer чтобы избежать рекурсии drain_cb
|
||||
|
||||
pn->output = queue_new(pn->ua, 0, 0, 0, "pn_output"); // No hash needed
|
||||
if (!pn->output) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "pn_init: queue_new(output) failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "queue_new(output) failed");
|
||||
pn_deinit(pn);
|
||||
return NULL;
|
||||
}
|
||||
@@ -187,7 +189,7 @@ void pn_packer_send(struct PKTNORM* pn, uint8_t* data, uint16_t len) {
|
||||
struct ll_entry* entry = ll_alloc_lldgram(len);
|
||||
if (!entry) {
|
||||
pn->alloc_errors++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "pn_packer_send: ll_alloc_lldgram failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "ll_alloc_lldgram failed");
|
||||
return;
|
||||
}
|
||||
memcpy(entry->dgram, data, len);
|
||||
@@ -205,7 +207,7 @@ void pn_packer_send(struct PKTNORM* pn, uint8_t* data, uint16_t len) {
|
||||
pn->in_total_pkts++;
|
||||
pn->in_total_bytes += len;
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_NORMALIZER, "PUT to input end");
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "pn_packer_send: queue_data_put returned %d, input count=%d", ret, queue_entry_count(pn->input));
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "queue_data_put returned %d, input count=%d", ret, queue_entry_count(pn->input));
|
||||
}
|
||||
|
||||
|
||||
@@ -223,11 +225,11 @@ static void pn_send_to_etcp(struct PKTNORM* pn) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_NORMALIZER, "");
|
||||
if (!pn || !pn->data || pn->data_ptr == 0) return;
|
||||
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "pn_packer: pn_send_to_etcp");
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "pn_send_to_etcp");
|
||||
// Allocate ETCP_FRAGMENT from io_pool
|
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(pn->etcp->io_pool);
|
||||
if (!frag) {// drop data
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "pn_packer: send to etcp alloc error");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "send to etcp alloc error");
|
||||
pn->alloc_errors++;
|
||||
pn->data_ptr = 0;
|
||||
return;
|
||||
@@ -260,14 +262,14 @@ static void pn_buf_renew(struct PKTNORM* pn) {
|
||||
pn->data = memory_pool_alloc(pn->etcp->instance->data_pool);
|
||||
if (!pn->data) {
|
||||
pn->alloc_errors++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "pn_buf_renew: memory_pool_alloc failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_NORMALIZER, "memory_pool_alloc failed");
|
||||
return;
|
||||
}
|
||||
int size=pn->etcp->instance->data_pool->object_size;
|
||||
if (size>pn->frag_size) size=pn->frag_size;
|
||||
pn->data_size = size;
|
||||
pn->data_ptr=0;
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "pn_packer: new bufer size=%d bytes",size);
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "new bufer size=%d bytes",size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +291,7 @@ static void etcp_input_ready_cb(struct ll_queue* q, void* arg) {
|
||||
if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_DUMP)) log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_DUMP, "->NORM", in_dgram->dgram, in_dgram->len);
|
||||
|
||||
pn_buf_renew(pn);
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "pn_packer: new pkt hdrpos=%d",pn->data_ptr);
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "new pkt hdrpos=%d",pn->data_ptr);
|
||||
if (!pn->data) goto exit; // Allocation failed
|
||||
pn->data[pn->data_ptr++] = in_dgram->len & 0xFF;
|
||||
pn->data[pn->data_ptr++] = (in_dgram->len >> 8) & 0xFF;
|
||||
@@ -299,7 +301,7 @@ static void etcp_input_ready_cb(struct ll_queue* q, void* arg) {
|
||||
int remain = pn->data_size - pn->data_ptr;
|
||||
int avail = in_dgram->len - in_ptr;
|
||||
if (avail < remain) remain = avail;
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "pn_packer: copy %d bytes (in_ptr=%d, out_ptr=%d)",remain, in_ptr, pn->data_ptr);
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "copy %d bytes (in_ptr=%d, out_ptr=%d)",remain, in_ptr, pn->data_ptr);
|
||||
memcpy(pn->data + pn->data_ptr, in_dgram->dgram + in_ptr, remain);
|
||||
pn->data_ptr += remain;
|
||||
in_ptr += remain;
|
||||
@@ -366,7 +368,7 @@ static void pn_unpacker_cb(struct ll_queue* q, void* arg) {
|
||||
break;
|
||||
}
|
||||
uint16_t part_size = payload[ptr] | (payload[ptr + 1] << 8);
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "pn_unpacker: new fragment pkt_len=%d (at %d)", part_size, ptr);
|
||||
// DEBUG_INFO(DEBUG_CATEGORY_NORMALIZER, "new fragment pkt_len=%d (at %d)", part_size, ptr);
|
||||
ptr += 2;
|
||||
|
||||
if (part_size<1 || part_size>16384) {
|
||||
@@ -394,11 +396,12 @@ static void pn_unpacker_cb(struct ll_queue* q, void* arg) {
|
||||
ptr += cp;
|
||||
|
||||
if (pn->recvpart->len == pn->recvpart->memlen) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_NORMALIZER, "unpacked dgram (size=%d)", pn->recvpart->len);
|
||||
if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_DUMP)) log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_DUMP, "NORM->", pn->recvpart->dgram, pn->recvpart->len);
|
||||
uint32_t recv_len = pn->recvpart->len;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_NORMALIZER, "unpacked dgram (size=%d)", recv_len);
|
||||
if (debug_should_output(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_DUMP)) log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_DUMP, "NORM->", pn->recvpart->dgram, recv_len);
|
||||
queue_data_put(pn->output, pn->recvpart);
|
||||
pn->out_total_pkts++;
|
||||
pn->out_total_bytes += pn->recvpart->len;
|
||||
pn->out_total_bytes += recv_len;
|
||||
pn->recvpart = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef PKT_NORMALIZER_H
|
||||
#define PKT_NORMALIZER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include <stdint.h>
|
||||
@@ -67,4 +72,8 @@ void pn_unpacker_reset_state(struct PKTNORM* pn);
|
||||
// создаёт malloc data, копирует, помещает в input.
|
||||
//void pn_packer_send(struct PKTNORM* pn, uint8_t* data, uint16_t len);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // PKT_NORMALIZER_H
|
||||
+30
-7
@@ -19,6 +19,29 @@
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#else
|
||||
#define ICMP_MINLEN 8
|
||||
#define ICMP_ECHO 8
|
||||
#define ICMP_ECHOREPLY 0
|
||||
struct icmp {
|
||||
uint8_t icmp_type;
|
||||
uint8_t icmp_code;
|
||||
uint16_t icmp_cksum;
|
||||
uint16_t icmp_id;
|
||||
uint16_t icmp_seq;
|
||||
uint8_t icmp_data[1];
|
||||
};
|
||||
struct ip {
|
||||
uint8_t ip_hl:4, ip_v:4;
|
||||
uint8_t ip_tos;
|
||||
uint16_t ip_len;
|
||||
uint16_t ip_id;
|
||||
uint16_t ip_off;
|
||||
uint8_t ip_ttl;
|
||||
uint8_t ip_p;
|
||||
uint16_t ip_sum;
|
||||
struct in_addr ip_src, ip_dst;
|
||||
};
|
||||
#endif
|
||||
|
||||
#define ICMP_DEF_TTL 64
|
||||
@@ -111,7 +134,7 @@ static void raw_read_cb(socket_t sock, void* arg) {
|
||||
if (!e) return;
|
||||
e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len);
|
||||
if (!e->dgram) { queue_entry_free(e); return; }
|
||||
e->dgram[0] = ETCP_ID_ICMP_PROXY;
|
||||
e->dgram[0] = ETCP_RT_ID_ICMP_PROXY;
|
||||
e->dgram[1] = ICMP_PROXY_SUBCMD_REPLY;
|
||||
memcpy(e->dgram + 2, &r->client_node_id, 8);
|
||||
memcpy(e->dgram + 10, &r->dst_ip, 4);
|
||||
@@ -149,7 +172,7 @@ static void exit_handle_request(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
if (e) {
|
||||
e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len);
|
||||
if (e->dgram) {
|
||||
e->dgram[0] = ETCP_ID_ICMP_PROXY;
|
||||
e->dgram[0] = ETCP_RT_ID_ICMP_PROXY;
|
||||
e->dgram[1] = ICMP_PROXY_SUBCMD_REPLY;
|
||||
memcpy(e->dgram + 2, &client_node_id, 8);
|
||||
memcpy(e->dgram + 10, &dst_ip, 4);
|
||||
@@ -173,7 +196,7 @@ drop:
|
||||
// Сторона клиента: принять REPLY, доставить echo ответ в TUN
|
||||
// ====================================================================
|
||||
static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (entry->len < ICMP_PROXY_HDR_SIZE + 1) { queue_entry_free(entry); queue_dgram_free(entry); return; }
|
||||
if (entry->len < ICMP_PROXY_HDR_SIZE + 1) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
uint32_t src_ip;
|
||||
uint32_t orig_src_ip;
|
||||
uint16_t echo_id, echo_seq;
|
||||
@@ -194,7 +217,7 @@ static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
// ====================================================================
|
||||
void icmp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (!entry || !entry->dgram || entry->len < 2) {
|
||||
if (entry) { queue_entry_free(entry); queue_dgram_free(entry); }
|
||||
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
|
||||
return;
|
||||
}
|
||||
uint8_t subcmd = entry->dgram[1];
|
||||
@@ -214,7 +237,7 @@ int icmp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id,
|
||||
if (!e) return -1;
|
||||
e->dgram = u_malloc(ICMP_PROXY_HDR_SIZE + payload_len);
|
||||
if (!e->dgram) { queue_entry_free(e); return -1; }
|
||||
e->dgram[0] = ETCP_ID_ICMP_PROXY;
|
||||
e->dgram[0] = ETCP_RT_ID_ICMP_PROXY;
|
||||
e->dgram[1] = ICMP_PROXY_SUBCMD_REQUEST;
|
||||
memcpy(e->dgram + 2, &inst->node_id, 8);
|
||||
memcpy(e->dgram + 10, &dst_ip, 4);
|
||||
@@ -322,7 +345,7 @@ int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) {
|
||||
}
|
||||
}
|
||||
|
||||
etcp_router_bind(inst, ETCP_ID_ICMP_PROXY, icmp_proxy_recv_cb);
|
||||
etcp_router_bind(inst, ETCP_RT_ID_ICMP_PROXY, icmp_proxy_recv_cb);
|
||||
ctx->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "icmp_proxy initialized (exit=%d raw_sock=%d)", ctx->is_exit, (int)ctx->raw_sock);
|
||||
return 0;
|
||||
@@ -330,7 +353,7 @@ int icmp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) {
|
||||
|
||||
void icmp_proxy_destroy(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst || !g_icmp_ctx) return;
|
||||
etcp_router_unbind(inst, ETCP_ID_ICMP_PROXY);
|
||||
etcp_router_unbind(inst, ETCP_RT_ID_ICMP_PROXY);
|
||||
if (g_icmp_ctx->expire_timer) { uasync_cancel_timeout(g_icmp_ctx->ua, g_icmp_ctx->expire_timer); g_icmp_ctx->expire_timer = NULL; }
|
||||
if (g_icmp_ctx->raw_sock != SOCKET_INVALID) {
|
||||
if (g_icmp_ctx->raw_read_id) { uasync_remove_socket_t(g_icmp_ctx->ua, g_icmp_ctx->raw_sock); g_icmp_ctx->raw_read_id = NULL; }
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef ICMP_PROXY_H
|
||||
#define ICMP_PROXY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../lib/socket_compat.h"
|
||||
|
||||
@@ -62,4 +67,8 @@ int icmp_proxy_deliver_reply(struct UTUN_INSTANCE* inst,
|
||||
|
||||
void icmp_proxy_set_test_loopback(struct UTUN_INSTANCE* inst, int enabled);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // ICMP_PROXY_H
|
||||
@@ -0,0 +1,620 @@
|
||||
// socks_proxy.c — SOCKS5 / HTTP CONNECT proxy (client side)
|
||||
#include "socks_proxy.h"
|
||||
#include "tcp_proxy_server.h"
|
||||
#include "etcp.h"
|
||||
#include "etcp_api.h"
|
||||
#include "etcp_router.h"
|
||||
#include "utun_instance.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "../lib/memory_pool.h"
|
||||
#include "../lib/tcp_io.h"
|
||||
#include "../lib/mem.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
static void on_accept_cb(socket_t sock, void* arg);
|
||||
static void on_read_cb(struct ll_queue* q, void* arg);
|
||||
static void on_fin_cb(struct tcp_conn* tc, void* arg);
|
||||
static void on_error_cb(struct tcp_conn* tc, int err, void* arg);
|
||||
static void on_closed_cb(struct tcp_conn* tc, void* arg);
|
||||
static void tx_waiter_cb(struct ll_queue* q, void* arg);
|
||||
|
||||
static int send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len, int force);
|
||||
|
||||
struct listen_ctx {
|
||||
socket_t listen_sock;
|
||||
void* socket_id;
|
||||
struct UASYNC* ua;
|
||||
struct UTUN_INSTANCE* inst;
|
||||
uint64_t via_node_id;
|
||||
int is_http;
|
||||
struct socks_proxy_conn** conns;
|
||||
int* conn_count;
|
||||
uint32_t* next_stream_id;
|
||||
};
|
||||
|
||||
// ====================================================================
|
||||
// Отправка сообщений через ETCP
|
||||
// ====================================================================
|
||||
static int send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd, uint32_t sid, const uint8_t* data, size_t len, int force) {
|
||||
struct ll_entry* e = queue_entry_new(0);
|
||||
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: queue_entry_new failed subcmd=%02x sid=%08x", subcmd, sid); return -1; }
|
||||
e->dgram = u_malloc(TCP_PROXY_HDR_SIZE + len);
|
||||
if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: malloc(%zu) failed", TCP_PROXY_HDR_SIZE + len); queue_entry_free(e); return -1; }
|
||||
e->dgram[0] = ETCP_RT_ID_TCP_PROXY;
|
||||
e->dgram[1] = subcmd;
|
||||
memcpy(e->dgram + 2, &sid, 4);
|
||||
if (len > 0) memcpy(e->dgram + TCP_PROXY_HDR_SIZE, data, len);
|
||||
if (TCP_PROXY_HDR_SIZE + len > UINT16_MAX) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: msg too large len=%zu subcmd=%02x", len, subcmd);
|
||||
queue_dgram_free(e); queue_entry_free(e); return -1;
|
||||
}
|
||||
e->len = (uint16_t)(TCP_PROXY_HDR_SIZE + len);
|
||||
int ret = etcp_route_send(inst, dst, e, force);
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "SOCKS send_msg subcmd=%02x sid=%08x len=%zu force=%d → ret=%d",
|
||||
subcmd, sid, len, force, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int send_connect(struct socks_proxy_conn* c) {
|
||||
uint8_t buf[6];
|
||||
memcpy(buf, c->dest_ip, 4); memcpy(buf + 4, &c->dest_port, 2);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCKS proxy: CONNECT sid=%08x to %d.%d.%d.%d:%d via_node=%016llx %s",
|
||||
c->stream_id, c->dest_ip[0], c->dest_ip[1], c->dest_ip[2], c->dest_ip[3],
|
||||
ntohs(c->dest_port), (unsigned long long)c->via_node_id, c->is_http ? "http" : "socks");
|
||||
return send_msg(c->inst, c->via_node_id, TCP_PROXY_SUBCMD_CONNECT, c->stream_id, buf, 6, 1);
|
||||
}
|
||||
|
||||
static int send_data(struct socks_proxy_conn* c, const uint8_t* data, uint16_t len) {
|
||||
return send_msg(c->inst, c->via_node_id, TCP_PROXY_SUBCMD_DATA, c->stream_id, data, len, 0);
|
||||
}
|
||||
|
||||
static void send_close(struct socks_proxy_conn* c) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "SOCKS send CLOSE sid=%08x", c->stream_id);
|
||||
if (send_msg(c->inst, c->via_node_id, TCP_PROXY_SUBCMD_CLOSE, c->stream_id, NULL, 0, 1) < 0) c->close_pending = 1;
|
||||
else { c->close_pending = 0; c->close_sent = 1; }
|
||||
}
|
||||
|
||||
static void send_error(struct socks_proxy_conn* c) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "SOCKS send ERROR sid=%08x", c->stream_id);
|
||||
if (send_msg(c->inst, c->via_node_id, TCP_PROXY_SUBCMD_ERROR, c->stream_id, NULL, 0, 1) < 0) c->close_pending = 1;
|
||||
else { c->close_pending = 0; c->close_sent = 1; }
|
||||
}
|
||||
|
||||
static void send_fin(struct socks_proxy_conn* c) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "SOCKS send FIN sid=%08x", c->stream_id);
|
||||
send_msg(c->inst, c->via_node_id, TCP_PROXY_SUBCMD_FIN, c->stream_id, NULL, 0, 1);
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Запись ответа в TCP клиенту через write_queue
|
||||
// ====================================================================
|
||||
static int write_to_client(struct socks_proxy_conn* c, const uint8_t* data, uint16_t len) {
|
||||
struct ll_entry* e = queue_entry_new_from_pool(c->tc->entry_pool);
|
||||
uint8_t* buf = memory_pool_alloc(c->tc->data_pool);
|
||||
if (!e || !buf) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: write_to_client alloc failed sid=%08x", c->stream_id);
|
||||
if (e) queue_entry_free(e); if (buf) memory_pool_free(c->tc->data_pool, buf);
|
||||
return -1;
|
||||
}
|
||||
if (len > c->tc->data_pool->object_size) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: write_to_client len=%u > pool_sz=%zu sid=%08x",
|
||||
len, c->tc->data_pool->object_size, c->stream_id);
|
||||
queue_entry_free(e); memory_pool_free(c->tc->data_pool, buf);
|
||||
return -1;
|
||||
}
|
||||
memcpy(buf, data, len);
|
||||
e->dgram = buf; e->len = len;
|
||||
queue_data_put(c->tc->write_queue, e);
|
||||
c->bytes_to_client += len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// SOCKS5 handshake
|
||||
// ====================================================================
|
||||
static void process_socks_greeting(struct socks_proxy_conn* c) {
|
||||
if (c->buf_len < 3) return;
|
||||
uint8_t ver = c->buf[0], nmethods = c->buf[1];
|
||||
if (ver != 5 || c->buf_len < (uint16_t)(2 + nmethods)) return;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "socks_proxy: greeting ver=%d nmethods=%d", ver, nmethods);
|
||||
uint8_t reply[] = { 0x05, 0x00 };
|
||||
write_to_client(c, reply, 2);
|
||||
c->buf_len = 0;
|
||||
c->state = SOCKS_STATE_REQUEST;
|
||||
}
|
||||
|
||||
static void process_socks_request(struct socks_proxy_conn* c) {
|
||||
if (c->buf_len < 10) return;
|
||||
uint8_t ver = c->buf[0], cmd = c->buf[1], atyp = c->buf[3];
|
||||
if (ver != 5) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad ver=%d", ver); goto error; }
|
||||
if (cmd != 1) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: unsupported cmd=%d (only CONNECT supported)", cmd); goto error; }
|
||||
|
||||
uint16_t need;
|
||||
if (atyp == 1) need = 10; // IPv4: 4+2=6 more bytes
|
||||
else if (atyp == 3) { // domain: 1+len+2
|
||||
if (c->buf_len < 5) return;
|
||||
need = (uint16_t)(5 + c->buf[4] + 2);
|
||||
}
|
||||
else if (atyp == 4) need = 22; // IPv6: 16+2=18 more
|
||||
else { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: unsupported atyp=%d", atyp); goto error; }
|
||||
|
||||
if (c->buf_len < need) return;
|
||||
|
||||
if (atyp == 1) {
|
||||
memcpy(c->dest_ip, c->buf + 4, 4);
|
||||
memcpy(&c->dest_port, c->buf + 8, 2);
|
||||
} else if (atyp == 4) {
|
||||
// Извлекаем первые 4 байта IPv6 в dest_ip (для упрощения: IPv6 mapped IPv4 или реальный IPv6)
|
||||
memcpy(c->dest_ip, c->buf + 12, 4);
|
||||
memcpy(&c->dest_port, c->buf + 20, 2);
|
||||
// TODO: proper IPv6 support
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: IPv6 unsupported, using last 4 bytes of addr");
|
||||
} else { // atyp == 3 (domain)
|
||||
uint8_t dlen = c->buf[4];
|
||||
char domain[256]; memcpy(domain, c->buf + 5, dlen); domain[dlen] = '\0';
|
||||
memcpy(&c->dest_port, c->buf + 5 + dlen, 2);
|
||||
// resolve domain
|
||||
struct hostent* he = gethostbyname(domain);
|
||||
if (!he || he->h_addrtype != AF_INET) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: DNS failed for %s", domain);
|
||||
goto error;
|
||||
}
|
||||
memcpy(c->dest_ip, he->h_addr, 4);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: resolved %s → %d.%d.%d.%d:%d",
|
||||
domain, c->dest_ip[0], c->dest_ip[1], c->dest_ip[2], c->dest_ip[3], ntohs(c->dest_port));
|
||||
}
|
||||
|
||||
c->buf_len = 0;
|
||||
c->state = SOCKS_STATE_RELAY; // relay immediately, no waiting for first DATA
|
||||
uint8_t reply[] = { 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
write_to_client(c, reply, 10);
|
||||
if (send_connect(c) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: send_connect failed sid=%08x", c->stream_id);
|
||||
tcp_conn_push_close(c->tc);
|
||||
}
|
||||
return;
|
||||
|
||||
error: {
|
||||
uint8_t err_reply[] = { 0x05, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
write_to_client(c, err_reply, 10);
|
||||
tcp_conn_push_close(c->tc);
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// HTTP CONNECT / HTTP proxy handshake
|
||||
// ====================================================================
|
||||
static void process_http_request(struct socks_proxy_conn* c) {
|
||||
char* line_end = memmem(c->buf, c->buf_len, "\r\n", 2);
|
||||
if (!line_end) return;
|
||||
|
||||
uint16_t line_len = (uint16_t)((uint8_t*)line_end - c->buf);
|
||||
if (line_len < 8) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: http request too short"); goto error; }
|
||||
|
||||
char line[512]; if (line_len > sizeof(line) - 1) line_len = sizeof(line) - 1;
|
||||
memcpy(line, c->buf, line_len); line[line_len] = '\0';
|
||||
|
||||
// ===== CONNECT =====
|
||||
if (strncmp(line, "CONNECT ", 8) == 0) {
|
||||
char host_port[256];
|
||||
if (sscanf(line, "CONNECT %255s HTTP/", host_port) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad http connect: %s", line);
|
||||
goto error;
|
||||
}
|
||||
|
||||
char* colon = strrchr(host_port, ':');
|
||||
if (!colon) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: no port in %s", host_port); goto error; }
|
||||
*colon = '\0'; int port = atoi(colon + 1);
|
||||
if (port <= 0 || port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad port %d", port); goto error; }
|
||||
|
||||
struct hostent* he = gethostbyname(host_port);
|
||||
if (!he || he->h_addrtype != AF_INET) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: DNS failed for %s", host_port);
|
||||
uint8_t resp[] = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
|
||||
write_to_client(c, resp, (uint16_t)strlen((char*)resp)); tcp_conn_push_close(c->tc); return;
|
||||
}
|
||||
memcpy(c->dest_ip, he->h_addr, 4); c->dest_port = htons((uint16_t)port);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: HTTP CONNECT %s → %d.%d.%d.%d:%d",
|
||||
host_port, c->dest_ip[0], c->dest_ip[1], c->dest_ip[2], c->dest_ip[3], port);
|
||||
|
||||
c->buf_len = 0;
|
||||
c->state = HTTP_STATE_RELAY;
|
||||
uint8_t resp[] = "HTTP/1.1 200 Connection Established\r\n\r\n";
|
||||
write_to_client(c, resp, (uint16_t)strlen((char*)resp));
|
||||
if (send_connect(c) < 0) { tcp_conn_push_close(c->tc); }
|
||||
return;
|
||||
}
|
||||
|
||||
// ===== Не-CONNECT: HTTP-прокси (GET, POST, PUT, HEAD, OPTIONS, ...) =====
|
||||
char* hdr_end = memmem(c->buf, c->buf_len, "\r\n\r\n", 4);
|
||||
if (!hdr_end) return;
|
||||
|
||||
uint16_t headers_len = (uint16_t)((uint8_t*)hdr_end - c->buf) + 4;
|
||||
|
||||
char method[16] = {0}, url[512] = {0};
|
||||
if (sscanf(line, "%15s %511s", method, url) < 2) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad http request line: %s", line);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (strncmp(url, "http://", 7) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: not an absolute http URL: %s", url);
|
||||
goto error;
|
||||
}
|
||||
|
||||
char host[256];
|
||||
int port = 80;
|
||||
const char* host_start = url + 7;
|
||||
const char* path_start = strchr(host_start, '/');
|
||||
const char* col = NULL;
|
||||
for (const char* p = host_start; (path_start ? p < path_start : *p); p++) {
|
||||
if (*p == ':') { col = p; break; }
|
||||
}
|
||||
|
||||
if (col && (!path_start || col < path_start)) {
|
||||
size_t hlen = (size_t)(col - host_start);
|
||||
if (hlen >= sizeof(host)) goto error;
|
||||
memcpy(host, host_start, hlen); host[hlen] = '\0';
|
||||
port = atoi(col + 1);
|
||||
if (port <= 0 || port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad port %d in %s", port, url); goto error; }
|
||||
} else if (path_start) {
|
||||
size_t hlen = (size_t)(path_start - host_start);
|
||||
if (hlen >= sizeof(host)) goto error;
|
||||
memcpy(host, host_start, hlen); host[hlen] = '\0';
|
||||
} else {
|
||||
size_t hlen = strlen(host_start);
|
||||
if (hlen >= sizeof(host)) goto error;
|
||||
memcpy(host, host_start, hlen); host[hlen] = '\0';
|
||||
}
|
||||
|
||||
if (host[0] == '\0') { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: empty host in %s", url); goto error; }
|
||||
if (!path_start || *path_start == '\0') path_start = "/";
|
||||
|
||||
struct hostent* he = gethostbyname(host);
|
||||
if (!he || he->h_addrtype != AF_INET) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: DNS failed for %s", host);
|
||||
uint8_t resp[] = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
|
||||
write_to_client(c, resp, (uint16_t)strlen((char*)resp)); tcp_conn_push_close(c->tc); return;
|
||||
}
|
||||
memcpy(c->dest_ip, he->h_addr, 4); c->dest_port = htons((uint16_t)port);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: HTTP %s %s:%d%s → %d.%d.%d.%d:%d",
|
||||
method, host, port, path_start,
|
||||
c->dest_ip[0], c->dest_ip[1], c->dest_ip[2], c->dest_ip[3], port);
|
||||
|
||||
if (send_connect(c) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: send_connect failed for %s", host);
|
||||
uint8_t resp[] = "HTTP/1.1 502 Bad Gateway\r\n\r\n";
|
||||
write_to_client(c, resp, (uint16_t)strlen((char*)resp)); tcp_conn_push_close(c->tc); return;
|
||||
}
|
||||
|
||||
const char* version = strstr(line, " HTTP/");
|
||||
char version_str[16] = " HTTP/1.1";
|
||||
if (version) {
|
||||
size_t vlen = strlen(version);
|
||||
if (vlen >= sizeof(version_str)) vlen = sizeof(version_str) - 1;
|
||||
memcpy(version_str, version, vlen);
|
||||
version_str[vlen] = '\0';
|
||||
}
|
||||
|
||||
uint16_t rest_off = line_len + 2;
|
||||
uint16_t rest_len = headers_len - rest_off;
|
||||
uint8_t hdr_buf[2048];
|
||||
int hdr_n = snprintf((char*)hdr_buf, sizeof(hdr_buf), "%s %s%s\r\n", method, path_start, version_str);
|
||||
if (hdr_n < 0 || (size_t)hdr_n + rest_len > sizeof(hdr_buf)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: header reconstruction overflow hdr_n=%d rest=%u", hdr_n, rest_len);
|
||||
goto error;
|
||||
}
|
||||
memcpy(hdr_buf + hdr_n, c->buf + rest_off, rest_len);
|
||||
uint16_t hdr_chunk = (uint16_t)hdr_n + rest_len;
|
||||
uint16_t extra = (headers_len < c->buf_len) ? (uint16_t)(c->buf_len - headers_len) : 0;
|
||||
uint16_t total = hdr_chunk + extra;
|
||||
|
||||
uint8_t* pkt = u_malloc(total);
|
||||
if (!pkt) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: tx pkt malloc=%u failed sid=%08x", total, c->stream_id); goto error; }
|
||||
memcpy(pkt, hdr_buf, hdr_chunk);
|
||||
if (extra) memcpy(pkt + hdr_chunk, c->buf + headers_len, extra);
|
||||
|
||||
int ret = send_data(c, pkt, total);
|
||||
if (ret == 0) {
|
||||
u_free(pkt);
|
||||
} else {
|
||||
c->tx_buf = pkt; c->tx_len = total;
|
||||
etcp_router_on_send_ready(c->inst, c->via_node_id, ETCP_RT_ID_TCP_PROXY, &c->tx_waiter, tx_waiter_cb, c);
|
||||
}
|
||||
|
||||
c->buf_len = 0;
|
||||
c->state = HTTP_STATE_RELAY;
|
||||
return;
|
||||
|
||||
error: {
|
||||
uint8_t resp[] = "HTTP/1.1 400 Bad Request\r\n\r\n";
|
||||
write_to_client(c, resp, (uint16_t)strlen((char*)resp)); tcp_conn_push_close(c->tc);
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// tcp_io read callback — парсинг рукопожатия + релей данных
|
||||
// ====================================================================
|
||||
static void on_read_cb(struct ll_queue* q, void* arg) {
|
||||
struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg;
|
||||
struct ll_entry* e = queue_data_get(q);
|
||||
if (!e) { queue_resume_callback(q); return; }
|
||||
|
||||
if (c->rem_closed) {
|
||||
queue_dgram_free(e); queue_entry_free(e); queue_resume_callback(q); return;
|
||||
}
|
||||
|
||||
if (c->state == SOCKS_STATE_GREETING || c->state == SOCKS_STATE_REQUEST ||
|
||||
c->state == HTTP_STATE_REQUEST) {
|
||||
// накапливаем данные для рукопожатия
|
||||
uint16_t space = sizeof(c->buf) - c->buf_len;
|
||||
if (space < e->len) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: handshake buffer overflow sid=%08x", c->stream_id);
|
||||
memory_pool_free(c->tc->data_pool, e->dgram); queue_entry_free(e);
|
||||
tcp_conn_push_close(c->tc); queue_resume_callback(q); return;
|
||||
}
|
||||
memcpy(c->buf + c->buf_len, e->dgram, e->len);
|
||||
c->buf_len += e->len;
|
||||
memory_pool_free(c->tc->data_pool, e->dgram); queue_entry_free(e);
|
||||
queue_resume_callback(q);
|
||||
|
||||
if (c->is_http) {
|
||||
process_http_request(c);
|
||||
} else {
|
||||
if (c->state == SOCKS_STATE_GREETING) process_socks_greeting(c);
|
||||
if (c->state == SOCKS_STATE_REQUEST) process_socks_request(c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// RELAY = релей данных в ETCP
|
||||
if (c->tx_buf) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: RELAY with pending tx_buf sid=%08x — drop new len=%u", c->stream_id, e->len);
|
||||
memory_pool_free(c->tc->data_pool, e->dgram); queue_entry_free(e);
|
||||
return;
|
||||
}
|
||||
int ret = send_data(c, e->dgram, e->len);
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "SOCKS PROXY SEND sid=%08x len=%u ret=%d is_http=%d", c->stream_id, e->len, ret, c->is_http);
|
||||
if (ret == 0) {
|
||||
memory_pool_free(c->tc->data_pool, e->dgram); queue_entry_free(e);
|
||||
queue_resume_callback(q);
|
||||
} else {
|
||||
c->tx_buf = u_malloc(e->len);
|
||||
if (c->tx_buf) { memcpy(c->tx_buf, e->dgram, e->len); c->tx_len = e->len; }
|
||||
else { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: tx_buf malloc=%u failed sid=%08x — drop", e->len, c->stream_id); }
|
||||
memory_pool_free(c->tc->data_pool, e->dgram); queue_entry_free(e);
|
||||
etcp_router_on_send_ready(c->inst, c->via_node_id, ETCP_RT_ID_TCP_PROXY, &c->tx_waiter, tx_waiter_cb, c);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SOCKS PROXY BP: sid=%08x tx_buf=%u waiter_reg", c->stream_id, c->tx_len);
|
||||
}
|
||||
}
|
||||
|
||||
static void tx_waiter_cb(struct ll_queue* q, void* arg) {
|
||||
(void)q;
|
||||
struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg;
|
||||
if (!c->tx_buf || c->rem_closed || c->close_sent) return;
|
||||
int ret = send_data(c, c->tx_buf, c->tx_len);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SOCKS PROXY WAKE: sid=%08x tx_buf=%u ret=%d is_http=%d", c->stream_id, c->tx_len, ret, c->is_http);
|
||||
if (ret == 0) {
|
||||
u_free(c->tx_buf); c->tx_buf = NULL; c->tx_len = 0;
|
||||
queue_resume_callback(c->tc->read_queue);
|
||||
} else {
|
||||
etcp_router_on_send_ready(c->inst, c->via_node_id, ETCP_RT_ID_TCP_PROXY, &c->tx_waiter, tx_waiter_cb, c);
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// tcp_io: FIN / Error / Closed
|
||||
// ====================================================================
|
||||
static void on_fin_cb(struct tcp_conn* tc, void* arg) {
|
||||
struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg;
|
||||
if (c->rem_closed || c->close_sent) return;
|
||||
if (!tc->fin_local && !tc->write_buf && !tc->write_queue->head) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "socks_proxy: local FIN → relay FIN sid=%08x", c->stream_id);
|
||||
send_fin(c);
|
||||
if (c->fin_remote && !c->close_sent && !c->close_pending) send_close(c);
|
||||
} else {
|
||||
// данные ещё в write_queue, отложим FIN
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "socks_proxy: local FIN deferred (wq=%d wbuf=%s) sid=%08x",
|
||||
tc->write_queue->count, tc->write_buf ? "y" : "n", c->stream_id);
|
||||
tcp_conn_set_flushed(tc, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_error_cb(struct tcp_conn* tc, int err, void* arg) {
|
||||
(void)err;
|
||||
struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: tcp error sid=%08x err=%d state=%d fm_r=%d fm_l=%d rem_cl=%d cs=%d cp=%d "
|
||||
"bytes_client=%u bytes_exit=%u wq=%d",
|
||||
c->stream_id, err, c->state, tc->fin_remote, tc->fin_local, c->rem_closed, c->close_sent, c->close_pending,
|
||||
c->bytes_to_client, c->bytes_from_exit, c->tc->write_queue->count);
|
||||
if (!c->close_sent && !c->close_pending) send_error(c);
|
||||
socks_proxy_conn_free(c);
|
||||
}
|
||||
|
||||
static void on_closed_cb(struct tcp_conn* tc, void* arg) {
|
||||
struct socks_proxy_conn* c = (struct socks_proxy_conn*)arg;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: tcp closed sid=%08x state=%d fm_r=%d fm_l=%d rem_cl=%d bytes_client=%u bytes_exit=%u",
|
||||
c->stream_id, c->state, tc->fin_remote, tc->fin_local, c->rem_closed, c->bytes_to_client, c->bytes_from_exit);
|
||||
if (!c->close_sent && !c->close_pending) send_close(c);
|
||||
socks_proxy_conn_free(c);
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Accept callback для слушающего сокета
|
||||
// ====================================================================
|
||||
static void on_accept_cb(socket_t sock, void* arg) {
|
||||
struct listen_ctx* ctx = (struct listen_ctx*)arg;
|
||||
struct sockaddr_in addr; socklen_t alen = sizeof(addr);
|
||||
socket_t csock = accept(sock, (struct sockaddr*)&addr, &alen);
|
||||
if (csock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: accept failed errno=%d", errno); return; }
|
||||
socket_set_nonblocking(csock);
|
||||
|
||||
struct socks_proxy_conn* c = u_calloc(1, sizeof(struct socks_proxy_conn));
|
||||
if (!c) { socket_close_wrapper(csock); DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: u_calloc failed"); return; }
|
||||
c->stream_id = ++(*ctx->next_stream_id);
|
||||
c->is_http = (uint8_t)ctx->is_http;
|
||||
c->ua = ctx->ua; c->inst = ctx->inst; c->via_node_id = ctx->via_node_id;
|
||||
c->state = ctx->is_http ? HTTP_STATE_REQUEST : SOCKS_STATE_GREETING;
|
||||
c->head = ctx->conns; c->count = ctx->conn_count;
|
||||
|
||||
c->tc = tcp_conn_create(ctx->ua, csock, 4096, 4096, 8, 0, 0, on_fin_cb, on_error_cb, c);
|
||||
if (!c->tc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: tcp_conn_create failed"); u_free(c); socket_close_wrapper(csock); return; }
|
||||
c->tc->on_closed = on_closed_cb;
|
||||
queue_set_callback(c->tc->read_queue, on_read_cb, c);
|
||||
queue_set_waiter_defer(c->tc->read_queue, 1);
|
||||
|
||||
c->next = *ctx->conns; *ctx->conns = c; (*ctx->conn_count)++;
|
||||
|
||||
char ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &addr.sin_addr, ip, sizeof(ip));
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: accepted %s conn from %s:%d sid=%08x total=%d",
|
||||
ctx->is_http ? "http" : "socks", ip, ntohs(addr.sin_port), c->stream_id, *ctx->conn_count);
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Публичное API для tcp_proxy_client (etcp dispatch)
|
||||
// ====================================================================
|
||||
struct socks_proxy_conn* socks_proxy_find_conn(struct socks_proxy_conn* head, uint32_t stream_id) {
|
||||
struct socks_proxy_conn* c;
|
||||
for (c = head; c; c = c->next) if (c->stream_id == stream_id) return c;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int socks_proxy_handle_etcp(struct socks_proxy_conn** head, int* count,
|
||||
uint32_t stream_id, uint8_t subcmd,
|
||||
const uint8_t* data, size_t data_len) {
|
||||
struct socks_proxy_conn* c = socks_proxy_find_conn(*head, stream_id);
|
||||
if (!c) return 0;
|
||||
|
||||
if (subcmd == TCP_PROXY_SUBCMD_DATA) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "SOCKS DATA <- sid=%08x len=%zu", stream_id, data_len);
|
||||
c->bytes_from_exit += (uint32_t)data_len;
|
||||
if (data_len > 0) {
|
||||
if (data_len > c->tc->data_pool->object_size) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: data_len=%zu > pool_sz=%zu, dropping sid=%08x",
|
||||
data_len, c->tc->data_pool->object_size, stream_id);
|
||||
return 1;
|
||||
}
|
||||
struct ll_entry* e = queue_entry_new_from_pool(c->tc->entry_pool);
|
||||
uint8_t* buf = memory_pool_alloc(c->tc->data_pool);
|
||||
if (e && buf) {
|
||||
memcpy(buf, data, data_len); e->dgram = buf; e->len = (uint16_t)data_len;
|
||||
queue_data_put(c->tc->write_queue, e);
|
||||
c->bytes_to_client += (uint32_t)data_len;
|
||||
} else {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: handle_data alloc failed sid=%08x wq=%d bytes_in=%u bytes_out=%u",
|
||||
stream_id, c->tc->write_queue->count, c->bytes_from_exit, c->bytes_to_client);
|
||||
if (e) queue_entry_free(e); if (buf) memory_pool_free(c->tc->data_pool, buf);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (subcmd == TCP_PROXY_SUBCMD_CLOSE) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: REM_CLOSED sid=%08x", stream_id);
|
||||
c->rem_closed = 1;
|
||||
etcp_router_cancel_send_ready(c->inst, c->via_node_id, ETCP_RT_ID_TCP_PROXY, &c->tx_waiter);
|
||||
tcp_conn_push_close(c->tc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (subcmd == TCP_PROXY_SUBCMD_ERROR) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: ERROR from exit sid=%08x", stream_id);
|
||||
c->rem_closed = 1;
|
||||
etcp_router_cancel_send_ready(c->inst, c->via_node_id, ETCP_RT_ID_TCP_PROXY, &c->tx_waiter);
|
||||
tcp_conn_push_close(c->tc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (subcmd == TCP_PROXY_SUBCMD_FIN) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: FIN from exit sid=%08x", stream_id);
|
||||
c->fin_remote = 1;
|
||||
tcp_conn_push_fin(c->tc);
|
||||
if (c->tc->fin_local && !c->close_sent && !c->close_pending) send_close(c);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void socks_proxy_conn_free(struct socks_proxy_conn* c) {
|
||||
if (!c) return;
|
||||
if (c->freed) return;
|
||||
c->freed = 1;
|
||||
struct socks_proxy_conn** head = c->head; int* count = c->count;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: FREE sid=%08x state=%d total=%d", c->stream_id, c->state, count ? *count : 0);
|
||||
if (c->close_pending && c->inst) {
|
||||
c->close_pending = 0;
|
||||
uint8_t subcmd = c->tc && c->tc->error ? TCP_PROXY_SUBCMD_ERROR : TCP_PROXY_SUBCMD_CLOSE;
|
||||
send_msg(c->inst, c->via_node_id, subcmd, c->stream_id, NULL, 0, 1);
|
||||
}
|
||||
if (head) { struct socks_proxy_conn** prev = head; while (*prev) { if (*prev == c) { *prev = c->next; if (count) (*count)--; break; } prev = &(*prev)->next; } }
|
||||
if (c->tx_buf) { u_free(c->tx_buf); c->tx_buf = NULL; c->tx_len = 0; }
|
||||
if (c->tc) { tcp_conn_destroy(c->tc); c->tc = NULL; }
|
||||
if (c->inst) etcp_router_waiter_cancel(c->inst, c->via_node_id, &c->tx_waiter);
|
||||
u_free(c);
|
||||
}
|
||||
|
||||
void socks_proxy_conn_free_all(struct socks_proxy_conn** head, int* count) {
|
||||
while (*head) { struct socks_proxy_conn* next = (*head)->next; socks_proxy_conn_free(*head); }
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Слушающий сокет
|
||||
// ====================================================================
|
||||
struct listen_ctx* socks_proxy_init_listen(struct UASYNC* ua, const char* addr_str,
|
||||
struct socks_proxy_conn** conns, int* count, uint32_t* next_stream_id,
|
||||
struct UTUN_INSTANCE* inst, uint64_t via_node_id, int is_http) {
|
||||
char ip[64], *colon = strrchr(addr_str, ':');
|
||||
if (!colon) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad addr '%s' (need IP:PORT)", addr_str); return NULL; }
|
||||
size_t ip_len = (size_t)(colon - addr_str);
|
||||
if (ip_len > sizeof(ip) - 1) ip_len = sizeof(ip) - 1;
|
||||
memcpy(ip, addr_str, ip_len); ip[ip_len] = '\0';
|
||||
int port = atoi(colon + 1);
|
||||
if (port <= 0 || port > 65535) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bad port %d", port); return NULL; }
|
||||
|
||||
struct listen_ctx* ctx = u_calloc(1, sizeof(struct listen_ctx));
|
||||
if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: u_calloc failed"); return NULL; }
|
||||
ctx->ua = ua; ctx->inst = inst; ctx->via_node_id = via_node_id;
|
||||
ctx->is_http = is_http; ctx->conns = conns; ctx->conn_count = count;
|
||||
ctx->next_stream_id = next_stream_id;
|
||||
|
||||
socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: socket() failed errno=%d", errno); u_free(ctx); return NULL; }
|
||||
socket_set_nonblocking(sock);
|
||||
socket_set_reuseaddr(sock, 1);
|
||||
|
||||
struct sockaddr_in addr; memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET; addr.sin_port = htons((uint16_t)port);
|
||||
if (inet_pton(AF_INET, ip, &addr.sin_addr) != 1) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: inet_pton(%s) failed", ip); socket_close_wrapper(sock); u_free(ctx); return NULL; }
|
||||
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: bind(%s:%d) failed errno=%d", ip, port, errno); socket_close_wrapper(sock); u_free(ctx); return NULL; }
|
||||
if (listen(sock, 32) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: listen() failed errno=%d", errno); socket_close_wrapper(sock); u_free(ctx); return NULL; }
|
||||
|
||||
ctx->listen_sock = sock;
|
||||
ctx->socket_id = uasync_add_socket_t(ua, sock, on_accept_cb, NULL, NULL, ctx);
|
||||
if (!ctx->socket_id) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socks_proxy: uasync_add_socket_t failed"); socket_close_wrapper(sock); u_free(ctx); return NULL; }
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: %s listening on %s:%d sock=%d",
|
||||
is_http ? "HTTP" : "SOCKS", ip, port, (int)sock);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void socks_proxy_close_listen(struct UASYNC* ua, struct listen_ctx* ctx, socket_t* sock_out) {
|
||||
if (!ctx) return;
|
||||
if (ctx->socket_id) uasync_remove_socket_t(ua, ctx->listen_sock);
|
||||
socket_close_wrapper(ctx->listen_sock);
|
||||
if (sock_out) *sock_out = SOCKET_INVALID;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "socks_proxy: listener closed sock=%d", (int)ctx->listen_sock);
|
||||
u_free(ctx);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// socks_proxy.h — SOCKS5 / HTTP CONNECT proxy (client side)
|
||||
#ifndef SOCKS_PROXY_H
|
||||
#define SOCKS_PROXY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../lib/socket_compat.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
|
||||
struct UASYNC;
|
||||
struct UTUN_INSTANCE;
|
||||
struct ll_entry;
|
||||
struct ll_queue;
|
||||
struct tcp_conn;
|
||||
|
||||
enum {
|
||||
SOCKS_STATE_GREETING = 0, // ждём SOCKS5 greeting (ver + methods)
|
||||
SOCKS_STATE_REQUEST, // ждём SOCKS5 CONNECT request
|
||||
SOCKS_STATE_CONNECTING, // отправили ETCP CONNECT, ждём ответ от exit
|
||||
SOCKS_STATE_RELAY // релей данных
|
||||
};
|
||||
|
||||
enum {
|
||||
HTTP_STATE_REQUEST = 0, // ждём "CONNECT host:port HTTP/1.1\r\n"
|
||||
HTTP_STATE_CONNECTING, // отправили ETCP CONNECT, ждём ответ от exit
|
||||
HTTP_STATE_RELAY // релей данных
|
||||
};
|
||||
|
||||
struct socks_proxy_conn {
|
||||
struct socks_proxy_conn* next;
|
||||
struct socks_proxy_conn** head; // указатель на голову списка (для удаления)
|
||||
int* count; // указатель на счётчик списка
|
||||
struct tcp_conn* tc;
|
||||
struct UASYNC* ua;
|
||||
struct UTUN_INSTANCE* inst;
|
||||
uint64_t via_node_id;
|
||||
uint32_t stream_id;
|
||||
uint8_t dest_ip[4];
|
||||
uint16_t dest_port;
|
||||
uint8_t fin_remote;
|
||||
uint8_t rem_closed;
|
||||
uint8_t close_sent;
|
||||
uint8_t close_pending;
|
||||
uint8_t is_http;
|
||||
uint8_t state;
|
||||
uint8_t freed;
|
||||
uint8_t buf[1024];
|
||||
uint16_t buf_len;
|
||||
uint8_t* tx_buf; // буфер при backpressure (retry в tx_waiter_cb)
|
||||
uint16_t tx_len;
|
||||
uint32_t bytes_to_client; // байт записано клиенту (curl) через write_queue
|
||||
uint32_t bytes_from_exit; // байт получено от exit node через ETCP DATA
|
||||
struct queue_waiter_handle tx_waiter;
|
||||
};
|
||||
|
||||
struct listen_ctx;
|
||||
|
||||
// Создать слушающий сокет для SOCKS/HTTP proxy. addr_str = "IP:PORT".
|
||||
// listen_ctx должен быть освобождён через socks_proxy_close_listen.
|
||||
// Возвращает listen_ctx при успехе, NULL при ошибке.
|
||||
struct listen_ctx* socks_proxy_init_listen(struct UASYNC* ua, const char* addr_str,
|
||||
struct socks_proxy_conn** conns, int* count, uint32_t* next_stream_id,
|
||||
struct UTUN_INSTANCE* inst, uint64_t via_node_id, int is_http);
|
||||
|
||||
// Закрыть слушающий сокет и освободить listen_ctx.
|
||||
// sock_out получает значение сокета (для последующего close).
|
||||
void socks_proxy_close_listen(struct UASYNC* ua, struct listen_ctx* ctx, socket_t* sock_out);
|
||||
|
||||
// Найти соединение по stream_id
|
||||
struct socks_proxy_conn* socks_proxy_find_conn(struct socks_proxy_conn* head, uint32_t stream_id);
|
||||
|
||||
// Обработать входящее ETCP сообщение (DATA/CLOSE/ERROR/FIN)
|
||||
// Возвращает 1 если обработано, 0 если stream_id не найден
|
||||
int socks_proxy_handle_etcp(struct socks_proxy_conn** head, int* count,
|
||||
uint32_t stream_id, uint8_t subcmd,
|
||||
const uint8_t* data, size_t data_len);
|
||||
|
||||
// Освободить одно соединение (удаляет из списка)
|
||||
void socks_proxy_conn_free(struct socks_proxy_conn* c);
|
||||
|
||||
// Освободить все соединения в списке
|
||||
void socks_proxy_conn_free_all(struct socks_proxy_conn** head, int* count);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // SOCKS_PROXY_H
|
||||
@@ -1,5 +1,6 @@
|
||||
// tcp_proxy_client.c — TCP прокси-клиент: стек lwIP TCP → ETCP → удалённый exit узел
|
||||
#include "tcp_proxy_client.h"
|
||||
#include "socks_proxy.h"
|
||||
#include "lwip_tcp/lwip_tcp.h"
|
||||
#include "lwip_tcp/lwip_tcp_priv.h"
|
||||
#include "lwip_tcp/lwip_tcp_opts.h"
|
||||
@@ -66,10 +67,10 @@ static struct ll_entry* tcp_proxy_client_entry_from_data(struct memory_pool* poo
|
||||
static int tcp_proxy_client_send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd,
|
||||
uint32_t sid, const uint8_t* data, size_t len, int force) {
|
||||
struct ll_entry* e = queue_entry_new(0);
|
||||
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_send_msg: queue_entry_new failed subcmd=%02x sid=%08x", subcmd, sid); return -1; }
|
||||
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "queue_entry_new failed subcmd=%02x sid=%08x", subcmd, sid); return -1; }
|
||||
e->dgram = u_malloc(TCP_PROXY_HDR_SIZE + len);
|
||||
if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_send_msg: malloc(%zu) failed subcmd=%02x sid=%08x", TCP_PROXY_HDR_SIZE + len, subcmd, sid); queue_entry_free(e); return -1; }
|
||||
e->dgram[0] = ETCP_ID_TCP_PROXY;
|
||||
if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "malloc(%zu) failed subcmd=%02x sid=%08x", TCP_PROXY_HDR_SIZE + len, subcmd, sid); queue_entry_free(e); return -1; }
|
||||
e->dgram[0] = ETCP_RT_ID_TCP_PROXY;
|
||||
e->dgram[1] = subcmd;
|
||||
memcpy(e->dgram + 2, &sid, 4);
|
||||
if (len > 0) memcpy(e->dgram + TCP_PROXY_HDR_SIZE, data, len);
|
||||
@@ -516,7 +517,7 @@ void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* en
|
||||
struct tcp_proxy_client* proxy = inst ? inst->tcp_proxy_client : NULL;
|
||||
|
||||
if (!entry || !entry->dgram || entry->len < TCP_PROXY_HDR_SIZE) {
|
||||
if (entry && entry->len == 9 && !conn && entry->dgram[0] == ETCP_ID_TCP_PROXY_CLIENT) {
|
||||
if (entry && entry->len == 9 && !conn && entry->dgram[0] == ETCP_RT_ID_TCP_PROXY) {
|
||||
uint64_t peer_id;
|
||||
memcpy(&peer_id, entry->dgram + 1, 8);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY CLOSE_ALL from %016llx — clearing client conns for peer",
|
||||
@@ -525,6 +526,8 @@ void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* en
|
||||
struct tcp_proxy_client_conn *pc, *next;
|
||||
for (pc = proxy->conns; pc; pc = next) { next = pc->next; tcp_proxy_client_conn_free(pc); }
|
||||
proxy->conns = NULL; proxy->conn_count = 0;
|
||||
socks_proxy_conn_free_all(&proxy->socks_conns, &proxy->socks_conn_count);
|
||||
socks_proxy_conn_free_all(&proxy->http_conns, &proxy->http_conn_count);
|
||||
}
|
||||
}
|
||||
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
|
||||
@@ -533,7 +536,20 @@ void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* en
|
||||
uint8_t subcmd = entry->dgram[1];
|
||||
uint32_t stream_id; memcpy(&stream_id, entry->dgram + 2, 4);
|
||||
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "PROXY CLIENT RECV subcmd=%02x sid=%08x pkt_len=%u", subcmd, stream_id, entry->len);
|
||||
|
||||
if (proxy) {
|
||||
size_t data_len = entry->len > TCP_PROXY_HDR_SIZE ? entry->len - TCP_PROXY_HDR_SIZE : 0;
|
||||
// Пробуем SOCKS/HTTP first (у них приоритет — могут быть без TUN)
|
||||
if (proxy->socks_enabled && socks_proxy_handle_etcp(&proxy->socks_conns, &proxy->socks_conn_count,
|
||||
stream_id, subcmd, entry->dgram + TCP_PROXY_HDR_SIZE, data_len)) {
|
||||
queue_dgram_free(entry); queue_entry_free(entry); return;
|
||||
}
|
||||
if (proxy->http_proxy_enabled && socks_proxy_handle_etcp(&proxy->http_conns, &proxy->http_conn_count,
|
||||
stream_id, subcmd, entry->dgram + TCP_PROXY_HDR_SIZE, data_len)) {
|
||||
queue_dgram_free(entry); queue_entry_free(entry); return;
|
||||
}
|
||||
// Существующие lwIP conns
|
||||
if (subcmd == TCP_PROXY_SUBCMD_DATA) { tcp_proxy_client_handle_data(proxy, conn, stream_id, entry); return; }
|
||||
if (subcmd == TCP_PROXY_SUBCMD_CLOSE) { tcp_proxy_client_handle_close(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
if (subcmd == TCP_PROXY_SUBCMD_ERROR) { tcp_proxy_client_handle_error(proxy, stream_id); queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
@@ -553,66 +569,90 @@ void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* en
|
||||
struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua,
|
||||
const char* tun_name, const char* tun_ip, int mtu, int test_mode,
|
||||
struct tcp_proxy_client_mapping_config* mappings, int mapping_count,
|
||||
uint64_t via_node_id)
|
||||
uint64_t via_node_id,
|
||||
int socks_enabled, const char* socks_addr,
|
||||
int http_proxy_enabled, const char* http_proxy_addr)
|
||||
{
|
||||
if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: ua is NULL"); return NULL; }
|
||||
if (!tun_name || !tun_ip) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: tun name/ip required"); return NULL; }
|
||||
if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "ua is NULL"); return NULL; }
|
||||
|
||||
int need_tun = tun_name && tun_name[0] && tun_ip && tun_ip[0];
|
||||
if (!need_tun && !socks_enabled && !http_proxy_enabled) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "no proxy mode enabled"); return NULL;
|
||||
}
|
||||
|
||||
struct tcp_proxy_client* p = u_calloc(1, sizeof(struct tcp_proxy_client));
|
||||
if (!p) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: u_calloc failed"); return NULL; }
|
||||
if (!p) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "u_calloc failed"); return NULL; }
|
||||
p->inst = inst; p->ua = ua; p->next_stream_id = 1;
|
||||
p->via_node_id = via_node_id;
|
||||
p->mappings = mappings; p->mapping_count = mapping_count;
|
||||
p->socks_enabled = socks_enabled;
|
||||
p->http_proxy_enabled = http_proxy_enabled;
|
||||
|
||||
p->entry_pool = memory_pool_init(sizeof(struct ll_entry), "entry_pool");
|
||||
if (!p->entry_pool) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: memory_pool_init failed"); u_free(p); return NULL; }
|
||||
if (!p->entry_pool) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "memory_pool_init failed"); u_free(p); return NULL; }
|
||||
|
||||
p->tun = tun_init_nat(ua, tun_name, tun_ip, mtu > 0 ? mtu : 1500, test_mode);
|
||||
if (!p->tun) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tcp_proxy_client: failed to create TUN %s", tun_name); memory_pool_destroy(p->entry_pool); u_free(p); return NULL; }
|
||||
queue_set_callback(p->tun->output_queue, tcp_proxy_client_tun_input, p);
|
||||
if (need_tun) {
|
||||
p->tun = tun_init_nat(ua, tun_name, tun_ip, mtu > 0 ? mtu : 1500, test_mode);
|
||||
if (!p->tun) { DEBUG_ERROR(DEBUG_CATEGORY_TUN, "tcp_proxy_client: failed to create TUN %s", tun_name); memory_pool_destroy(p->entry_pool); u_free(p); return NULL; }
|
||||
queue_set_callback(p->tun->output_queue, tcp_proxy_client_tun_input, p);
|
||||
|
||||
p->lwip = lwip_tcp_init(ua, tcp_proxy_client_output_cb, p);
|
||||
if (!p->lwip) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client_create: lwip_tcp_init failed");
|
||||
tun_close(p->tun);
|
||||
memory_pool_destroy(p->entry_pool); u_free(p); return NULL;
|
||||
}
|
||||
p->lwip = lwip_tcp_init(ua, tcp_proxy_client_output_cb, p);
|
||||
if (!p->lwip) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "lwip_tcp_init failed"); tun_close(p->tun); memory_pool_destroy(p->entry_pool); u_free(p); return NULL; }
|
||||
|
||||
if (mapping_count > 0) {
|
||||
int j; for (j = 0; j < mapping_count; j++) {
|
||||
uint16_t port_net = htons(mappings[j].local_port);
|
||||
struct tcp_pcb *lpcb = tcp_new(p->lwip);
|
||||
if (!lpcb) continue;
|
||||
tcp_bind(lpcb, INADDR_ANY, port_net);
|
||||
struct tcp_pcb *listen_pcb = tcp_listen(lpcb);
|
||||
if (listen_pcb) {
|
||||
tcp_arg(listen_pcb, p);
|
||||
tcp_accept(listen_pcb, tcp_proxy_client_accept_cb);
|
||||
if (mapping_count > 0) {
|
||||
int j; for (j = 0; j < mapping_count; j++) {
|
||||
uint16_t port_net = htons(mappings[j].local_port);
|
||||
struct tcp_pcb *lpcb = tcp_new(p->lwip);
|
||||
if (!lpcb) continue;
|
||||
tcp_bind(lpcb, INADDR_ANY, port_net);
|
||||
struct tcp_pcb *listen_pcb = tcp_listen(lpcb);
|
||||
if (listen_pcb) { tcp_arg(listen_pcb, p); tcp_accept(listen_pcb, tcp_proxy_client_accept_cb); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (socks_enabled && socks_addr && socks_addr[0]) {
|
||||
p->socks_listen = socks_proxy_init_listen(ua, socks_addr, &p->socks_conns, &p->socks_conn_count,
|
||||
&p->next_stream_id, inst, via_node_id, 0);
|
||||
if (!p->socks_listen) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client: SOCKS init failed on %s", socks_addr); }
|
||||
}
|
||||
|
||||
if (http_proxy_enabled && http_proxy_addr && http_proxy_addr[0]) {
|
||||
p->http_listen = socks_proxy_init_listen(ua, http_proxy_addr, &p->http_conns, &p->http_conn_count,
|
||||
&p->next_stream_id, inst, via_node_id, 1);
|
||||
if (!p->http_listen) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_client: HTTP proxy init failed on %s", http_proxy_addr); }
|
||||
}
|
||||
|
||||
if (inst) {
|
||||
if (etcp_router_bind(inst, ETCP_ID_TCP_PROXY_CLIENT, tcp_proxy_client_router_recv_cb) != 0) {
|
||||
if (etcp_router_bind(inst, ETCP_RT_ID_TCP_PROXY, tcp_proxy_client_router_recv_cb) != 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_SOCKET, "TCP proxy client: etcp_router_bind failed");
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client: etcp_router bind registered for ID=0x%02x", ETCP_ID_TCP_PROXY_CLIENT);
|
||||
udp_proxy_init(inst, ua);
|
||||
icmp_proxy_init(inst, ua);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client: etcp_router bind registered for ID=0x%02x", ETCP_RT_ID_TCP_PROXY);
|
||||
if (need_tun) { udp_proxy_init(inst, ua); icmp_proxy_init(inst, ua); }
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy client created: mappings=%d via_node=%016llx", mapping_count, (unsigned long long)via_node_id);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy client created: tun=%s socks=%s(http=%s) mappings=%d via_node=%016llx",
|
||||
need_tun ? "yes" : "no", socks_enabled ? "yes" : "no", http_proxy_enabled ? "yes" : "no",
|
||||
mapping_count, (unsigned long long)via_node_id);
|
||||
return p;
|
||||
}
|
||||
|
||||
void tcp_proxy_client_destroy(struct tcp_proxy_client* p) {
|
||||
if (!p) return;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client destroying: conns=%d", p->conn_count);
|
||||
if (p->inst) etcp_router_unbind(p->inst, ETCP_ID_TCP_PROXY_CLIENT);
|
||||
int total = p->conn_count + p->socks_conn_count + p->http_conn_count;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy client destroying: lwip=%d socks=%d http=%d",
|
||||
p->conn_count, p->socks_conn_count, p->http_conn_count);
|
||||
if (p->inst) etcp_router_unbind(p->inst, ETCP_RT_ID_TCP_PROXY);
|
||||
udp_proxy_destroy(p->inst);
|
||||
icmp_proxy_destroy(p->inst);
|
||||
|
||||
if (p->socks_listen) socks_proxy_close_listen(p->ua, p->socks_listen, NULL);
|
||||
socks_proxy_conn_free_all(&p->socks_conns, &p->socks_conn_count);
|
||||
|
||||
if (p->http_listen) socks_proxy_close_listen(p->ua, p->http_listen, NULL);
|
||||
socks_proxy_conn_free_all(&p->http_conns, &p->http_conn_count);
|
||||
|
||||
struct tcp_proxy_client_conn* pc = p->conns;
|
||||
while (pc) {
|
||||
struct tcp_proxy_client_conn* next = pc->next;
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef TCP_PROXY_CLIENT_H
|
||||
#define TCP_PROXY_CLIENT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../lib/socket_compat.h"
|
||||
@@ -17,6 +22,8 @@ struct memory_pool;
|
||||
struct tcp_proxy_client_mapping_config;
|
||||
struct tcp_proxy_server;
|
||||
struct lwip_tcp_ctx;
|
||||
struct socks_proxy_conn;
|
||||
struct listen_ctx;
|
||||
|
||||
struct tcp_pcb;
|
||||
|
||||
@@ -57,13 +64,31 @@ struct tcp_proxy_client {
|
||||
|
||||
struct tcp_proxy_client_mapping_config* mappings; // указатель на конфиг
|
||||
int mapping_count;
|
||||
|
||||
// SOCKS proxy
|
||||
int socks_enabled;
|
||||
struct socks_proxy_conn* socks_conns;
|
||||
int socks_conn_count;
|
||||
struct listen_ctx* socks_listen;
|
||||
|
||||
// HTTP CONNECT proxy
|
||||
int http_proxy_enabled;
|
||||
struct socks_proxy_conn* http_conns;
|
||||
int http_conn_count;
|
||||
struct listen_ctx* http_listen;
|
||||
};
|
||||
|
||||
struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua,
|
||||
const char* tun_name, const char* tun_ip, int mtu, int test_mode,
|
||||
struct tcp_proxy_client_mapping_config* mappings, int mapping_count,
|
||||
uint64_t via_node_id);
|
||||
uint64_t via_node_id,
|
||||
int socks_enabled, const char* socks_addr,
|
||||
int http_proxy_enabled, const char* http_proxy_addr);
|
||||
void tcp_proxy_client_destroy(struct tcp_proxy_client* p);
|
||||
void tcp_proxy_client_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // TCP_PROXY_CLIENT_H
|
||||
+109
-24
@@ -33,6 +33,7 @@ static void read_queue_drain_cb(struct ll_queue* q, void* arg);
|
||||
static void pause_resume_cb(struct ll_queue* q, void* arg);
|
||||
static void close_retry_cb(void* arg);
|
||||
static void diag_timer_cb(void* arg);
|
||||
static void retry_timer_cb(void* arg);
|
||||
static int send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd,
|
||||
uint32_t sid, const uint8_t* data, size_t len, int force);
|
||||
static void send_close(struct tcp_proxy_server_conn* rc);
|
||||
@@ -53,10 +54,14 @@ static int send_msg(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t subcmd,
|
||||
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_server: queue_entry_new failed subcmd=%02x sid=%08x", subcmd, sid); return -1; }
|
||||
e->dgram = u_malloc(TCP_PROXY_HDR_SIZE + len);
|
||||
if (!e->dgram) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_server: malloc(%zu) failed", TCP_PROXY_HDR_SIZE + len); queue_entry_free(e); return -1; }
|
||||
e->dgram[0] = ETCP_ID_TCP_PROXY_CLIENT;
|
||||
e->dgram[0] = ETCP_RT_ID_TCP_PROXY;
|
||||
e->dgram[1] = subcmd;
|
||||
memcpy(e->dgram + 2, &sid, 4);
|
||||
if (len > 0) memcpy(e->dgram + TCP_PROXY_HDR_SIZE, data, len);
|
||||
if (TCP_PROXY_HDR_SIZE + len > UINT16_MAX) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_server: msg too large len=%zu subcmd=%02x", len, subcmd);
|
||||
queue_dgram_free(e); queue_entry_free(e); return -1;
|
||||
}
|
||||
e->len = TCP_PROXY_HDR_SIZE + len;
|
||||
return etcp_route_send(inst, dst, e, force);
|
||||
}
|
||||
@@ -112,19 +117,26 @@ static void send_fin(struct tcp_proxy_server_conn* rc) {
|
||||
static void on_fin_cb(struct tcp_conn* tc, void* arg) {
|
||||
struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg;
|
||||
if (rc->cli_closed) return;
|
||||
int pend = write_pending(tc);
|
||||
int pend_w = write_pending(tc);
|
||||
int pend_r = (rc->tx_buf != NULL) || (tc->read_queue && tc->read_queue->head);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET,
|
||||
"SOCK:DST_FIN fd=%d sid=%08x fin_l=%d pend_w=%d pend_r=%d "
|
||||
"sent=%u recv=%u relayed=%u drain#=%u data#=%u "
|
||||
"rq=%d(%zub) wq=%d(%zub) wbuf=%s err=%d tx_buf=%s",
|
||||
(int)tc->sock, rc->stream_id, tc->fin_local, pend_w, pend_r,
|
||||
rc->bytes_sent, rc->bytes_recv, rc->bytes_relayed,
|
||||
rc->drain_count, rc->data_count,
|
||||
tc->read_queue->count, queue_total_bytes(tc->read_queue),
|
||||
tc->write_queue->count, queue_total_bytes(tc->write_queue),
|
||||
tc->write_buf ? "y" : "n", tc->error, rc->tx_buf ? "y" : "n");
|
||||
if (tc->fin_local) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SOCK:DST_FIN fd=%d sid=%08x fin_local=%d — both FINs, closing",
|
||||
(int)tc->sock, rc->stream_id, tc->fin_local);
|
||||
send_close(rc); tcp_conn_push_close(tc);
|
||||
} else if (pend) {
|
||||
} else if (pend_w) {
|
||||
tcp_conn_set_flushed(tc, on_flushed_cb);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SOCK:DST_FIN fd=%d sid=%08x write_pend=%d → deferred",
|
||||
(int)tc->sock, rc->stream_id, pend);
|
||||
} else if (pend_r) {
|
||||
rc->dst_fin_deferred = 1;
|
||||
} else {
|
||||
send_fin(rc);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SOCK:DST_FIN fd=%d sid=%08x → relay now",
|
||||
(int)tc->sock, rc->stream_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +162,16 @@ static void on_error_cb(struct tcp_conn* tc, int err, void* arg) {
|
||||
(void)err;
|
||||
if (tc->closed) return;
|
||||
struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET,
|
||||
"SOCK:ERR fd=%d sid=%08x err=%d fin_r=%d fin_l=%d "
|
||||
"sent=%u recv=%u relayed=%u drain#=%u data#=%u "
|
||||
"rq=%d wq=%d wbuf=%s conn=%d",
|
||||
tc ? (int)tc->sock : -1, rc->stream_id, err,
|
||||
tc ? tc->fin_remote : 0, tc ? tc->fin_local : 0,
|
||||
rc->bytes_sent, rc->bytes_recv, rc->bytes_relayed,
|
||||
rc->drain_count, rc->data_count,
|
||||
tc ? tc->read_queue->count : 0, tc ? tc->write_queue->count : 0,
|
||||
tc && tc->write_buf ? "y" : "n", tc ? tc->connected : 0);
|
||||
send_error(rc);
|
||||
tcp_proxy_server_conn_free(rc);
|
||||
}
|
||||
@@ -189,8 +211,36 @@ static void diag_timer_cb(void* arg) {
|
||||
static void read_queue_drain_cb(struct ll_queue* q, void* arg) {
|
||||
struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg;
|
||||
struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL;
|
||||
struct ll_entry* e = queue_data_get(q);
|
||||
if (!e) { queue_resume_callback(q); return; }
|
||||
struct ll_entry* e;
|
||||
|
||||
if (rc->tx_buf) {
|
||||
int ret = send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_DATA, rc->stream_id, rc->tx_buf, rc->tx_len, 0);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "TPS TXBUF RETRY: sid=%08x len=%u ret=%d", rc->stream_id, rc->tx_len, ret);
|
||||
if (ret == 0) {
|
||||
u_free(rc->tx_buf); rc->tx_buf = NULL; rc->tx_len = 0;
|
||||
if (rc->retry_timer) { uasync_cancel_timeout(rc->ua, rc->retry_timer); rc->retry_timer = NULL; }
|
||||
if (rc->dst_fin_deferred && !q->head) {
|
||||
rc->dst_fin_deferred = 0;
|
||||
send_fin(rc);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
etcp_router_on_send_ready(inst, rc->peer_node_id, ETCP_RT_ID_TCP_PROXY,
|
||||
&rc->pause_waiter, pause_resume_cb, rc);
|
||||
if (!rc->retry_timer) rc->retry_timer = uasync_set_timeout(rc->ua, 5000, rc, retry_timer_cb, "tps_retry");
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "TPS TXBUF BP AGAIN: sid=%08x waiter_reg", rc->stream_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
e = queue_data_get(q);
|
||||
if (!e) {
|
||||
if (rc->dst_fin_deferred) {
|
||||
rc->dst_fin_deferred = 0;
|
||||
send_fin(rc);
|
||||
}
|
||||
queue_resume_callback(q); return;
|
||||
}
|
||||
if (rc->cli_closed) {
|
||||
do {
|
||||
memory_pool_free(rc->tc->data_pool, e->dgram);
|
||||
@@ -201,27 +251,55 @@ static void read_queue_drain_cb(struct ll_queue* q, void* arg) {
|
||||
return;
|
||||
}
|
||||
int ret = send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_DATA, rc->stream_id, e->dgram, e->len, 0);
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_SOCKET, "SOCK:RECV fd=%d sid=%08x len=%u total=%d",
|
||||
(int)rc->tc->sock, rc->stream_id, e->len, rc->ctx ? rc->ctx->conn_count : 0);
|
||||
memory_pool_free(rc->tc->data_pool, e->dgram);
|
||||
queue_entry_free(e);
|
||||
rc->bytes_relayed += e->len; rc->drain_count++;
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "TPS DRAIN #%u sid=%08x len=%u → ret=%d rq=%d",
|
||||
rc->drain_count, rc->stream_id, e->len, ret, q->count);
|
||||
if (ret == 0) {
|
||||
memory_pool_free(rc->tc->data_pool, e->dgram); queue_entry_free(e);
|
||||
queue_resume_callback(q);
|
||||
} else {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SOCK:BACKPRESSURE fd=%d sid=%08x — pausing drain",
|
||||
(int)rc->tc->sock, rc->stream_id);
|
||||
etcp_router_on_send_ready(inst, rc->peer_node_id, ETCP_ID_TCP_PROXY_CLIENT,
|
||||
rc->tx_buf = u_malloc(e->len);
|
||||
if (rc->tx_buf) { memcpy(rc->tx_buf, e->dgram, e->len); rc->tx_len = e->len; }
|
||||
else { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TPS tx_buf malloc=%u failed sid=%08x — drop", e->len, rc->stream_id); }
|
||||
memory_pool_free(rc->tc->data_pool, e->dgram); queue_entry_free(e);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SOCK:BACKPRESSURE fd=%d sid=%08x — buffered %u for retry",
|
||||
(int)rc->tc->sock, rc->stream_id, rc->tx_len);
|
||||
etcp_router_on_send_ready(inst, rc->peer_node_id, ETCP_RT_ID_TCP_PROXY,
|
||||
&rc->pause_waiter, pause_resume_cb, rc);
|
||||
if (!rc->retry_timer) rc->retry_timer = uasync_set_timeout(rc->ua, 5000, rc, retry_timer_cb, "tps_retry");
|
||||
}
|
||||
}
|
||||
|
||||
static void pause_resume_cb(struct ll_queue* q, void* arg) {
|
||||
(void)q;
|
||||
struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg;
|
||||
if (!rc->tc || rc->tc->sock == SOCKET_INVALID) return;
|
||||
if (!rc->tc || rc->tc->sock == SOCKET_INVALID || rc->cli_closed) return;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "TPS RESUME sid=%08x tx_buf=%s rq=%d",
|
||||
rc->stream_id, rc->tx_buf ? "y" : "n",
|
||||
rc->tc->read_queue ? rc->tc->read_queue->count : 0);
|
||||
queue_resume_callback(rc->tc->read_queue);
|
||||
}
|
||||
|
||||
static void retry_timer_cb(void* arg) {
|
||||
struct tcp_proxy_server_conn* rc = (struct tcp_proxy_server_conn*)arg;
|
||||
rc->retry_timer = NULL;
|
||||
if (!rc->tx_buf || !rc->tc || rc->tc->sock == SOCKET_INVALID || rc->cli_closed) return;
|
||||
struct UTUN_INSTANCE* inst = rc->ctx ? rc->ctx->inst : NULL;
|
||||
int ret = send_msg(inst, rc->peer_node_id, TCP_PROXY_SUBCMD_DATA, rc->stream_id, rc->tx_buf, rc->tx_len, 1);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "TPS RETRY TIMER: sid=%08x len=%u force=1 ret=%d", rc->stream_id, rc->tx_len, ret);
|
||||
if (ret == 0) {
|
||||
u_free(rc->tx_buf); rc->tx_buf = NULL; rc->tx_len = 0;
|
||||
if (rc->dst_fin_deferred && rc->tc->read_queue && !rc->tc->read_queue->head) {
|
||||
rc->dst_fin_deferred = 0;
|
||||
send_fin(rc);
|
||||
return;
|
||||
}
|
||||
queue_resume_callback(rc->tc->read_queue);
|
||||
} else {
|
||||
rc->retry_timer = uasync_set_timeout(rc->ua, 5000, rc, retry_timer_cb, "tps_retry");
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Управление жизненным циклом коннекта
|
||||
// ====================================================================
|
||||
@@ -253,10 +331,12 @@ void tcp_proxy_server_conn_free(struct tcp_proxy_server_conn* rc) {
|
||||
struct tcp_proxy_server_conn** prev = &rc->ctx->conns;
|
||||
while (*prev) { if (*prev == rc) { *prev = rc->next; rc->ctx->conn_count--; break; } prev = &(*prev)->next; }
|
||||
}
|
||||
if (rc->tc) { tcp_conn_destroy(rc->tc); rc->tc = NULL; }
|
||||
if (rc->ctx && rc->ctx->inst) etcp_router_cancel_send_ready(rc->ctx->inst, rc->peer_node_id, ETCP_RT_ID_TCP_PROXY, &rc->pause_waiter);
|
||||
if (rc->tx_buf) { u_free(rc->tx_buf); rc->tx_buf = NULL; rc->tx_len = 0; }
|
||||
if (rc->retry_timer) { uasync_cancel_timeout(rc->ua, rc->retry_timer); rc->retry_timer = NULL; }
|
||||
if (rc->close_timer) { uasync_cancel_timeout(rc->ua, rc->close_timer); rc->close_timer = NULL; }
|
||||
if (rc->diag_timer) { uasync_cancel_timeout(rc->ua, rc->diag_timer); rc->diag_timer = NULL; }
|
||||
if (rc->ctx && rc->ctx->inst) etcp_router_cancel_send_ready(rc->ctx->inst, rc->peer_node_id, ETCP_ID_TCP_PROXY_CLIENT, &rc->pause_waiter);
|
||||
if (rc->tc) { tcp_conn_destroy(rc->tc); rc->tc = NULL; }
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SOCK:FREE u_free rc=%p", (void*)rc);
|
||||
u_free(rc);
|
||||
}
|
||||
@@ -287,7 +367,7 @@ int tcp_proxy_server_handle_connect(struct UTUN_INSTANCE* inst, struct ll_entry*
|
||||
socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy server: socket() failed"); u_free(rc); queue_dgram_free(entry); queue_entry_free(entry); return -1; }
|
||||
ctx->conn_count++;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "SOCK:NEW fd=%d sid=%08x dest=%d.%d.%d.%d:%d total=%d",
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "SOCK:NEW fd=%d sid=%08x dest=%d.%d.%d.%d:%d total=%d",
|
||||
(int)sock, stream_id, dest_ip[0],dest_ip[1],dest_ip[2],dest_ip[3],ntohs(dest_port), ctx->conn_count);
|
||||
socket_set_nonblocking(sock);
|
||||
|
||||
@@ -325,6 +405,9 @@ int tcp_proxy_server_handle_data(struct UTUN_INSTANCE* inst, struct ETCP_CONN* c
|
||||
queue_dgram_free(entry); queue_entry_free(entry); return -1;
|
||||
}
|
||||
size_t data_len = entry->len - TCP_PROXY_HDR_SIZE;
|
||||
rc->bytes_sent += (uint32_t)data_len; rc->data_count++;
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "TPS DATA #%u sid=%08x len=%zu",
|
||||
rc->data_count, stream_id, data_len);
|
||||
if (data_len > 0) {
|
||||
if (data_len > rc->tc->data_pool->object_size) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "tcp_proxy_server: data_len=%zu > pool_sz=%zu, dropping sid=%08x",
|
||||
@@ -397,7 +480,7 @@ void tcp_proxy_server_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
struct UTUN_INSTANCE* inst = conn ? conn->instance : NULL;
|
||||
|
||||
if (!entry || !entry->dgram || entry->len < TCP_PROXY_HDR_SIZE) {
|
||||
if (entry && entry->len == 9 && !conn && entry->dgram[0] == ETCP_ID_TCP_PROXY) {
|
||||
if (entry && entry->len == 9 && !conn && entry->dgram[0] == ETCP_RT_ID_TCP_PROXY) {
|
||||
uint64_t peer_id;
|
||||
memcpy(&peer_id, entry->dgram + 1, 8);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY CLOSE_ALL from %016llx — clearing server conns for peer",
|
||||
@@ -416,6 +499,8 @@ void tcp_proxy_server_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
uint8_t subcmd = entry->dgram[1];
|
||||
uint32_t stream_id; memcpy(&stream_id, entry->dgram + 2, 4);
|
||||
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_TRAFFIC, "PROXY SERVER RECV subcmd=%02x sid=%08x pkt_len=%u", subcmd, stream_id, entry->len);
|
||||
|
||||
if (subcmd == TCP_PROXY_SUBCMD_CONNECT) {
|
||||
uint64_t src_node_id = conn ? conn->peer_node_id : (inst ? inst->node_id : 0);
|
||||
tcp_proxy_server_handle_connect(inst, entry, stream_id, src_node_id);
|
||||
@@ -451,7 +536,7 @@ int tcp_proxy_server_init(struct UTUN_INSTANCE* inst) {
|
||||
ctx->inst = inst;
|
||||
if (!ctx->enabled) return 0;
|
||||
g_tcp_proxy_server_ctx = ctx;
|
||||
etcp_router_bind(inst, ETCP_ID_TCP_PROXY, tcp_proxy_server_recv_cb);
|
||||
etcp_router_bind(inst, ETCP_RT_ID_TCP_PROXY, tcp_proxy_server_recv_cb);
|
||||
if (!inst->config->global.tcp_proxy_client_enabled) {
|
||||
udp_proxy_init(inst, inst->ua);
|
||||
icmp_proxy_init(inst, inst->ua);
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef TCP_PROXY_SERVER_H
|
||||
#define TCP_PROXY_SERVER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../lib/socket_compat.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
@@ -36,9 +41,18 @@ struct tcp_proxy_server_conn {
|
||||
void* close_timer; // таймер повтора CLOSE/ERROR
|
||||
int close_backoff; // backoff: 50..5000 tb (5ms..500ms)
|
||||
void* diag_timer; // 1-секундный таймер диагностики
|
||||
uint8_t dst_fin_deferred; // FIN от destination отложен (ждём drain read_queue + tx_buf)
|
||||
uint8_t freed; // 1 = уже освобождён, защита от double-free
|
||||
|
||||
struct queue_waiter_handle pause_waiter;
|
||||
void* retry_timer; // fallback timer (500ms) for force=1 retry on stall
|
||||
uint8_t* tx_buf; // буфер при backpressure (retry в pause_resume_cb)
|
||||
uint16_t tx_len;
|
||||
uint32_t bytes_sent; // байт записано в destination (реальный TCP)
|
||||
uint32_t bytes_recv; // байт прочитано от destination
|
||||
uint32_t bytes_relayed; // байт отправлено клиенту через ETCP
|
||||
uint32_t drain_count; // счётчик вызовов read_queue_drain_cb
|
||||
uint32_t data_count; // счётчик входящих DATA от клиента
|
||||
struct UASYNC* ua;
|
||||
};
|
||||
|
||||
@@ -64,4 +78,8 @@ void tcp_proxy_server_handle_close(struct UTUN_INSTANCE* inst, uint32_t stream_i
|
||||
void tcp_proxy_server_handle_error(struct UTUN_INSTANCE* inst, uint32_t stream_id);
|
||||
void tcp_proxy_server_handle_fin(struct UTUN_INSTANCE* inst, uint32_t stream_id);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -52,7 +52,7 @@ static void flow_read_cb(socket_t sock, void* arg) {
|
||||
if (!e) return;
|
||||
e->dgram = u_malloc(UDP_PROXY_HDR_SIZE + n);
|
||||
if (!e->dgram) { queue_entry_free(e); return; }
|
||||
e->dgram[0] = ETCP_ID_UDP_PROXY;
|
||||
e->dgram[0] = ETCP_RT_ID_UDP_PROXY;
|
||||
e->dgram[1] = UDP_PROXY_SUBCMD_DATA;
|
||||
memcpy(e->dgram + 2, &f->client_node_id, 8);
|
||||
// Ответ src = оригинальный dst_ip:dest_port
|
||||
@@ -114,7 +114,7 @@ drop:
|
||||
// Сторона клиента: принять REPLY, доставить в TUN
|
||||
// ====================================================================
|
||||
static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (entry->len < UDP_PROXY_HDR_SIZE + 1) { queue_entry_free(entry); queue_dgram_free(entry); return; }
|
||||
if (entry->len < UDP_PROXY_HDR_SIZE + 1) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
// svc_id уже обработан диспетчером etcp_router
|
||||
uint32_t src_ip, dst_ip;
|
||||
uint16_t src_port, dst_port;
|
||||
@@ -136,7 +136,7 @@ static void client_handle_reply(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
// ====================================================================
|
||||
void udp_proxy_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry) {
|
||||
if (!entry || !entry->dgram || entry->len < 2) {
|
||||
if (entry) { queue_entry_free(entry); queue_dgram_free(entry); }
|
||||
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
|
||||
return;
|
||||
}
|
||||
uint8_t subcmd = entry->dgram[1];
|
||||
@@ -160,7 +160,7 @@ int udp_proxy_send_to_exit(struct UTUN_INSTANCE* inst, uint64_t exit_node_id,
|
||||
if (!e) return -1;
|
||||
e->dgram = u_malloc(UDP_PROXY_HDR_SIZE + payload_len);
|
||||
if (!e->dgram) { queue_entry_free(e); return -1; }
|
||||
e->dgram[0] = ETCP_ID_UDP_PROXY;
|
||||
e->dgram[0] = ETCP_RT_ID_UDP_PROXY;
|
||||
e->dgram[1] = UDP_PROXY_SUBCMD_DATA;
|
||||
memcpy(e->dgram + 2, &inst->node_id, 8);
|
||||
memcpy(e->dgram + 10, &src_ip, 4);
|
||||
@@ -245,7 +245,7 @@ int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) {
|
||||
ctx->is_exit = inst->tcp_proxy_server.enabled;
|
||||
g_udp_ctx = ctx;
|
||||
|
||||
etcp_router_bind(inst, ETCP_ID_UDP_PROXY, udp_proxy_recv_cb);
|
||||
etcp_router_bind(inst, ETCP_RT_ID_UDP_PROXY, udp_proxy_recv_cb);
|
||||
ctx->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "udp_proxy initialized (exit=%d)", ctx->is_exit);
|
||||
return 0;
|
||||
@@ -253,7 +253,7 @@ int udp_proxy_init(struct UTUN_INSTANCE* inst, struct UASYNC* ua) {
|
||||
|
||||
void udp_proxy_destroy(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst || !g_udp_ctx) return;
|
||||
etcp_router_unbind(inst, ETCP_ID_UDP_PROXY);
|
||||
etcp_router_unbind(inst, ETCP_RT_ID_UDP_PROXY);
|
||||
if (g_udp_ctx->expire_timer) { uasync_cancel_timeout(g_udp_ctx->ua, g_udp_ctx->expire_timer); g_udp_ctx->expire_timer = NULL; }
|
||||
struct udp_flow* f = g_udp_ctx->flows;
|
||||
while (f) { struct udp_flow* n = f->next;
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef UDP_PROXY_H
|
||||
#define UDP_PROXY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../lib/socket_compat.h"
|
||||
|
||||
@@ -62,4 +67,8 @@ int udp_proxy_deliver_reply(struct UTUN_INSTANCE* inst,
|
||||
uint32_t dst_ip, uint16_t dst_port,
|
||||
const uint8_t* payload, size_t payload_len);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // UDP_PROXY_H
|
||||
+15
-21
@@ -3,7 +3,7 @@
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/radix.h"
|
||||
#include "route_node.h"
|
||||
#include "topo_node.h"
|
||||
#include "route6_lib.h"
|
||||
|
||||
#define HASH_SIZE 256
|
||||
@@ -31,22 +31,22 @@ static void make_mask6(uint8_t out[ROUTE6_KEY_SIZE], uint8_t plen) {
|
||||
struct ROUTE6_TABLE *route6_table_create(struct UASYNC *ua) {
|
||||
struct ROUTE6_TABLE *t = u_calloc(1, sizeof(struct ROUTE6_TABLE));
|
||||
if (!t) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route6_table_create: alloc failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "alloc failed");
|
||||
return NULL;
|
||||
}
|
||||
if (rn_inithead((void**)&t->rnh, RADIX_OFF) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route6_table_create: rn_inithead failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "rn_inithead failed");
|
||||
u_free(t);
|
||||
return NULL;
|
||||
}
|
||||
t->queue = queue_new(ua, HASH_SIZE, ROUTE6_NODE_ID_OFF, sizeof(uint64_t), "route6");
|
||||
if (!t->queue) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route6_table_create: queue_new failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "queue_new failed");
|
||||
rn_detachhead((void**)&t->rnh);
|
||||
u_free(t);
|
||||
return NULL;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route6_table_create: table=%p", (void*)t);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "table=%p", (void*)t);
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -63,23 +63,17 @@ void route6_table_destroy(struct ROUTE6_TABLE *table) {
|
||||
u_free(table);
|
||||
}
|
||||
|
||||
bool route6_insert(struct ROUTE6_TABLE *table, struct NODEINFO_Q *node) {
|
||||
if (!table || !node) return false;
|
||||
const struct NODEINFO_IPV6_SUBNET *subnets = NULL;
|
||||
int cnt = get_node_v6_routes(node, &subnets);
|
||||
if (cnt <= 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route6_insert: node has no v6 subnets");
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
bool route6_insert(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node) {
|
||||
if (!table || !node || !node->subnets || !node->subnets->v6_subnets) return false;
|
||||
for (const struct TOPO_SUBNET6* s = node->subnets->v6_subnets; s; s = s->next) {
|
||||
struct ll_entry *entry = queue_entry_new(ROUTE6_DATA_SIZE);
|
||||
if (!entry) { DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route6_insert: queue_entry_new failed"); return false; }
|
||||
if (!entry) { DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "queue_entry_new failed"); return false; }
|
||||
struct ROUTE6_DATA *rd = (struct ROUTE6_DATA *)entry->data;
|
||||
make_key6(rd->key, subnets[i].addr);
|
||||
make_mask6(rd->mask, subnets[i].prefix_length);
|
||||
make_key6(rd->key, s->addr);
|
||||
make_mask6(rd->mask, s->prefix_length);
|
||||
rd->v_node_info = node;
|
||||
rd->node_id = node->node.node_id;
|
||||
rd->prefix_length = subnets[i].prefix_length;
|
||||
rd->node_id = node->node->node_id;
|
||||
rd->prefix_length = s->prefix_length;
|
||||
struct radix_node *rn = rn_addroute(rd->key, rd->mask, &table->rnh->rh, rd->rn_nodes);
|
||||
if (!rn) { queue_entry_free(entry); continue; }
|
||||
queue_data_put_with_index(table->queue, entry);
|
||||
@@ -87,9 +81,9 @@ bool route6_insert(struct ROUTE6_TABLE *table, struct NODEINFO_Q *node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void route6_delete(struct ROUTE6_TABLE *table, struct NODEINFO_Q *node) {
|
||||
void route6_delete(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node) {
|
||||
if (!table || !node) return;
|
||||
uint64_t node_id = node->node.node_id;
|
||||
uint64_t node_id = node->node->node_id;
|
||||
struct ll_entry *entry = queue_find_data_by_index(table->queue, &node_id);
|
||||
while (entry) {
|
||||
struct ROUTE6_DATA *rd = (struct ROUTE6_DATA *)entry->data;
|
||||
|
||||
+44
-3
@@ -1,5 +1,10 @@
|
||||
#ifndef ROUTE6_LIB_H
|
||||
#define ROUTE6_LIB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
@@ -7,11 +12,43 @@
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "../lib/u_async.h"
|
||||
|
||||
|
||||
/*
|
||||
Диапазон Назначение
|
||||
::/128 Неопределенный адрес (unspecified). Используется до назначения адреса интерфейсу.
|
||||
::1/128 Адрес обратной петли (loopback), аналог 127.0.0.1 в IPv4.
|
||||
2000::/3 Глобально маршрутизируемые (Global Unicast) адреса. Основное адресное пространство Интернета.
|
||||
fc00::/7 Уникальные локальные адреса (ULA), аналог частных IPv4 (10.0.0.0/8, 192.168.0.0/16). На практике обычно используется fd00::/8.
|
||||
fe80::/10 Локальные канальные адреса (Link-Local). Автоматически назначаются интерфейсам и работают только в пределах одного сегмента сети. Не маршрутизируются.
|
||||
ff00::/8 Multicast-адреса. Используются вместо широковещания (broadcast), которого в IPv6 нет.
|
||||
100::/64 Адреса для протокола Discard-Only. Предназначены для тестирования и отладки.
|
||||
64:ff9b::/96 Префикс для трансляции IPv4↔IPv6 (NAT64).
|
||||
64:ff9b:1::/48 Расширенный префикс NAT64.
|
||||
2001:db8::/32 Зарезервирован для документации и примеров. Не используется в Интернете.
|
||||
2002::/16 6to4-адреса (устаревший механизм перехода с IPv4 на IPv6).
|
||||
2001::/32 Teredo (устаревшая технология туннелирования IPv6 через IPv4).
|
||||
::ffff:0:0/96 IPv4-mapped IPv6 addresses. Используются ОС для представления IPv4-адресов в IPv6 API.
|
||||
|
||||
Адреса узлов сети формируются по следующему принципу:
|
||||
FCxx:xxxx:xxxx:xxxx : yyyy:yyyy:yyyy:yyyy - local network: x = network id, y - node id.
|
||||
-- network id --- ---- node id ------
|
||||
|
||||
FDxx/8 - опциональные ipv6 подсети узлов /64 (если нужно)
|
||||
|
||||
В разных node_groups общие:
|
||||
- nodelists с их настройками (глобальная таблица)
|
||||
- одинаковые линки между nodes (т.к. только один линк между парой узлов)
|
||||
|
||||
- При маршрутизации используем только узлы
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// таблица маршрутизации IPv6 на radix tree + ll_queue (поиск по node_id для удаления)
|
||||
|
||||
struct ROUTE6_DATA {
|
||||
struct radix_node rn_nodes[2]; // scratch-буфер для rn_addroute
|
||||
struct NODEINFO_Q *v_node_info; // узел-владелец
|
||||
struct TOPO_NODEQ *v_node_info; // узел-владелец
|
||||
uint64_t node_id; // ключ для hash-поиска в ll_queue
|
||||
uint8_t prefix_length; // 0..128
|
||||
uint8_t key[17]; // sockaddr-like: {len=17, addr[16]}
|
||||
@@ -29,8 +66,12 @@ struct ROUTE6_TABLE {
|
||||
|
||||
struct ROUTE6_TABLE *route6_table_create(struct UASYNC *ua);
|
||||
void route6_table_destroy(struct ROUTE6_TABLE *table);
|
||||
bool route6_insert(struct ROUTE6_TABLE *table, struct NODEINFO_Q *node);
|
||||
void route6_delete(struct ROUTE6_TABLE *table, struct NODEINFO_Q *node);
|
||||
bool route6_insert(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node);
|
||||
void route6_delete(struct ROUTE6_TABLE *table, struct TOPO_NODEQ *node);
|
||||
const struct ROUTE6_DATA *route6_lookup(struct ROUTE6_TABLE *table, const uint8_t addr[16]);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
-1212
File diff suppressed because it is too large.
Load diff
-226
@@ -1,226 +0,0 @@
|
||||
#ifndef ROUTE_BGP_H
|
||||
#define ROUTE_BGP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "../lib/liblmdb/lmdb.h"
|
||||
#include "route_lib.h"
|
||||
#include "route_node.h"
|
||||
#include "secure_channel.h"
|
||||
|
||||
|
||||
// ETCP ID для маршрутных пакетов
|
||||
#define ETCP_ID_ROUTE_ENTRY 0x01
|
||||
|
||||
// Sub-команды
|
||||
#define ROUTE_SUBCMD_NODEINFO 0x04 // полная информация об узле + маршруты
|
||||
#define ROUTE_SUBCMD_REQUEST_TABLE 0x05 // запрос полной таблицы
|
||||
#define ROUTE_SUBCMD_WITHDRAW 0x06 // узел стал недоступен
|
||||
#define ROUTE_SUBCMD_NAT_INFO 0x09 // информация о типе NAT клиента
|
||||
#define ROUTE_SUBCMD_NAT_CHECK_REQ 0x0A // запрос от клиента на проверку NAT для сокета
|
||||
|
||||
#define MAX_HOPS 16
|
||||
#define BGP_NODES_HASH_SIZE 256
|
||||
|
||||
/**
|
||||
* @brief Пакет с информацией об узле (NODEINFO)
|
||||
*/
|
||||
struct BGP_NODEINFO_PACKET {
|
||||
uint8_t cmd; // ETCP_ID_ROUTE_ENTRY
|
||||
uint8_t subcmd; // ROUTE_SUBCMD_NODEINFO
|
||||
struct NODEINFO node;
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* @brief Пакет WITHDRAW (фиксированный)
|
||||
*/
|
||||
struct BGP_WITHDRAW_PACKET {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd;
|
||||
uint64_t node_id; // удаляемый узел (который стал недоступен)
|
||||
uint64_t wd_source; // узел который инициировал withdraw (при удалении он должен быть в hoplist или = current node_id)
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* @brief Пакет запроса полной таблицы
|
||||
*/
|
||||
struct BGP_ROUTE_REQUEST {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ROUTE_BGP_CONN_ITEM {
|
||||
struct ll_entry ll;
|
||||
struct ETCP_CONN* conn;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Пакет NAT_INFO (фиксированный)
|
||||
*/
|
||||
struct BGP_NAT_INFO {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd;
|
||||
uint8_t socket_id; // MY socket id (link->conn->sock_id)
|
||||
uint8_t nat_ip[4]; // network byte order
|
||||
uint16_t nat_port; // network byte order
|
||||
uint8_t nat_type; // NAT_TYPE_*
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* @brief Пакет запроса на проверку NAT от клиента к серверу
|
||||
*/
|
||||
struct BGP_NAT_CHECK_REQ {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd; // ROUTE_SUBCMD_NAT_CHECK_REQ
|
||||
uint8_t socket_id; // ID сокета клиента, который надо проверить
|
||||
uint32_t interface_ip; // interface_addr IP клиента (network byte order)
|
||||
uint16_t interface_port; // interface_addr port клиента (network byte order)
|
||||
} __attribute__((packed));
|
||||
|
||||
struct route_ping_pending;
|
||||
struct ETCP_LINK;
|
||||
|
||||
struct ROUTE_BGP {
|
||||
struct UTUN_INSTANCE* instance;
|
||||
struct ll_queue* senders_list;
|
||||
struct ll_queue* nodes;
|
||||
struct NODEINFO_Q* local_node;
|
||||
struct route_ping_pending* ping_pending;
|
||||
uint32_t next_ping_req_id;
|
||||
uint8_t allow_nat_check_local; // 1 = разрешить NAT check для локальных подсетей (для тестов)
|
||||
uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; // свой Ed25519 pubkey (для подписи сообщений)
|
||||
MDB_env* nodeinfo_env; // LMDB environment handle
|
||||
MDB_dbi nodeinfo_dbi; // LMDB database handle for nodeinfo table
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Инициализирует модуль BGP маршрутизации.
|
||||
*
|
||||
* Создает очереди, local_node из подсетей конфига, привязывает к ETCP_ID_ROUTE_ENTRY,
|
||||
* устанавливает коллбеки new_conn для on_up/on_down.
|
||||
*
|
||||
* @param instance экземпляр utun с node_id и конфигом
|
||||
* @return ROUTE_BGP или NULL при ошибке
|
||||
*/
|
||||
struct ROUTE_BGP* route_bgp_init(struct UTUN_INSTANCE* instance);
|
||||
|
||||
/**
|
||||
* @brief Освобождает ресурсы BGP (очереди, local_node, снимает привязку ETCP).
|
||||
*
|
||||
* @param instance экземпляр utun
|
||||
*/
|
||||
void route_bgp_destroy(struct UTUN_INSTANCE* instance);
|
||||
|
||||
/**
|
||||
* @brief Добавляет conn в senders_list (если нет), отправляет запрос таблицы (nodeinfo).
|
||||
*
|
||||
* Вызывается при ETCP on_up.
|
||||
*/
|
||||
void route_bgp_new_conn(struct ETCP_CONN* conn);
|
||||
|
||||
/**
|
||||
* @brief Удаляет conn из senders_list, очищает paths во всех nodes, отправляет withdraw если node unreachable.
|
||||
*
|
||||
* Вызывается при ETCP on_down.
|
||||
*/
|
||||
void route_bgp_remove_conn(struct ETCP_CONN* conn);
|
||||
|
||||
/**
|
||||
* @brief Обрабатывает пакет NODEINFO.
|
||||
*
|
||||
* Проверка версии, обновление или создание NODEINFO_Q, добавление пути,
|
||||
* вставка в роутинг, broadcast если не max hops.
|
||||
*
|
||||
* @return 0 при успехе
|
||||
*/
|
||||
|
||||
int route_bgp_process_nodeinfo(struct ROUTE_BGP* bgp, struct ETCP_CONN* from, const uint8_t* data, size_t len);
|
||||
/**
|
||||
* @brief Обрабатывает WITHDRAW.
|
||||
*
|
||||
* Удаляет node из роутинга и nodes, broadcast withdraw.
|
||||
*
|
||||
* @return 0 при успехе
|
||||
*/
|
||||
int route_bgp_process_withdraw(struct ROUTE_BGP* bgp, struct ETCP_CONN* sender, const uint8_t* data, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Отправляет NODEINFO пакет одному conn (всегда local_node).
|
||||
*/
|
||||
void route_bgp_send_nodeinfo(struct NODEINFO_Q* node, struct ETCP_CONN* conn);
|
||||
|
||||
/**
|
||||
* @brief Отправляет WITHDRAW для node_id (вызывает broadcast_withdraw).
|
||||
*/
|
||||
void route_bgp_send_withdraw(struct ROUTE_BGP* bgp, uint64_t node_id);
|
||||
|
||||
int nodeinfo_dyn_size(struct NODEINFO* node);
|
||||
|
||||
/**
|
||||
* @brief Поиск оптимального ETCP соединения для указанного node_id.
|
||||
*
|
||||
* Перебирает paths узла, выбирает путь с минимальным hop_count.
|
||||
*
|
||||
* @param bgp указатель на ROUTE_BGP
|
||||
* @param node_id целевой узел
|
||||
* @return оптимальный ETCP_CONN* или NULL
|
||||
*/
|
||||
struct ETCP_CONN* route_bgp_find_conn_for_node(struct ROUTE_BGP* bgp, uint64_t node_id);
|
||||
|
||||
/**
|
||||
* @brief Добавляет путь (conn) в paths узла.
|
||||
*
|
||||
* @return 0 при успехе
|
||||
*/
|
||||
int route_bgp_add_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count);
|
||||
|
||||
/**
|
||||
* @brief Удаляет conn из paths узла.
|
||||
*
|
||||
* @return 1 если путей не осталось (unreachable)
|
||||
*/
|
||||
int route_bgp_remove_path(struct NODEINFO_Q* nq, struct ETCP_CONN* conn);
|
||||
|
||||
/**
|
||||
* @brief Отправляет NAT_INFO клиенту с его типом NAT.
|
||||
*
|
||||
* @param conn соединение к клиенту
|
||||
* @param nat_ip IP клиента (network byte order)
|
||||
* @param nat_port порт клиента (network byte order)
|
||||
* @param nat_type NAT_TYPE_EIM или NAT_TYPE_STRICT
|
||||
*/
|
||||
void route_bgp_send_nat_info(struct ETCP_CONN* conn, uint8_t socket_id, uint32_t nat_ip, uint16_t nat_port, uint8_t nat_type);
|
||||
|
||||
/**
|
||||
* @brief Отправляет запрос на проверку NAT для указанного socket_id серверу.
|
||||
*
|
||||
* @param conn соединение к серверу
|
||||
* @param socket_id ID сокета для проверки
|
||||
*/
|
||||
void route_bgp_send_nat_check_req(struct ETCP_CONN* conn, uint8_t socket_id);
|
||||
|
||||
/**
|
||||
* @brief Разрешить/запретить NAT check для локальных подсетей (127.0.0.1, 10.x.x.x и т.д.)
|
||||
* По умолчанию запрещено. Полезно для тестов.
|
||||
*/
|
||||
void route_bgp_set_nat_check_local(struct ROUTE_BGP* bgp, int allow);
|
||||
|
||||
/**
|
||||
* @brief Запускает NAT check для всех линков всех соединений.
|
||||
*/
|
||||
void route_bgp_request_nat_check_all(struct ROUTE_BGP* bgp);
|
||||
|
||||
/**
|
||||
* @brief Запускает NAT check для одного линка.
|
||||
*/
|
||||
void route_bgp_start_link_nat_check(struct ROUTE_BGP* bgp, struct ETCP_LINK* link);
|
||||
|
||||
/* Аргумент callback NAT check */
|
||||
struct nat_check_arg {
|
||||
struct ETCP_LINK* link;
|
||||
uint32_t nat_ip;
|
||||
uint16_t nat_port;
|
||||
};
|
||||
|
||||
#endif // ROUTE_BGP_H
|
||||
+92
-136
@@ -13,15 +13,16 @@
|
||||
#include "utun_instance.h"
|
||||
#include "etcp.h"
|
||||
#include "etcp_connections.h"
|
||||
#include "route_node.h"
|
||||
#include "route_bgp.h"
|
||||
#include "topo_node.h"
|
||||
#include "topo_group.h"
|
||||
#include "route_connectivity.h"
|
||||
|
||||
#define CONN_MAX_SOCKET_CANDIDATES 8
|
||||
|
||||
struct conn_probe_ctx {
|
||||
struct conn_probe_ctx* next; /* linked list in nq->connectivity.probe_list */
|
||||
struct UTUN_INSTANCE* instance;
|
||||
struct NODEINFO_Q* nq;
|
||||
struct TOPO_NODEQ* nq;
|
||||
uint8_t addr_type; // ADDR_TYPE_*
|
||||
struct sockaddr_storage target_addr;
|
||||
uint8_t peer_pubkey[SC_PUBKEY_SIZE];
|
||||
@@ -67,7 +68,7 @@ static int sock_addr_cmp(const struct sockaddr_storage* a, const struct sockaddr
|
||||
}
|
||||
|
||||
// проверяет что два адреса (NODEINFO_IPV4_ADDR) не дубликаты по IP+port
|
||||
static int addr_eq(const struct NODEINFO_IPV4_ADDR* a, uint32_t ip, uint16_t port) {
|
||||
static int addr_eq(const struct TOPO_ADDR4* a, uint32_t ip, uint16_t port) {
|
||||
uint32_t a_ip; memcpy(&a_ip, a->addr, 4);
|
||||
return a_ip == ip && a->port == port;
|
||||
}
|
||||
@@ -122,15 +123,16 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance,
|
||||
struct sockaddr_in* if_sin = (struct sockaddr_in*)&e_sock->interface_addr;
|
||||
uint32_t if_ip = if_sin->sin_addr.s_addr;
|
||||
|
||||
if (addr_type == ADDR_TYPE_INTERFACE) {
|
||||
// предпочитаем PRIVATE сокеты в той же /24 подсети
|
||||
if (e_sock->type == CFG_SERVER_TYPE_PRIVATE && (if_ip & 0x00FFFFFF) == (target_ip & 0x00FFFFFF))
|
||||
if (addr_type == TOPO_ADDR_INTERFACE) {
|
||||
// предпочитаем PRIVATE/LOCAL сокеты в той же /24 подсети
|
||||
if ((e_sock->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL)
|
||||
&& (if_ip & 0x00FFFFFF) == (target_ip & 0x00FFFFFF))
|
||||
match = 1;
|
||||
} else {
|
||||
// NAT или REAL: предпочитаем DIRECT > EIM > PUBLIC
|
||||
// NAT или REAL: предпочитаем DIRECT > EIM > PUBLIC/UNKNOWN
|
||||
if (e_sock->nat_type == NAT_VERIFIED_DIRECT) match = 1;
|
||||
else if (e_sock->nat_type == NAT_VERIFIED_EIM) match = 1;
|
||||
else if (e_sock->type == CFG_SERVER_TYPE_PUBLIC) match = 1;
|
||||
else if (e_sock->type == CFG_SERVER_TYPE_PUBLIC || e_sock->type == CFG_SERVER_TYPE_UNKNOWN) match = 1;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
@@ -148,11 +150,13 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance,
|
||||
int dup = 0;
|
||||
for (int i = 0; i < found; i++) if (out_sockets[i] == e_sock) { dup = 1; break; }
|
||||
if (!dup) {
|
||||
if (addr_type == ADDR_TYPE_INTERFACE) {
|
||||
if (e_sock->type == CFG_SERVER_TYPE_PRIVATE) out_sockets[found++] = e_sock;
|
||||
if (addr_type == TOPO_ADDR_INTERFACE) {
|
||||
if (e_sock->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL)
|
||||
out_sockets[found++] = e_sock;
|
||||
} else {
|
||||
// любой не-private
|
||||
if (e_sock->type != CFG_SERVER_TYPE_PRIVATE) out_sockets[found++] = e_sock;
|
||||
// любой не-private и не-local
|
||||
if (e_sock->type != CFG_SERVER_TYPE_PRIVATE && e_sock->type != CFG_SERVER_TYPE_LOCAL)
|
||||
out_sockets[found++] = e_sock;
|
||||
}
|
||||
}
|
||||
e_sock = e_sock->next;
|
||||
@@ -245,33 +249,37 @@ static void conn_probe_single_cb(int success, uint16_t rtt, void* arg,
|
||||
}
|
||||
|
||||
static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok) {
|
||||
if (!ctx || !ctx->nq) return;
|
||||
struct NODE_CONNECTIVITY* c = &ctx->nq->connectivity;
|
||||
if (!ctx) return;
|
||||
if (ctx->nq) {
|
||||
struct TOPO_CONNECTIVITY* c = &ctx->nq->connectivity;
|
||||
struct conn_probe_ctx** p = (struct conn_probe_ctx**)&c->probe_list;
|
||||
while (*p) { if (*p == ctx) { *p = ctx->next; break; } p = &(*p)->next; }
|
||||
|
||||
switch (ctx->addr_type) {
|
||||
case ADDR_TYPE_INTERFACE:
|
||||
c->interface_status = ok ? PROBE_RESULT_REACHABLE : (c->interface_status == PROBE_RESULT_UNKNOWN ? PROBE_RESULT_UNREACHABLE : c->interface_status);
|
||||
if (ok && ctx->best_across_sockets < c->interface_min_rtt) c->interface_min_rtt = ctx->best_across_sockets;
|
||||
c->interface_probe_time = get_time_tb();
|
||||
break;
|
||||
case ADDR_TYPE_NAT:
|
||||
c->nat_status = ok ? PROBE_RESULT_REACHABLE : (c->nat_status == PROBE_RESULT_UNKNOWN ? PROBE_RESULT_UNREACHABLE : c->nat_status);
|
||||
if (ok && ctx->best_across_sockets < c->nat_min_rtt) c->nat_min_rtt = ctx->best_across_sockets;
|
||||
c->nat_probe_time = get_time_tb();
|
||||
break;
|
||||
case ADDR_TYPE_REAL:
|
||||
c->real_status = ok ? PROBE_RESULT_REACHABLE : (c->real_status == PROBE_RESULT_UNKNOWN ? PROBE_RESULT_UNREACHABLE : c->real_status);
|
||||
if (ok && ctx->best_across_sockets < c->real_min_rtt) c->real_min_rtt = ctx->best_across_sockets;
|
||||
c->real_probe_time = get_time_tb();
|
||||
break;
|
||||
}
|
||||
switch (ctx->addr_type) {
|
||||
case TOPO_ADDR_INTERFACE:
|
||||
c->interface_status = ok ? PROBE_RESULT_REACHABLE : (c->interface_status == PROBE_RESULT_UNKNOWN ? PROBE_RESULT_UNREACHABLE : c->interface_status);
|
||||
if (ok && ctx->best_across_sockets < c->interface_min_rtt) c->interface_min_rtt = ctx->best_across_sockets;
|
||||
c->interface_probe_time = get_time_tb();
|
||||
break;
|
||||
case TOPO_ADDR_NAT:
|
||||
c->nat_status = ok ? PROBE_RESULT_REACHABLE : (c->nat_status == PROBE_RESULT_UNKNOWN ? PROBE_RESULT_UNREACHABLE : c->nat_status);
|
||||
if (ok && ctx->best_across_sockets < c->nat_min_rtt) c->nat_min_rtt = ctx->best_across_sockets;
|
||||
c->nat_probe_time = get_time_tb();
|
||||
break;
|
||||
case TOPO_ADDR_REAL:
|
||||
c->real_status = ok ? PROBE_RESULT_REACHABLE : (c->real_status == PROBE_RESULT_UNKNOWN ? PROBE_RESULT_UNREACHABLE : c->real_status);
|
||||
if (ok && ctx->best_across_sockets < c->real_min_rtt) c->real_min_rtt = ctx->best_across_sockets;
|
||||
c->real_probe_time = get_time_tb();
|
||||
break;
|
||||
}
|
||||
|
||||
if (c->pending_count > 0) c->pending_count--;
|
||||
if (c->pending_count == 0) {
|
||||
c->probe_status = PROBE_STATUS_DONE;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe DONE for node %016llx: intf=%d nat=%d real=%d",
|
||||
(unsigned long long)ctx->nq->node.node_id,
|
||||
c->interface_status, c->nat_status, c->real_status);
|
||||
if (c->pending_count > 0) c->pending_count--;
|
||||
if (c->pending_count == 0) {
|
||||
c->probe_status = PROBE_STATUS_DONE;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe DONE for node %016llx: intf=%d nat=%d real=%d",
|
||||
(unsigned long long)ctx->nq->node->node_id,
|
||||
c->interface_status, c->nat_status, c->real_status);
|
||||
}
|
||||
}
|
||||
|
||||
u_free(ctx);
|
||||
@@ -279,11 +287,11 @@ static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok) {
|
||||
|
||||
// ---- public API ----
|
||||
|
||||
void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct NODEINFO_Q* nq) {
|
||||
void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_NODEQ* nq) {
|
||||
if (!instance || !nq) return;
|
||||
if (nq->node.node_id == instance->node_id) return; // не пингуем себя
|
||||
if (nq->node->node_id == instance->node_id) return; // не пингуем себя
|
||||
if (nq->connectivity.probe_status == PROBE_STATUS_IN_PROGRESS) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe already in progress for node %016llx", (unsigned long long)nq->node.node_id);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe already in progress for node %016llx", (unsigned long long)nq->node->node_id);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -300,143 +308,91 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct NODEIN
|
||||
int pend = 0;
|
||||
|
||||
// ---- IPv4 addresses ----
|
||||
const struct NODEINFO_IPV4_ADDR* addrs;
|
||||
int addr_count = get_node_v4_addrs(nq, &addrs);
|
||||
if (addr_count > 0) {
|
||||
uint32_t seen_ips[16]; uint16_t seen_ports[16];
|
||||
int seen_count = 0;
|
||||
|
||||
for (int i = 0; i < addr_count; i++) {
|
||||
uint32_t ip; memcpy(&ip, addrs[i].addr, 4);
|
||||
uint16_t port = addrs[i].port;
|
||||
if (nq->node && nq->node->v4_addrs) {
|
||||
uint32_t seen_ips[16]; uint16_t seen_ports[16]; int seen_count = 0;
|
||||
for (const struct TOPO_ADDR4* a = nq->node->v4_addrs; a; a = a->next) {
|
||||
uint32_t ip; memcpy(&ip, a->addr, 4); uint16_t port = a->port;
|
||||
if (ip == 0 || port == 0) continue;
|
||||
|
||||
int dup = 0;
|
||||
for (int j = 0; j < seen_count; j++) {
|
||||
if (seen_ips[j] == ip && seen_ports[j] == port) { dup = 1; break; }
|
||||
}
|
||||
int dup = 0; for (int j = 0; j < seen_count; j++) { if (seen_ips[j] == ip && seen_ports[j] == port) { dup = 1; break; } }
|
||||
if (dup) continue;
|
||||
if (seen_count >= 16) break;
|
||||
seen_ips[seen_count] = ip; seen_ports[seen_count] = port; seen_count++;
|
||||
|
||||
struct sockaddr_storage target;
|
||||
memset(&target, 0, sizeof(target));
|
||||
struct sockaddr_in* sin = (struct sockaddr_in*)⌖
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr.s_addr = ip;
|
||||
sin->sin_port = htons(port);
|
||||
struct sockaddr_storage target; memset(&target, 0, sizeof(target));
|
||||
struct sockaddr_in* sin = (struct sockaddr_in*)⌖ sin->sin_family = AF_INET; sin->sin_addr.s_addr = ip; sin->sin_port = htons(port);
|
||||
|
||||
struct ETCP_SOCKET* candidates[CONN_MAX_SOCKET_CANDIDATES];
|
||||
int cand_count = conn_match_candidate_sockets(instance, addrs[i].type, &target,
|
||||
candidates, CONN_MAX_SOCKET_CANDIDATES);
|
||||
if (cand_count == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for target %s:%u type=%d",
|
||||
ip_to_str(&ip, AF_INET).str, port, addrs[i].type);
|
||||
continue;
|
||||
}
|
||||
int cand_count = conn_match_candidate_sockets(instance, a->type, &target, candidates, CONN_MAX_SOCKET_CANDIDATES);
|
||||
if (cand_count == 0) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for target %s:%u type=%d", ip_to_str(&ip, AF_INET).str, port, a->type); continue; }
|
||||
|
||||
struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx));
|
||||
if (!ctx) continue;
|
||||
ctx->instance = instance;
|
||||
ctx->nq = nq;
|
||||
ctx->addr_type = addrs[i].type;
|
||||
ctx->target_addr = target;
|
||||
memcpy(ctx->peer_pubkey, nq->node.public_key, SC_PUBKEY_SIZE);
|
||||
struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx)); if (!ctx) continue;
|
||||
ctx->instance = instance; ctx->nq = nq; ctx->addr_type = a->type; ctx->target_addr = target;
|
||||
memcpy(ctx->peer_pubkey, nq->node->public_key, SC_PUBKEY_SIZE);
|
||||
memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*));
|
||||
ctx->candidate_count = cand_count;
|
||||
ctx->candidate_index = 0;
|
||||
ctx->count_total = CONN_PROBE_COUNT;
|
||||
ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS;
|
||||
ctx->best_across_sockets = 65535;
|
||||
|
||||
pend++;
|
||||
ctx->candidate_count = cand_count; ctx->candidate_index = 0; ctx->count_total = CONN_PROBE_COUNT; ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535;
|
||||
ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list; nq->connectivity.probe_list = ctx; pend++;
|
||||
conn_probe_start_series(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- IPv6 addresses ----
|
||||
const struct NODEINFO_IPV6_ADDR* addrs6;
|
||||
int addr6_count = get_node_v6_addrs(nq, &addrs6);
|
||||
if (addr6_count > 0) {
|
||||
uint8_t seen_ip6s[16][16]; uint16_t seen_ports6[16];
|
||||
int seen6_count = 0;
|
||||
|
||||
for (int i = 0; i < addr6_count; i++) {
|
||||
if (addrs6[i].port == 0) continue;
|
||||
// skip zero address
|
||||
int zero = 1;
|
||||
for (int z = 0; z < 16; z++) if (addrs6[i].addr[z] != 0) { zero = 0; break; }
|
||||
if (zero) continue;
|
||||
|
||||
int dup = 0;
|
||||
for (int j = 0; j < seen6_count; j++) {
|
||||
if (memcmp(seen_ip6s[j], addrs6[i].addr, 16) == 0 && seen_ports6[j] == addrs6[i].port) { dup = 1; break; }
|
||||
}
|
||||
if (nq->node && nq->node->v6_addrs) {
|
||||
uint8_t seen_ip6s[16][16]; uint16_t seen_ports6[16]; int seen6_count = 0;
|
||||
for (const struct TOPO_ADDR6* a6 = nq->node->v6_addrs; a6; a6 = a6->next) {
|
||||
if (a6->port == 0) continue;
|
||||
int zero = 1; for (int z = 0; z < 16; z++) if (a6->addr[z] != 0) { zero = 0; break; } if (zero) continue;
|
||||
int dup = 0; for (int j = 0; j < seen6_count; j++) { if (memcmp(seen_ip6s[j], a6->addr, 16) == 0 && seen_ports6[j] == a6->port) { dup = 1; break; } }
|
||||
if (dup) continue;
|
||||
if (seen6_count >= 16) break;
|
||||
memcpy(seen_ip6s[seen6_count], addrs6[i].addr, 16);
|
||||
seen_ports6[seen6_count] = addrs6[i].port;
|
||||
seen6_count++;
|
||||
memcpy(seen_ip6s[seen6_count], a6->addr, 16); seen_ports6[seen6_count] = a6->port; seen6_count++;
|
||||
|
||||
struct sockaddr_storage target;
|
||||
memset(&target, 0, sizeof(target));
|
||||
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)⌖
|
||||
sin6->sin6_family = AF_INET6;
|
||||
memcpy(&sin6->sin6_addr, addrs6[i].addr, 16);
|
||||
sin6->sin6_port = htons(addrs6[i].port);
|
||||
struct sockaddr_storage target; memset(&target, 0, sizeof(target));
|
||||
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)⌖ sin6->sin6_family = AF_INET6;
|
||||
memcpy(&sin6->sin6_addr, a6->addr, 16); sin6->sin6_port = htons(a6->port);
|
||||
|
||||
struct ETCP_SOCKET* candidates[CONN_MAX_SOCKET_CANDIDATES];
|
||||
int cand_count = conn_match_candidate_sockets(instance, addrs6[i].type, &target,
|
||||
candidates, CONN_MAX_SOCKET_CANDIDATES);
|
||||
if (cand_count == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for IPv6 target type=%d", addrs6[i].type);
|
||||
continue;
|
||||
}
|
||||
int cand_count = conn_match_candidate_sockets(instance, a6->type, &target, candidates, CONN_MAX_SOCKET_CANDIDATES);
|
||||
if (cand_count == 0) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for IPv6 target type=%d", a6->type); continue; }
|
||||
|
||||
struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx));
|
||||
if (!ctx) continue;
|
||||
ctx->instance = instance;
|
||||
ctx->nq = nq;
|
||||
ctx->addr_type = addrs6[i].type;
|
||||
ctx->target_addr = target;
|
||||
memcpy(ctx->peer_pubkey, nq->node.public_key, SC_PUBKEY_SIZE);
|
||||
struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx)); if (!ctx) continue;
|
||||
ctx->instance = instance; ctx->nq = nq; ctx->addr_type = a6->type; ctx->target_addr = target;
|
||||
memcpy(ctx->peer_pubkey, nq->node->public_key, SC_PUBKEY_SIZE);
|
||||
memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*));
|
||||
ctx->candidate_count = cand_count;
|
||||
ctx->candidate_index = 0;
|
||||
ctx->count_total = CONN_PROBE_COUNT;
|
||||
ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS;
|
||||
ctx->best_across_sockets = 65535;
|
||||
|
||||
pend++;
|
||||
ctx->candidate_count = cand_count; ctx->candidate_index = 0; ctx->count_total = CONN_PROBE_COUNT; ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535;
|
||||
ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list; nq->connectivity.probe_list = ctx; pend++;
|
||||
conn_probe_start_series(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (pend == 0) {
|
||||
nq->connectivity.probe_status = PROBE_STATUS_DONE;
|
||||
if (addr_count == 0 && addr6_count == 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "no addresses to probe for node %016llx", (unsigned long long)nq->node.node_id);
|
||||
if (!nq->node->v4_addrs && !nq->node->v6_addrs) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "no addresses to probe for node %016llx", (unsigned long long)nq->node->node_id);
|
||||
}
|
||||
} else {
|
||||
nq->connectivity.pending_count = pend;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe started for node %016llx: %d series pending",
|
||||
(unsigned long long)nq->node.node_id, pend);
|
||||
(unsigned long long)nq->node->node_id, pend);
|
||||
}
|
||||
}
|
||||
|
||||
void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, struct NODEINFO_Q* nq) {
|
||||
void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, struct TOPO_NODEQ* nq) {
|
||||
if (!instance || !nq) return;
|
||||
nq->connectivity.probe_status = PROBE_STATUS_NONE;
|
||||
nq->connectivity.pending_count = 0;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe cancelled for node %016llx", (unsigned long long)nq->node.node_id);
|
||||
struct conn_probe_ctx* ctx = (struct conn_probe_ctx*)nq->connectivity.probe_list;
|
||||
while (ctx) { ctx->nq = NULL; ctx = ctx->next; }
|
||||
nq->connectivity.probe_list = NULL;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe cancelled for node %016llx", (unsigned long long)nq->node->node_id);
|
||||
}
|
||||
|
||||
void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance) {
|
||||
if (!instance || !instance->bgp || !instance->bgp->nodes) return;
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(instance->topo_groups);
|
||||
if (!instance || !group || !group->nodes) return;
|
||||
int count = 0;
|
||||
struct ll_entry* e = instance->bgp->nodes->head;
|
||||
struct ll_entry* e = group->nodes->head;
|
||||
while (e) {
|
||||
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e;
|
||||
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e;
|
||||
nq->connectivity.probe_status = PROBE_STATUS_NONE;
|
||||
nq->connectivity.pending_count = 0;
|
||||
e = e->next;
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
#ifndef ROUTE_CONNECTIVITY_H
|
||||
#define ROUTE_CONNECTIVITY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "route_node.h"
|
||||
#include "topo_node.h"
|
||||
|
||||
struct UTUN_INSTANCE;
|
||||
|
||||
// Запускает зондирование связности ко всем адресам удалённого узла
|
||||
void route_connectivity_probe_node(struct UTUN_INSTANCE* instance,
|
||||
struct NODEINFO_Q* nq);
|
||||
struct TOPO_NODEQ* nq);
|
||||
|
||||
// Отменяет все pending пробы для узла (при удалении / withdraw)
|
||||
void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance,
|
||||
struct NODEINFO_Q* nq);
|
||||
struct TOPO_NODEQ* nq);
|
||||
|
||||
// Отменяет все pending пробы для всех узлов (при destroy)
|
||||
void route_connectivity_cancel_all(struct UTUN_INSTANCE* instance);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // ROUTE_CONNECTIVITY_H
|
||||
+41
-85
@@ -5,7 +5,7 @@
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/platform_compat.h"
|
||||
#include "utun_instance.h"
|
||||
#include "route_node.h"
|
||||
#include "topo_node.h"
|
||||
#include "route_lib.h"
|
||||
#include "etcp_debug.h"
|
||||
|
||||
@@ -21,10 +21,7 @@ static uint32_t prefix_to_mask(uint8_t prefix) {
|
||||
}
|
||||
|
||||
static bool routes_overlap(uint32_t net1, uint8_t pre1, uint32_t net2, uint8_t pre2) {
|
||||
uint32_t mask1 = prefix_to_mask(pre1);
|
||||
uint32_t mask2 = prefix_to_mask(pre2);
|
||||
if ((net1 & mask2) == net2 && pre1 >= pre2) return true;
|
||||
if ((net2 & mask1) == net1 && pre2 >= pre1) return true;
|
||||
if (pre1 == pre2 && net1 == net2) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -113,13 +110,13 @@ static struct ROUTE_ENTRY* binary_search_lpm(struct ROUTE_TABLE *table, uint32_t
|
||||
struct ROUTE_TABLE *route_table_create(void) {
|
||||
struct ROUTE_TABLE *table = u_calloc(1, sizeof(struct ROUTE_TABLE));
|
||||
if (!table) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_table_create: alloc failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "alloc failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
table->entries = u_calloc(INITIAL_CAPACITY, sizeof(struct ROUTE_ENTRY));
|
||||
if (!table->entries) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_table_create: alloc entries failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "alloc entries failed");
|
||||
u_free(table);
|
||||
return NULL;
|
||||
}
|
||||
@@ -137,9 +134,9 @@ struct ROUTE_TABLE *route_table_create(void) {
|
||||
void route_table_destroy(struct ROUTE_TABLE *table) {
|
||||
if (!table) return;
|
||||
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_table_destroy: count=%zu", table->count);
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "count=%zu", table->count);
|
||||
|
||||
// v_node_info — внешний объект (NODEINFO_Q), память им не управляем
|
||||
// v_node_info — внешний объект (TOPO_NODEQ), память им не управляем
|
||||
u_free(table->dynamic_subnets);
|
||||
u_free(table->local_subnets);
|
||||
u_free(table->entries);
|
||||
@@ -148,121 +145,80 @@ void route_table_destroy(struct ROUTE_TABLE *table) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "Routing table destroyed");
|
||||
}
|
||||
|
||||
bool route_insert(struct ROUTE_TABLE *table, struct NODEINFO_Q *node) {
|
||||
if (!table || !node) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: invalid arguments");
|
||||
bool route_insert(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node) {
|
||||
if (!table || !node) { DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "invalid arguments"); return false; }
|
||||
|
||||
if (!node->subnets || !node->subnets->v4_subnets) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "node has no v4 subnets");
|
||||
return false;
|
||||
}
|
||||
|
||||
// === Вызов сторонней функции (теперь без malloc) ===
|
||||
const struct NODEINFO_IPV4_SUBNET *subnets = NULL;
|
||||
int cnt = get_node_routes(node, &subnets);
|
||||
|
||||
if (cnt <= 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_insert: get_node_routes returned %d subnets", cnt);
|
||||
return false;
|
||||
}
|
||||
|
||||
const struct TOPO_SUBNET4* sub = node->subnets->v4_subnets;
|
||||
int cnt = 0; for (const struct TOPO_SUBNET4* s = sub; s; s = s->next) cnt++;
|
||||
if (cnt == 0) { DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "0 subnets"); return false; }
|
||||
size_t count = (size_t)cnt;
|
||||
|
||||
// === Проверка пересечений ===
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
uint32_t network;
|
||||
memcpy(&network, subnets[i].addr, 4);
|
||||
network = ntohl(network);
|
||||
uint8_t prefix = subnets[i].prefix_length;
|
||||
|
||||
if (check_route_overlap_in_table(network, prefix,
|
||||
table->entries, table->count)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_insert: overlap detected for %s/%d",
|
||||
ip_to_string(network).a, prefix);
|
||||
for (const struct TOPO_SUBNET4* s = sub; s; s = s->next) {
|
||||
uint32_t network; memcpy(&network, s->addr, 4); network = ntohl(network);
|
||||
if (check_route_overlap_in_table(network, s->prefix_length, table->entries, table->count)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "overlap detected for %s/%d", ip_to_string(network).a, s->prefix_length);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// === Реаллокация при необходимости ===
|
||||
if (table->count + count > table->capacity) {
|
||||
size_t new_cap = table->capacity * 2;
|
||||
while (new_cap < table->count + count) new_cap *= 2;
|
||||
struct ROUTE_ENTRY *new_entries = u_realloc(table->entries, new_cap * sizeof(struct ROUTE_ENTRY));
|
||||
if (!new_entries) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "route_insert: realloc failed");
|
||||
return false;
|
||||
}
|
||||
table->entries = new_entries;
|
||||
table->capacity = new_cap;
|
||||
struct ROUTE_ENTRY* new_entries = u_realloc(table->entries, new_cap * sizeof(struct ROUTE_ENTRY));
|
||||
if (!new_entries) { DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "realloc failed"); return false; }
|
||||
table->entries = new_entries; table->capacity = new_cap;
|
||||
}
|
||||
|
||||
// === Вставка каждого префикса (таблица остаётся отсортированной) ===
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
uint32_t network;
|
||||
memcpy(&network, subnets[i].addr, 4);
|
||||
network = ntohl(network);
|
||||
uint8_t prefix_length = subnets[i].prefix_length;
|
||||
|
||||
int pos = binary_search_insert_pos(table->entries, table->count, network, prefix_length);
|
||||
|
||||
if (pos < (int)table->count) {
|
||||
memmove(&table->entries[pos + 1], &table->entries[pos],
|
||||
(table->count - pos) * sizeof(struct ROUTE_ENTRY));
|
||||
}
|
||||
|
||||
struct ROUTE_ENTRY *e = &table->entries[pos];
|
||||
e->network = network;
|
||||
e->prefix_length = prefix_length;
|
||||
e->v_node_info = node;
|
||||
|
||||
for (const struct TOPO_SUBNET4* s = sub; s; s = s->next) {
|
||||
uint32_t network; memcpy(&network, s->addr, 4); network = ntohl(network);
|
||||
int pos = binary_search_insert_pos(table->entries, table->count, network, s->prefix_length);
|
||||
if (pos < (int)table->count) memmove(&table->entries[pos + 1], &table->entries[pos], (table->count - pos) * sizeof(struct ROUTE_ENTRY));
|
||||
struct ROUTE_ENTRY* e = &table->entries[pos];
|
||||
e->network = network; e->prefix_length = s->prefix_length; e->v_node_info = node;
|
||||
table->count++;
|
||||
if (node && node->node.hop_count == 0) table->stats.local_routes++; else table->stats.learned_routes++;
|
||||
if (node->hop_count == 0) table->stats.local_routes++; else table->stats.learned_routes++;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_insert: added %zu route(s) for node=%016llx",
|
||||
count, node ? (unsigned long long)node->node.node_id : 0ULL);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "added %zu route(s) for node=%016llx",
|
||||
count, (unsigned long long)node->node->node_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void route_delete(struct ROUTE_TABLE *table, struct NODEINFO_Q *node) {
|
||||
void route_delete(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node) {
|
||||
if (!table || !node) return;
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_delete: removing all routes for node=%016llx",
|
||||
(unsigned long long)node->node.node_id);
|
||||
|
||||
size_t i = 0;
|
||||
size_t removed = 0;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "removing all routes for node=%016llx", (unsigned long long)node->node->node_id);
|
||||
size_t i = 0, removed = 0;
|
||||
while (i < table->count) {
|
||||
if (table->entries[i].v_node_info == node) {
|
||||
if (i < table->count - 1) {
|
||||
memmove(&table->entries[i], &table->entries[i + 1],
|
||||
(table->count - i - 1) * sizeof(struct ROUTE_ENTRY));
|
||||
}
|
||||
if (i < table->count - 1) memmove(&table->entries[i], &table->entries[i + 1], (table->count - i - 1) * sizeof(struct ROUTE_ENTRY));
|
||||
table->count--;
|
||||
if (node && node->node.hop_count == 0) table->stats.local_routes--; else table->stats.learned_routes--;
|
||||
removed++;
|
||||
continue;
|
||||
if (node->hop_count == 0) table->stats.local_routes--; else table->stats.learned_routes--;
|
||||
removed++; continue;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (removed > 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "route_delete: removed %zu route(s)", removed);
|
||||
}
|
||||
if (removed > 0) DEBUG_INFO(DEBUG_CATEGORY_ROUTING, "removed %zu route(s)", removed);
|
||||
}
|
||||
|
||||
struct ROUTE_ENTRY* route_lookup(struct ROUTE_TABLE *table, uint32_t dest_ip) {
|
||||
if (!table) return NULL;
|
||||
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: dest_ip=%s", ip_to_string(dest_ip).a);
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "dest_ip=%s", ip_to_string(dest_ip).a);
|
||||
|
||||
struct ROUTE_ENTRY *result = binary_search_lpm(table, dest_ip);
|
||||
|
||||
if (result) {
|
||||
table->stats.routes_lookup_hits++;
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: FOUND %s/%d (node_info=%p)",
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "FOUND %s/%d (node_info=%p)",
|
||||
ip_to_string(result->network).a, result->prefix_length,
|
||||
(void*)result->v_node_info);
|
||||
} else {
|
||||
table->stats.routes_lookup_misses++;
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_lookup: NOT FOUND");
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "NOT FOUND");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -280,7 +236,7 @@ void route_table_print(const struct ROUTE_TABLE *table) {
|
||||
const struct ROUTE_ENTRY *entry = &table->entries[i];
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " %zu: %s/%d",
|
||||
i + 1, ip_to_string(entry->network).a, entry->prefix_length);
|
||||
if (entry->v_node_info && entry->v_node_info->node.hop_count != 0) {
|
||||
if (entry->v_node_info && entry->v_node_info->hop_count != 0) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " v_node_info=%p", (void*)entry->v_node_info);
|
||||
} else {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ROUTING, " LOCAL");
|
||||
|
||||
+13
-4
@@ -1,5 +1,10 @@
|
||||
#ifndef ROUTE_LIB_H
|
||||
#define ROUTE_LIB_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
@@ -10,7 +15,7 @@
|
||||
struct ETCP_CONNECTIONS;
|
||||
struct ROUTE_TABLE;
|
||||
struct ROUTE_ENTRY;
|
||||
struct NODEINFO_Q;
|
||||
struct TOPO_NODEQ;
|
||||
|
||||
/**
|
||||
* @brief Флаги узла
|
||||
@@ -29,7 +34,7 @@ typedef enum {
|
||||
struct ROUTE_ENTRY {
|
||||
uint32_t network; // Сетевой адрес (big-endian)
|
||||
uint8_t prefix_length; // Длина префикса подсети
|
||||
struct NODEINFO_Q* v_node_info; // узел владелец этих маршрутов. null если - локальный маршрут.
|
||||
struct TOPO_NODEQ* v_node_info; // узел владелец этих маршрутов. null если - локальный маршрут.
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -74,7 +79,7 @@ void route_table_destroy(struct ROUTE_TABLE *table);
|
||||
*
|
||||
* @return true если вставка/обновление успешно
|
||||
*/
|
||||
bool route_insert(struct ROUTE_TABLE *table, struct NODEINFO_Q *node);
|
||||
bool route_insert(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node);
|
||||
|
||||
/**
|
||||
* @brief Удаляет все записи из таблицы маршрутизации для указанного узла
|
||||
@@ -82,7 +87,7 @@ bool route_insert(struct ROUTE_TABLE *table, struct NODEINFO_Q *node);
|
||||
* @param table Указатель на таблицу маршрутизации
|
||||
* @param node узел, все маршруты которого нужно удалить
|
||||
*/
|
||||
void route_delete(struct ROUTE_TABLE *table, struct NODEINFO_Q *node);
|
||||
void route_delete(struct ROUTE_TABLE *table, struct TOPO_NODEQ *node);
|
||||
|
||||
/**
|
||||
* @brief Выполняет поиск маршрута для заданного IP-адреса
|
||||
@@ -120,4 +125,8 @@ bool route_add_local_subnet(struct ROUTE_TABLE *table, uint32_t network, uint8_t
|
||||
*/
|
||||
int is_local_subnet(uint32_t ip);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // ROUTE_LIB_H
|
||||
@@ -1,318 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "utun_instance.h"
|
||||
#include "etcp.h"
|
||||
#include "config_parser.h"
|
||||
#include "route_node.h"
|
||||
#include "route_bgp.h"
|
||||
#include "etcp_debug.h"
|
||||
|
||||
static const uint8_t* node_dyn_start(const struct NODEINFO* info) {
|
||||
return (const uint8_t*)info + sizeof(struct NODEINFO);
|
||||
}
|
||||
|
||||
static const uint8_t* skip_name(const uint8_t* p, uint8_t name_len) {
|
||||
return p + name_len;
|
||||
}
|
||||
|
||||
static const uint8_t* skip_v4_sockets_meta(const uint8_t* p, uint8_t count) {
|
||||
return p + count * sizeof(struct NODEINFO_IPV4_SOCKET_META);
|
||||
}
|
||||
|
||||
static const uint8_t* skip_v4_addrs(const uint8_t* p, uint8_t count) {
|
||||
return p + count * sizeof(struct NODEINFO_IPV4_ADDR);
|
||||
}
|
||||
|
||||
static const uint8_t* skip_v6_sockets_meta(const uint8_t* p, uint8_t count) {
|
||||
return p + count * sizeof(struct NODEINFO_IPV6_SOCKET_META);
|
||||
}
|
||||
|
||||
static const uint8_t* skip_v6_addrs(const uint8_t* p, uint8_t count) {
|
||||
return p + count * sizeof(struct NODEINFO_IPV6_ADDR);
|
||||
}
|
||||
|
||||
static const uint8_t* skip_v4_subnets(const uint8_t* p, uint8_t count) {
|
||||
return p + count * sizeof(struct NODEINFO_IPV4_SUBNET);
|
||||
}
|
||||
|
||||
int get_node_v4_sockets_meta(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SOCKET_META **out_meta) {
|
||||
if (!node || !out_meta) return -1;
|
||||
*out_meta = NULL;
|
||||
const struct NODEINFO *info = &node->node;
|
||||
if (info->local_v4_sockets == 0) return 0;
|
||||
const uint8_t *dynamic = node_dyn_start(info);
|
||||
dynamic = skip_name(dynamic, info->node_name_len);
|
||||
*out_meta = (const struct NODEINFO_IPV4_SOCKET_META *)dynamic;
|
||||
return (int)info->local_v4_sockets;
|
||||
}
|
||||
|
||||
int get_node_v4_addrs(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_ADDR **out_addrs) {
|
||||
if (!node || !out_addrs) return -1;
|
||||
*out_addrs = NULL;
|
||||
const struct NODEINFO *info = &node->node;
|
||||
if (info->local_v4_addrs == 0) return 0;
|
||||
const uint8_t *dynamic = node_dyn_start(info);
|
||||
dynamic = skip_name(dynamic, info->node_name_len);
|
||||
dynamic = skip_v4_sockets_meta(dynamic, info->local_v4_sockets);
|
||||
*out_addrs = (const struct NODEINFO_IPV4_ADDR *)dynamic;
|
||||
return (int)info->local_v4_addrs;
|
||||
}
|
||||
|
||||
int get_node_v6_sockets_meta(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_SOCKET_META **out_meta) {
|
||||
if (!node || !out_meta) return -1;
|
||||
*out_meta = NULL;
|
||||
const struct NODEINFO *info = &node->node;
|
||||
if (info->local_v6_sockets == 0) return 0;
|
||||
const uint8_t *dynamic = node_dyn_start(info);
|
||||
dynamic = skip_name(dynamic, info->node_name_len);
|
||||
dynamic = skip_v4_sockets_meta(dynamic, info->local_v4_sockets);
|
||||
dynamic = skip_v4_addrs(dynamic, info->local_v4_addrs);
|
||||
*out_meta = (const struct NODEINFO_IPV6_SOCKET_META *)dynamic;
|
||||
return (int)info->local_v6_sockets;
|
||||
}
|
||||
|
||||
int get_node_v6_addrs(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_ADDR **out_addrs) {
|
||||
if (!node || !out_addrs) return -1;
|
||||
*out_addrs = NULL;
|
||||
const struct NODEINFO *info = &node->node;
|
||||
if (info->local_v6_addrs == 0) return 0;
|
||||
const uint8_t *dynamic = node_dyn_start(info);
|
||||
dynamic = skip_name(dynamic, info->node_name_len);
|
||||
dynamic = skip_v4_sockets_meta(dynamic, info->local_v4_sockets);
|
||||
dynamic = skip_v4_addrs(dynamic, info->local_v4_addrs);
|
||||
dynamic = skip_v6_sockets_meta(dynamic, info->local_v6_sockets);
|
||||
*out_addrs = (const struct NODEINFO_IPV6_ADDR *)dynamic;
|
||||
return (int)info->local_v6_addrs;
|
||||
}
|
||||
|
||||
int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets) {
|
||||
if (!node || !out_subnets) return -1;
|
||||
*out_subnets = NULL;
|
||||
const struct NODEINFO *info = &node->node;
|
||||
if (info->local_v4_subnets == 0) return 0;
|
||||
const uint8_t *dynamic = node_dyn_start(info);
|
||||
dynamic = skip_name(dynamic, info->node_name_len);
|
||||
dynamic = skip_v4_sockets_meta(dynamic, info->local_v4_sockets);
|
||||
dynamic = skip_v4_addrs(dynamic, info->local_v4_addrs);
|
||||
dynamic = skip_v6_sockets_meta(dynamic, info->local_v6_sockets);
|
||||
dynamic = skip_v6_addrs(dynamic, info->local_v6_addrs);
|
||||
*out_subnets = (const struct NODEINFO_IPV4_SUBNET *)dynamic;
|
||||
return (int)info->local_v4_subnets;
|
||||
}
|
||||
|
||||
int get_node_v6_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_SUBNET **out_subnets) {
|
||||
if (!node || !out_subnets) return -1;
|
||||
*out_subnets = NULL;
|
||||
const struct NODEINFO *info = &node->node;
|
||||
if (info->local_v6_subnets == 0) return 0;
|
||||
const uint8_t *dynamic = node_dyn_start(info);
|
||||
dynamic = skip_name(dynamic, info->node_name_len);
|
||||
dynamic = skip_v4_sockets_meta(dynamic, info->local_v4_sockets);
|
||||
dynamic = skip_v4_addrs(dynamic, info->local_v4_addrs);
|
||||
dynamic = skip_v6_sockets_meta(dynamic, info->local_v6_sockets);
|
||||
dynamic = skip_v6_addrs(dynamic, info->local_v6_addrs);
|
||||
dynamic = skip_v4_subnets(dynamic, info->local_v4_subnets);
|
||||
*out_subnets = (const struct NODEINFO_IPV6_SUBNET *)dynamic;
|
||||
return (int)info->local_v6_subnets;
|
||||
}
|
||||
|
||||
struct NODEINFO_Q* nodeinfo_find_by_id(struct ROUTE_BGP* bgp, uint64_t node_id) {
|
||||
if (!bgp || !bgp->nodes) return NULL;
|
||||
uint64_t key = node_id;
|
||||
struct ll_entry* e = queue_find_data_by_index(bgp->nodes, &key);
|
||||
return e ? (struct NODEINFO_Q*)e : NULL;
|
||||
}
|
||||
|
||||
int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp) {
|
||||
if (!instance || !bgp) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_update_my_nodeinfo: invalid args");
|
||||
return -1;
|
||||
}
|
||||
size_t name_len = 0;
|
||||
if (instance->name[0]) {
|
||||
name_len = strlen(instance->name);
|
||||
if (name_len > 63) name_len = 63;
|
||||
}
|
||||
int vc = 0, vc6 = 0;
|
||||
struct CFG_ROUTE_ENTRY* s = instance->config->my_subnets;
|
||||
while (s) { if (s->ip.family == AF_INET) vc++; else if (s->ip.family == AF_INET6) vc6++; s = s->next; }
|
||||
|
||||
int sock_count = 0, addr_count = 0;
|
||||
int sock6_count = 0, addr6_count = 0;
|
||||
struct ETCP_SOCKET* e_sock = instance->etcp_sockets;
|
||||
while (e_sock) {
|
||||
if (e_sock->local_addr.ss_family == AF_INET) {
|
||||
sock_count++;
|
||||
addr_count++; // INTERFACE
|
||||
if (e_sock->nat_addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in* nat_sin = (struct sockaddr_in*)&e_sock->nat_addr;
|
||||
if (nat_sin->sin_addr.s_addr != 0) {
|
||||
if (e_sock->nat_type != NAT_VERIFIED_STRICT) addr_count++;
|
||||
}
|
||||
}
|
||||
if (e_sock->nat_type == NAT_VERIFIED_DIRECT ||
|
||||
(e_sock->type == CFG_SERVER_TYPE_PUBLIC && e_sock->nat_type < NAT_VERIFIED_UNKNOWN)) addr_count++;
|
||||
} else if (e_sock->local_addr.ss_family == AF_INET6) {
|
||||
sock6_count++;
|
||||
addr6_count++; // INTERFACE only (no NAT for IPv6)
|
||||
}
|
||||
e_sock = e_sock->next;
|
||||
}
|
||||
|
||||
size_t dyn = name_len + sock_count * sizeof(struct NODEINFO_IPV4_SOCKET_META)
|
||||
+ addr_count * sizeof(struct NODEINFO_IPV4_ADDR)
|
||||
+ sock6_count * sizeof(struct NODEINFO_IPV6_SOCKET_META)
|
||||
+ addr6_count * sizeof(struct NODEINFO_IPV6_ADDR)
|
||||
+ vc * sizeof(struct NODEINFO_IPV4_SUBNET)
|
||||
+ vc6 * sizeof(struct NODEINFO_IPV6_SUBNET);
|
||||
|
||||
int changed = 1;
|
||||
uint8_t old_ver = 0;
|
||||
if (bgp->local_node) {
|
||||
old_ver = bgp->local_node->node.ver;
|
||||
changed = (vc != (int)bgp->local_node->node.local_v4_subnets)
|
||||
|| (sock_count != (int)bgp->local_node->node.local_v4_sockets)
|
||||
|| (addr_count != (int)bgp->local_node->node.local_v4_addrs)
|
||||
|| (name_len != bgp->local_node->node.node_name_len)
|
||||
|| (memcmp(bgp->local_node->node.public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE) != 0)
|
||||
|| (vc6 != (int)bgp->local_node->node.local_v6_subnets)
|
||||
|| (sock6_count != (int)bgp->local_node->node.local_v6_sockets)
|
||||
|| (addr6_count != (int)bgp->local_node->node.local_v6_addrs);
|
||||
if (bgp->local_node && bgp->instance && bgp->instance->bgp) {
|
||||
if (memcmp(bgp->local_node->node.ed25519_public_key, bgp->instance->bgp->ed25519_public_key, SC_PUBKEY_SIZE) != 0) changed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (bgp->local_node) u_free(bgp->local_node);
|
||||
bgp->local_node = u_calloc(1, sizeof(struct NODEINFO_Q) + dyn);
|
||||
if (!bgp->local_node) return -1;
|
||||
bgp->local_node->node.node_id = instance->node_id;
|
||||
bgp->local_node->node.hop_count = 0;
|
||||
bgp->local_node->node.ver = (old_ver % 255) + 1;
|
||||
bgp->local_node->node.local_v4_sockets = sock_count;
|
||||
bgp->local_node->node.local_v4_addrs = addr_count;
|
||||
bgp->local_node->node.local_v6_sockets = sock6_count;
|
||||
bgp->local_node->node.local_v6_addrs = addr6_count;
|
||||
bgp->local_node->node.local_v4_subnets = vc;
|
||||
bgp->local_node->node.local_v6_subnets = vc6;
|
||||
bgp->local_node->node.node_name_len = name_len;
|
||||
memcpy(bgp->local_node->node.public_key, instance->my_keys.public_key, SC_PUBKEY_SIZE);
|
||||
memcpy(bgp->local_node->node.ed25519_public_key, bgp->ed25519_public_key, SC_PUBKEY_SIZE);
|
||||
bgp->local_node->dirty = 1;
|
||||
bgp->local_node->last_ver = bgp->local_node->node.ver;
|
||||
|
||||
uint8_t* dp = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO);
|
||||
if (name_len) { memcpy(dp, instance->name, name_len); dp += name_len; }
|
||||
|
||||
// Write IPv4 socket meta
|
||||
struct NODEINFO_IPV4_SOCKET_META* meta = (struct NODEINFO_IPV4_SOCKET_META*)dp;
|
||||
e_sock = instance->etcp_sockets;
|
||||
int midx = 0;
|
||||
while (e_sock) {
|
||||
if (e_sock->local_addr.ss_family == AF_INET) {
|
||||
meta[midx].id = e_sock->sock_id;
|
||||
meta[midx].type = e_sock->nat_type;
|
||||
midx++;
|
||||
}
|
||||
e_sock = e_sock->next;
|
||||
}
|
||||
dp += sock_count * sizeof(struct NODEINFO_IPV4_SOCKET_META);
|
||||
|
||||
// Write IPv4 addresses
|
||||
struct NODEINFO_IPV4_ADDR* addrs = (struct NODEINFO_IPV4_ADDR*)dp;
|
||||
int aidx = 0;
|
||||
e_sock = instance->etcp_sockets;
|
||||
while (e_sock) {
|
||||
if (e_sock->local_addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in* if_sin = (struct sockaddr_in*)&e_sock->interface_addr;
|
||||
struct sockaddr_in* nat_sin = (struct sockaddr_in*)&e_sock->nat_addr;
|
||||
|
||||
memcpy(addrs[aidx].addr, &if_sin->sin_addr.s_addr, 4);
|
||||
addrs[aidx].port = ntohs(if_sin->sin_port);
|
||||
addrs[aidx].type = ADDR_TYPE_INTERFACE;
|
||||
addrs[aidx].socket_id = e_sock->sock_id;
|
||||
aidx++;
|
||||
|
||||
if (e_sock->nat_addr.ss_family == AF_INET && nat_sin->sin_addr.s_addr != 0) {
|
||||
if (e_sock->nat_type != NAT_VERIFIED_STRICT) {
|
||||
memcpy(addrs[aidx].addr, &nat_sin->sin_addr.s_addr, 4);
|
||||
addrs[aidx].port = ntohs(nat_sin->sin_port);
|
||||
addrs[aidx].type = ADDR_TYPE_NAT;
|
||||
addrs[aidx].socket_id = e_sock->sock_id;
|
||||
aidx++;
|
||||
}
|
||||
}
|
||||
if (e_sock->nat_type == NAT_VERIFIED_DIRECT ||
|
||||
(e_sock->type == CFG_SERVER_TYPE_PUBLIC && e_sock->nat_type < NAT_VERIFIED_UNKNOWN)) {
|
||||
memcpy(addrs[aidx].addr, &if_sin->sin_addr.s_addr, 4);
|
||||
addrs[aidx].port = ntohs(if_sin->sin_port);
|
||||
addrs[aidx].type = ADDR_TYPE_REAL;
|
||||
addrs[aidx].socket_id = e_sock->sock_id;
|
||||
aidx++;
|
||||
}
|
||||
}
|
||||
e_sock = e_sock->next;
|
||||
}
|
||||
dp += addr_count * sizeof(struct NODEINFO_IPV4_ADDR);
|
||||
|
||||
// Write IPv6 socket meta
|
||||
struct NODEINFO_IPV6_SOCKET_META* meta6 = (struct NODEINFO_IPV6_SOCKET_META*)dp;
|
||||
e_sock = instance->etcp_sockets;
|
||||
int m6idx = 0;
|
||||
while (e_sock) {
|
||||
if (e_sock->local_addr.ss_family == AF_INET6) {
|
||||
meta6[m6idx].id = e_sock->sock_id;
|
||||
meta6[m6idx].type = e_sock->nat_type;
|
||||
m6idx++;
|
||||
}
|
||||
e_sock = e_sock->next;
|
||||
}
|
||||
dp += sock6_count * sizeof(struct NODEINFO_IPV6_SOCKET_META);
|
||||
|
||||
// Write IPv6 addresses (INTERFACE only)
|
||||
struct NODEINFO_IPV6_ADDR* addrs6 = (struct NODEINFO_IPV6_ADDR*)dp;
|
||||
int a6idx = 0;
|
||||
e_sock = instance->etcp_sockets;
|
||||
while (e_sock) {
|
||||
if (e_sock->local_addr.ss_family == AF_INET6) {
|
||||
struct sockaddr_in6* if_sin6 = (struct sockaddr_in6*)&e_sock->interface_addr;
|
||||
memcpy(addrs6[a6idx].addr, &if_sin6->sin6_addr, 16);
|
||||
addrs6[a6idx].port = ntohs(if_sin6->sin6_port);
|
||||
addrs6[a6idx].type = ADDR_TYPE_INTERFACE;
|
||||
addrs6[a6idx].socket_id = e_sock->sock_id;
|
||||
a6idx++;
|
||||
}
|
||||
e_sock = e_sock->next;
|
||||
}
|
||||
dp += addr6_count * sizeof(struct NODEINFO_IPV6_ADDR);
|
||||
|
||||
// Write IPv4 subnets
|
||||
struct NODEINFO_IPV4_SUBNET* ra = (struct NODEINFO_IPV4_SUBNET*)dp;
|
||||
s = instance->config->my_subnets;
|
||||
while (s) {
|
||||
if (s->ip.family == AF_INET) { memcpy(ra->addr, &s->ip.addr.v4, 4); ra->prefix_length = s->netmask; ra++; }
|
||||
s = s->next;
|
||||
}
|
||||
dp += vc * sizeof(struct NODEINFO_IPV4_SUBNET);
|
||||
|
||||
// Write IPv6 subnets
|
||||
struct NODEINFO_IPV6_SUBNET* ra6 = (struct NODEINFO_IPV6_SUBNET*)dp;
|
||||
s = instance->config->my_subnets;
|
||||
while (s) {
|
||||
if (s->ip.family == AF_INET6) { memcpy(ra6->addr, &s->ip.addr.v6, 16); ra6->prefix_length = s->netmask; ra6++; }
|
||||
s = s->next;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "my_nodeinfo updated: v4s=%d v4a=%d v6s=%d v6a=%d v4sub=%d v6sub=%d ver=%d",
|
||||
sock_count, addr_count, sock6_count, addr6_count, vc, vc6, bgp->local_node->node.ver);
|
||||
} else {
|
||||
bgp->local_node->last_ver = bgp->local_node->node.ver;
|
||||
}
|
||||
return vc;
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
#ifndef ROUTE_NODE_H
|
||||
#define ROUTE_NODE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "../lib/ll_queue.h"
|
||||
#include "secure_channel.h"
|
||||
|
||||
struct ROUTE_BGP;
|
||||
struct ETCP_SOCKET;
|
||||
struct UTUN_INSTANCE;
|
||||
|
||||
// ---- типы адресов в плоском списке NODEINFO_IPV4_ADDR ----
|
||||
|
||||
#define ADDR_TYPE_INTERFACE 0 // interface_addr сокета (LAN)
|
||||
#define ADDR_TYPE_NAT 1 // nat_addr после детекции NAT
|
||||
#define ADDR_TYPE_REAL 2 // подтверждённый прямой интернет-адрес (nat проверка показала совпадение с interface_addr)
|
||||
|
||||
// ---- типы NAT (etcp_connections.h) ----
|
||||
// NAT_TYPE_UNKNOWN(0), NAT_TYPE_EIM(1), NAT_TYPE_STRICT(2)
|
||||
// NAT_VERIFIED_UNKNOWN(4), NAT_VERIFIED_EIM(5), NAT_VERIFIED_STRICT(6), NAT_VERIFIED_DIRECT(7)
|
||||
|
||||
// ---- статусы зондирования связности ----
|
||||
|
||||
#define PROBE_STATUS_NONE 0
|
||||
#define PROBE_STATUS_IN_PROGRESS 1
|
||||
#define PROBE_STATUS_DONE 2
|
||||
|
||||
#define PROBE_RESULT_UNKNOWN 0
|
||||
#define PROBE_RESULT_REACHABLE 1
|
||||
#define PROBE_RESULT_UNREACHABLE 2
|
||||
|
||||
#define CONN_PROBE_COUNT 3
|
||||
#define CONN_PROBE_TIMEOUT_MS 1000
|
||||
|
||||
#define CONN_TYPE_NONE 0
|
||||
#define CONN_TYPE_DIRECT 1
|
||||
#define CONN_TYPE_REVERSE 2
|
||||
#define CONN_TYPE_INDIRECT 3
|
||||
#define CONN_MGR_MAX_INTERMEDIARIES 3
|
||||
|
||||
// ---- состояние связности с удалённым узлом (локальное, не передаётся по BGP) ----
|
||||
|
||||
struct NODE_CONNECTIVITY {
|
||||
uint8_t probe_status; // PROBE_STATUS_*
|
||||
uint8_t pending_count; // активных probe series
|
||||
uint64_t probe_start_time; // timebase
|
||||
uint16_t interface_min_rtt; // x0.1ms
|
||||
uint16_t nat_min_rtt;
|
||||
uint16_t real_min_rtt;
|
||||
uint8_t interface_status; // PROBE_RESULT_*
|
||||
uint8_t nat_status;
|
||||
uint8_t real_status;
|
||||
uint8_t reserved;
|
||||
uint64_t interface_probe_time;
|
||||
uint64_t nat_probe_time;
|
||||
uint64_t real_probe_time;
|
||||
uint64_t ping_req_time; // время последнего полученного ping request от этого узла (0.1ms tb)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Информация о узле (передаётся по BGP)
|
||||
*/
|
||||
struct NODEINFO {
|
||||
uint64_t node_id; // (big-endian)
|
||||
uint8_t ver; // версия пакета (циклический счетчик чтобы быстро сравнивать с локальной копией - были ли обновления)
|
||||
uint8_t public_key[SC_PUBKEY_SIZE]; // X25519 pubkey
|
||||
uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; // Ed25519 pubkey for signature verification
|
||||
uint8_t node_name_len; // размер в байтах (без null терминации)
|
||||
uint8_t local_v4_sockets; // NODEINFO_IPV4_SOCKET_META число метаданных ipv4 сокетов
|
||||
uint8_t local_v4_addrs; // NODEINFO_IPV4_ADDR число типизированных ipv4 адресов узла (плоский список)
|
||||
uint8_t local_v6_sockets; // NODEINFO_IPV6_SOCKET_META число метаданных ipv6 сокетов
|
||||
uint8_t local_v6_addrs; // NODEINFO_IPV6_ADDR число типизированных ipv6 адресов узла
|
||||
uint8_t local_v4_subnets; // NODEINFO_IPV4_SUBNET число локальных ipv4 подсетей узла
|
||||
uint8_t local_v6_subnets; // NODEINFO_IPV6_SUBNET число локальных ipv6 подсетей узла
|
||||
uint8_t tranzit_nodes; // NODEINFO_TRANZIT_NODE лучшие транзитные узлы для этой ноды
|
||||
uint8_t hop_count; // hop list: маршрут по которому распространялся этот NODEINFO_PACKET
|
||||
// далее идут динамические поля по порядку:
|
||||
// char node_name[node_name_len]
|
||||
// NODEINFO_IPV4_SOCKET_META[local_v4_sockets]
|
||||
// NODEINFO_IPV4_ADDR[local_v4_addrs]
|
||||
// NODEINFO_IPV6_SOCKET_META[local_v6_sockets]
|
||||
// NODEINFO_IPV6_ADDR[local_v6_addrs]
|
||||
// NODEINFO_IPV4_SUBNET[local_v4_subnets]
|
||||
// NODEINFO_IPV6_SUBNET[local_v6_subnets]
|
||||
// NODEINFO_TRANZIT_NODE[tranzit_nodes]
|
||||
// uint64_t hop_list[hop_count]
|
||||
} __attribute__((packed));
|
||||
|
||||
// метаданные одного сокета
|
||||
struct NODEINFO_IPV4_SOCKET_META {
|
||||
uint8_t id; // unique socket id (0-255)
|
||||
uint8_t type; // CFG_SERVER_TYPE_*/NAT_VERIFIED_*
|
||||
} __attribute__((packed));
|
||||
|
||||
// одна типизированная адресная запись (плоский список)
|
||||
struct NODEINFO_IPV4_ADDR {
|
||||
uint8_t addr[4]; // IP (network byte order)
|
||||
uint16_t port; // port (host byte order)
|
||||
uint8_t type; // ADDR_TYPE_INTERFACE/NAT/REAL
|
||||
uint8_t socket_id; // к какому сокету относится
|
||||
} __attribute__((packed));
|
||||
|
||||
struct NODEINFO_IPV6_SOCKET_META {
|
||||
uint8_t id; // unique socket id (0-255)
|
||||
uint8_t type; // CFG_SERVER_TYPE_*/NAT_VERIFIED_*
|
||||
} __attribute__((packed));
|
||||
|
||||
struct NODEINFO_IPV6_ADDR {
|
||||
uint8_t addr[16];
|
||||
uint16_t port;
|
||||
uint8_t type;
|
||||
uint8_t socket_id;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct NODEINFO_IPV4_SUBNET {
|
||||
uint8_t addr[4];// network byte order
|
||||
uint8_t prefix_length;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct NODEINFO_IPV6_SUBNET {
|
||||
uint8_t addr[16];
|
||||
uint8_t prefix_length;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct NODEINFO_TRANZIT_NODE {
|
||||
uint64_t node_id; // (big-endian)
|
||||
uint16_t rtt; // x0.1 ms (измеренный удаленным узлом RTT до транзитного узла)
|
||||
uint16_t link_q; // меньше - лучше (потери + 1/BW)
|
||||
} __attribute__((packed));
|
||||
|
||||
struct NODEINFO_PATH {
|
||||
struct ll_entry ll;
|
||||
struct ETCP_CONN* conn;
|
||||
uint8_t hop_count; // hop list: маршрут этого path
|
||||
};
|
||||
|
||||
struct NODEINFO_Q {
|
||||
struct ll_entry ll;
|
||||
struct ll_queue* paths; // сюда помещаем struct NODEINFO_PATH
|
||||
uint8_t dirty;
|
||||
uint8_t last_ver;
|
||||
uint8_t alien; // 1 = чужой узел (не запрашиваем роутинг при connect)
|
||||
uint8_t conn_mgr_type; // CONN_TYPE_* — тип managed-подключения
|
||||
uint64_t conn_mgr_intermediaries[CONN_MGR_MAX_INTERMEDIARIES]; // посредники для INDIRECT
|
||||
uint8_t conn_mgr_intermediariy_count;
|
||||
struct NODE_CONNECTIVITY connectivity; // состояние связности (локальное)
|
||||
struct ETCP_SOCKET* best_socket;
|
||||
struct NODEINFO node; // Всегда в конце структуры - динамически расширяемый блок
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Создаёт/обновляет nodeinfo для собственного узла
|
||||
*/
|
||||
int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BGP* bgp);
|
||||
|
||||
/**
|
||||
* @brief Получает указатель на массив IPv4-подсетей узла (без malloc/копирования).
|
||||
*/
|
||||
int get_node_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SUBNET **out_subnets);
|
||||
|
||||
/**
|
||||
* @brief Получает указатель на массив IPv6-подсетей узла (без malloc/копирования).
|
||||
*/
|
||||
int get_node_v6_routes(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_SUBNET **out_subnets);
|
||||
|
||||
/**
|
||||
* @brief Получает указатель на массив метаданных IPv4-сокетов узла.
|
||||
*/
|
||||
int get_node_v4_sockets_meta(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_SOCKET_META **out_meta);
|
||||
|
||||
/**
|
||||
* @brief Получает указатель на плоский список IPv4-адресов узла.
|
||||
*/
|
||||
int get_node_v4_addrs(struct NODEINFO_Q *node, const struct NODEINFO_IPV4_ADDR **out_addrs);
|
||||
|
||||
/**
|
||||
* @brief Получает указатель на массив метаданных IPv6-сокетов узла.
|
||||
*/
|
||||
int get_node_v6_sockets_meta(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_SOCKET_META **out_meta);
|
||||
|
||||
/**
|
||||
* @brief Получает указатель на плоский список IPv6-адресов узла.
|
||||
*/
|
||||
int get_node_v6_addrs(struct NODEINFO_Q *node, const struct NODEINFO_IPV6_ADDR **out_addrs);
|
||||
|
||||
struct NODEINFO_Q* nodeinfo_find_by_id(struct ROUTE_BGP* bgp, uint64_t node_id);
|
||||
|
||||
#endif // ROUTE_NODE_H
|
||||
@@ -1,150 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include "route_node_lmdb.h"
|
||||
#include "route_bgp.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
|
||||
#define NODEINFO_DB_MAPSIZE (10UL * 1024 * 1024)
|
||||
|
||||
int route_node_lmdb_init(struct ROUTE_BGP* bgp, const char* db_path) {
|
||||
if (!bgp || !db_path || !db_path[0]) return -1;
|
||||
|
||||
int rc = mdb_env_create(&bgp->nodeinfo_env);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_create failed: %s", mdb_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_env_set_mapsize(bgp->nodeinfo_env, NODEINFO_DB_MAPSIZE);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_set_mapsize failed: %s", mdb_strerror(rc));
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_env_set_maxdbs(bgp->nodeinfo_env, 2);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_set_maxdbs failed: %s", mdb_strerror(rc));
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_env_open(bgp->nodeinfo_env, db_path, 0, 0644);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_env_open(%s) failed: %s", db_path, mdb_strerror(rc));
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
MDB_txn* txn = NULL;
|
||||
rc = mdb_txn_begin(bgp->nodeinfo_env, NULL, 0, &txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_txn_begin failed: %s", mdb_strerror(rc));
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_dbi_open(txn, "nodeinfo", MDB_CREATE, &bgp->nodeinfo_dbi);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_dbi_open(nodeinfo) failed: %s", mdb_strerror(rc));
|
||||
mdb_txn_abort(txn);
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_txn_commit failed: %s", mdb_strerror(rc));
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "LMDB nodeinfo database opened: %s", db_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void route_node_lmdb_destroy(struct ROUTE_BGP* bgp) {
|
||||
if (!bgp) return;
|
||||
if (bgp->nodeinfo_env) {
|
||||
mdb_env_close(bgp->nodeinfo_env);
|
||||
bgp->nodeinfo_env = NULL;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "LMDB nodeinfo database closed");
|
||||
}
|
||||
}
|
||||
|
||||
int route_node_lmdb_put(struct ROUTE_BGP* bgp, struct NODEINFO_Q* nq) {
|
||||
if (!bgp || !bgp->nodeinfo_env || !nq) return -1;
|
||||
|
||||
int dyn_size = nodeinfo_dyn_size(&nq->node);
|
||||
size_t val_size = sizeof(struct NODEINFO) + dyn_size;
|
||||
|
||||
MDB_val key, data;
|
||||
key.mv_size = 8;
|
||||
key.mv_data = &nq->node.node_id;
|
||||
|
||||
data.mv_size = val_size;
|
||||
data.mv_data = (void*)&nq->node;
|
||||
|
||||
MDB_txn* txn = NULL;
|
||||
int rc = mdb_txn_begin(bgp->nodeinfo_env, NULL, 0, &txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB put mdb_txn_begin failed: %s", mdb_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_put(txn, bgp->nodeinfo_dbi, &key, &data, 0);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_put node %016llx failed: %s",
|
||||
(unsigned long long)nq->node.node_id, mdb_strerror(rc));
|
||||
mdb_txn_abort(txn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB put mdb_txn_commit failed: %s", mdb_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int route_node_lmdb_del(struct ROUTE_BGP* bgp, uint64_t node_id) {
|
||||
if (!bgp || !bgp->nodeinfo_env) return -1;
|
||||
|
||||
MDB_val key;
|
||||
key.mv_size = 8;
|
||||
key.mv_data = &node_id;
|
||||
|
||||
MDB_txn* txn = NULL;
|
||||
int rc = mdb_txn_begin(bgp->nodeinfo_env, NULL, 0, &txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB del mdb_txn_begin failed: %s", mdb_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_del(txn, bgp->nodeinfo_dbi, &key, NULL);
|
||||
if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB mdb_del node %016llx failed: %s",
|
||||
(unsigned long long)node_id, mdb_strerror(rc));
|
||||
mdb_txn_abort(txn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = mdb_txn_commit(txn);
|
||||
if (rc != MDB_SUCCESS) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "LMDB del mdb_txn_commit failed: %s", mdb_strerror(rc));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef ROUTE_NODE_LMDB_H
|
||||
#define ROUTE_NODE_LMDB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "route_node.h"
|
||||
|
||||
struct ROUTE_BGP;
|
||||
|
||||
int route_node_lmdb_init(struct ROUTE_BGP* bgp, const char* db_path);
|
||||
void route_node_lmdb_destroy(struct ROUTE_BGP* bgp);
|
||||
int route_node_lmdb_put(struct ROUTE_BGP* bgp, struct NODEINFO_Q* nq);
|
||||
int route_node_lmdb_del(struct ROUTE_BGP* bgp, uint64_t node_id);
|
||||
|
||||
#endif
|
||||
+39
-39
@@ -14,8 +14,8 @@
|
||||
#include "etcp_api.h"
|
||||
#include "etcp.h"
|
||||
#include "etcp_connections.h"
|
||||
#include "route_node.h"
|
||||
#include "route_bgp.h"
|
||||
#include "topo_node.h"
|
||||
#include "topo_group.h"
|
||||
#include "route_ping.h"
|
||||
#include "../lib/u_async.h"
|
||||
|
||||
@@ -36,7 +36,7 @@ struct route_ping_series_ctx {
|
||||
|
||||
struct route_ping_pending {
|
||||
struct route_ping_pending* next;
|
||||
struct ROUTE_BGP* bgp;
|
||||
struct TOPO_GROUP* group;
|
||||
uint32_t request_id;
|
||||
route_ping_callback_t callback;
|
||||
void* arg;
|
||||
@@ -54,8 +54,8 @@ static void route_ping_pending_timeout(void* arg) {
|
||||
u_free(p);
|
||||
return;
|
||||
}
|
||||
struct ROUTE_BGP* bgp = p->bgp;
|
||||
struct route_ping_pending** cur = &bgp->ping_pending;
|
||||
struct TOPO_GROUP* group = p->group;
|
||||
struct route_ping_pending** cur = &group->ping_pending;
|
||||
while (*cur) {
|
||||
if (*cur == p) {
|
||||
*cur = p->next;
|
||||
@@ -70,23 +70,23 @@ static void route_ping_pending_timeout(void* arg) {
|
||||
}
|
||||
|
||||
// прошел ответ "серия пигнов на удаленном узле завершена"
|
||||
void route_ping_handle_resp(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) {
|
||||
if (!bgp || !from_conn || !data || len < sizeof(struct BGP_PING_RESPONSE)) {
|
||||
void route_ping_handle_resp(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) {
|
||||
if (!group || !from_conn || !data || len < sizeof(struct TOPO_PING_RESP)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args");
|
||||
return;
|
||||
}
|
||||
const struct BGP_PING_RESPONSE* resp = (const struct BGP_PING_RESPONSE*)data;
|
||||
const struct TOPO_PING_RESP* resp = (const struct TOPO_PING_RESP*)data;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "from=%s request_id=%08x sent=%u ok=%u avg_rtt=%u",
|
||||
from_conn->log_name, (unsigned)resp->request_id,
|
||||
(unsigned)resp->count_sent, (unsigned)resp->count_ok, (unsigned)resp->avg_rtt);
|
||||
|
||||
struct route_ping_pending** cur = &bgp->ping_pending;
|
||||
struct route_ping_pending** cur = &group->ping_pending;
|
||||
while (*cur) {
|
||||
struct route_ping_pending* p = *cur;
|
||||
if (p->request_id == resp->request_id) {
|
||||
*cur = p->next;
|
||||
if (p->timeout_timer) {
|
||||
uasync_cancel_timeout(bgp->instance->ua, p->timeout_timer);
|
||||
uasync_cancel_timeout(group->instance->ua, p->timeout_timer);
|
||||
p->timeout_timer = NULL;
|
||||
}
|
||||
if (p->callback) {
|
||||
@@ -102,10 +102,10 @@ void route_ping_handle_resp(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn,
|
||||
(unsigned)resp->request_id);
|
||||
}
|
||||
|
||||
void route_ping_cancel_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
|
||||
if (!bgp || !conn) return;
|
||||
void route_ping_cancel_for_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
|
||||
if (!group || !conn) return;
|
||||
|
||||
if (!bgp->ping_pending) {
|
||||
if (!group->ping_pending) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "cancel_for_conn: ping_pending is NULL for %s", conn->log_name);
|
||||
return;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ void route_ping_cancel_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "cancel_for_conn: checking pending for %s", conn->log_name);
|
||||
|
||||
int cancelled = 0;
|
||||
struct route_ping_pending** cur = &bgp->ping_pending;
|
||||
struct route_ping_pending** cur = &group->ping_pending;
|
||||
while (*cur) {
|
||||
struct route_ping_pending* p = *cur;
|
||||
struct nat_check_arg* na = (struct nat_check_arg*)p->arg;
|
||||
@@ -126,7 +126,7 @@ void route_ping_cancel_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
|
||||
if ((na && na->link && na->link->etcp == conn) || p->via_conn == conn) {
|
||||
*cur = p->next;
|
||||
if (p->timeout_timer) {
|
||||
uasync_cancel_timeout(bgp->instance->ua, p->timeout_timer);
|
||||
uasync_cancel_timeout(group->instance->ua, p->timeout_timer);
|
||||
p->timeout_timer = NULL;
|
||||
}
|
||||
p->cancelled = 1;
|
||||
@@ -148,24 +148,24 @@ void route_ping_cancel_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) {
|
||||
|
||||
// ========================================================================
|
||||
|
||||
int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn,
|
||||
int route_ping_send_req_addr(struct TOPO_GROUP* group, struct ETCP_CONN* to_conn,
|
||||
uint32_t target_ip, uint16_t target_port,
|
||||
uint8_t count, uint16_t interval_ms, uint16_t timeout_ms,
|
||||
uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg,
|
||||
const uint8_t* pubkey) {
|
||||
if (!bgp || !to_conn || count == 0 || timeout_ms == 0) {
|
||||
if (!group || !to_conn || count == 0 || timeout_ms == 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args");
|
||||
return -1;
|
||||
}
|
||||
size_t pkt_size = pubkey ? sizeof(struct BGP_PING_REQUEST) : offsetof(struct BGP_PING_REQUEST, pubkey);
|
||||
struct BGP_PING_REQUEST* req_pkt = u_calloc(1, pkt_size);
|
||||
size_t pkt_size = pubkey ? sizeof(struct TOPO_PING_REQ) : offsetof(struct TOPO_PING_REQ, pubkey);
|
||||
struct TOPO_PING_REQ* req_pkt = u_calloc(1, pkt_size);
|
||||
if (!req_pkt) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc failed");
|
||||
return -2;
|
||||
}
|
||||
req_pkt->cmd = ETCP_ID_ROUTE_ENTRY;
|
||||
req_pkt->subcmd = ROUTE_SUBCMD_PING_REQ;
|
||||
uint32_t new_req_id = bgp->next_ping_req_id++;
|
||||
req_pkt->cmd = ETCP_ID_TOPO_ENTRY;
|
||||
req_pkt->subcmd = TOPO_SUBCMD_PING_REQ;
|
||||
uint32_t new_req_id = group->next_ping_req_id++;
|
||||
req_pkt->request_id = new_req_id;
|
||||
req_pkt->count = count;
|
||||
req_pkt->interval_ms = interval_ms;
|
||||
@@ -203,15 +203,15 @@ int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn,
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "pending alloc failed");
|
||||
return -5;
|
||||
}
|
||||
pending->bgp = bgp;
|
||||
pending->group = group;
|
||||
pending->request_id = new_req_id; // req_pkt may be freed by etcp_send callback
|
||||
pending->callback = cb;
|
||||
pending->arg = arg;
|
||||
pending->via_conn = to_conn;
|
||||
pending->next = bgp->ping_pending;
|
||||
bgp->ping_pending = pending;
|
||||
pending->next = group->ping_pending;
|
||||
group->ping_pending = pending;
|
||||
|
||||
pending->timeout_timer = uasync_set_timeout(bgp->instance->ua, wait_timeout_ms * 10, pending, route_ping_pending_timeout, "route_ping");
|
||||
pending->timeout_timer = uasync_set_timeout(group->instance->ua, wait_timeout_ms * 10, pending, route_ping_pending_timeout, "route_ping");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -228,10 +228,10 @@ static void route_ping_series_finish(struct route_ping_series_ctx* ctx) {
|
||||
: (uint16_t)(ctx->timeout_ms * 10); /* таймаут как "плохо" */
|
||||
|
||||
/* Отправляем ответ по BGP */
|
||||
struct BGP_PING_RESPONSE* resp = u_calloc(1, sizeof(struct BGP_PING_RESPONSE));
|
||||
struct TOPO_PING_RESP* resp = u_calloc(1, sizeof(struct TOPO_PING_RESP));
|
||||
if (resp) {
|
||||
resp->cmd = ETCP_ID_ROUTE_ENTRY;
|
||||
resp->subcmd = ROUTE_SUBCMD_PING_RESP;
|
||||
resp->cmd = ETCP_ID_TOPO_ENTRY;
|
||||
resp->subcmd = TOPO_SUBCMD_PING_RESP;
|
||||
resp->request_id = ctx->request_id;
|
||||
resp->count_sent = ctx->count_sent;
|
||||
resp->count_ok = ctx->count_ok;
|
||||
@@ -240,7 +240,7 @@ static void route_ping_series_finish(struct route_ping_series_ctx* ctx) {
|
||||
struct ll_entry* e = queue_entry_new(0);
|
||||
if (e) {
|
||||
e->dgram = (uint8_t*)resp;
|
||||
e->len = sizeof(struct BGP_PING_RESPONSE);
|
||||
e->len = sizeof(struct TOPO_PING_RESP);
|
||||
etcp_send(ctx->reply_conn, e);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP,
|
||||
"PING series done %s request_id=%08x sent=%u ok=%u avg_rtt=%u",
|
||||
@@ -303,16 +303,16 @@ static void route_ping_single_cb(int success,
|
||||
}
|
||||
|
||||
// Обработчик PING_REQ (нам запросили "пропингуй узел и верни результат")
|
||||
void route_ping_handle_req(struct ROUTE_BGP* bgp,
|
||||
void route_ping_handle_req(struct TOPO_GROUP* group,
|
||||
struct ETCP_CONN* from_conn,
|
||||
const uint8_t* data,
|
||||
size_t len) {
|
||||
if (!bgp || !from_conn || !data || len < offsetof(struct BGP_PING_REQUEST, pubkey)) {
|
||||
if (!group || !from_conn || !data || len < offsetof(struct TOPO_PING_REQ, pubkey)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "ping req: bad args len=%zu", len);
|
||||
return;
|
||||
}
|
||||
|
||||
const struct BGP_PING_REQUEST* req_pkt = (const struct BGP_PING_REQUEST*)data;
|
||||
const struct TOPO_PING_REQ* req_pkt = (const struct TOPO_PING_REQ*)data;
|
||||
|
||||
/* Создаём контекст серии */
|
||||
struct route_ping_series_ctx* ctx = u_calloc(1, sizeof(struct route_ping_series_ctx));
|
||||
@@ -327,10 +327,10 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp,
|
||||
ctx->timeout_ms = req_pkt->timeout_ms;
|
||||
|
||||
{
|
||||
struct NODEINFO_Q* req_nq = nodeinfo_find_by_id(bgp, from_conn->peer_node_id);
|
||||
struct TOPO_NODEQ* req_nq = topo_node_find_by_id(group, from_conn->peer_node_id);
|
||||
if (req_nq) req_nq->connectivity.ping_req_time = get_time_tb();
|
||||
}
|
||||
if (len >= sizeof(struct BGP_PING_REQUEST)) {
|
||||
if (len >= sizeof(struct TOPO_PING_REQ)) {
|
||||
memcpy(ctx->pubkey, req_pkt->pubkey, SC_PUBKEY_SIZE);
|
||||
}
|
||||
|
||||
@@ -343,9 +343,9 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp,
|
||||
/* Если target не указан — можно разрешить из nodeinfo */
|
||||
/*
|
||||
if (sin->sin_addr.s_addr == 0 && sin->sin_port == 0) {
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(bgp, req_pkt->node_id);
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(bgp, req_pkt->node_id);
|
||||
if (nq) {
|
||||
const struct NODEINFO_IPV4_ADDR* addrs;
|
||||
const struct TOPOMSG_ADDR4* addrs;
|
||||
int count = get_node_v4_addrs(nq, &addrs);
|
||||
if (count > 0) {
|
||||
memcpy(&sin->sin_addr.s_addr, addrs[0].addr, 4);
|
||||
@@ -356,7 +356,7 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp,
|
||||
*/
|
||||
|
||||
/* Ищем первый IPv4-сокет (как в старом коде) */
|
||||
struct ETCP_SOCKET* ls = bgp->instance->etcp_sockets;
|
||||
struct ETCP_SOCKET* ls = group->instance->etcp_sockets;
|
||||
while (ls) {
|
||||
if (ls->local_addr.ss_family == AF_INET) {
|
||||
ctx->local_sock = ls;
|
||||
@@ -377,7 +377,7 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp,
|
||||
|
||||
/* Запускаем первый пинг (дальше цепочка через callback) */
|
||||
int ret = etcp_send_ping_to_socket(
|
||||
bgp->instance,
|
||||
group->instance,
|
||||
ctx->local_sock,
|
||||
ctx->pubkey,
|
||||
&ctx->target_addr,
|
||||
|
||||
+23
-14
@@ -1,16 +1,21 @@
|
||||
#ifndef ROUTE_PING_H
|
||||
#define ROUTE_PING_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "route_bgp.h"
|
||||
#include "topo_group.h"
|
||||
|
||||
#define ROUTE_SUBCMD_PING_REQ 0x07
|
||||
#define ROUTE_SUBCMD_PING_RESP 0x08
|
||||
#define TOPO_SUBCMD_PING_REQ 0x07
|
||||
#define TOPO_SUBCMD_PING_RESP 0x08
|
||||
|
||||
struct BGP_PING_REQUEST {
|
||||
uint8_t cmd; // ETCP_ID_ROUTE_ENTRY
|
||||
uint8_t subcmd; // ROUTE_SUBCMD_PING_REQ
|
||||
struct TOPO_PING_REQ {
|
||||
uint8_t cmd; // ETCP_ID_TOPO_ENTRY
|
||||
uint8_t subcmd; // TOPO_SUBCMD_PING_REQ
|
||||
uint16_t reserved; // padding для выравнивания request_id
|
||||
uint32_t request_id; // для корреляции
|
||||
uint8_t count; // число пингов
|
||||
@@ -22,9 +27,9 @@ struct BGP_PING_REQUEST {
|
||||
uint8_t pubkey[SC_PUBKEY_SIZE]; // pubkey for encryption if target not in nodes
|
||||
} __attribute__((packed));
|
||||
|
||||
struct BGP_PING_RESPONSE {
|
||||
struct TOPO_PING_RESP {
|
||||
uint8_t cmd;
|
||||
uint8_t subcmd; // ROUTE_SUBCMD_PING_RESP
|
||||
uint8_t subcmd; // TOPO_SUBCMD_PING_RESP
|
||||
uint16_t reserved; // padding для выравнивания
|
||||
uint32_t request_id;
|
||||
uint8_t count_sent;
|
||||
@@ -37,20 +42,24 @@ typedef void (*route_ping_callback_t)(int success, uint16_t avg_rtt, uint8_t cou
|
||||
// Отправить запрос пинга по произвольному IP:port (используется для NAT-детекции)
|
||||
// Если target_ip == 0, используется node_id из списка известных узлов
|
||||
// Если pubkey != NULL, он передается в пакете (для пинга без локального nodeinfo)
|
||||
int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn,
|
||||
int route_ping_send_req_addr(struct TOPO_GROUP* group, struct ETCP_CONN* to_conn,
|
||||
uint32_t target_ip, uint16_t target_port,
|
||||
uint8_t count, uint16_t interval_ms, uint16_t timeout_ms,
|
||||
uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg,
|
||||
const uint8_t* pubkey);
|
||||
|
||||
// Очистить список ожидающих запросов (при уничтожении BGP)
|
||||
void route_ping_destroy_pending(struct ROUTE_BGP* bgp);
|
||||
void route_ping_destroy_pending(struct TOPO_GROUP* group);
|
||||
|
||||
// Отменить pending запросы для указанного соединения (при удалении conn)
|
||||
void route_ping_cancel_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn);
|
||||
void route_ping_cancel_for_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn);
|
||||
|
||||
// Обработчики, вызываемые из route_bgp_receive_cbk
|
||||
void route_ping_handle_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len);
|
||||
void route_ping_handle_resp(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len);
|
||||
// Обработчики, вызываемые из topo_group_receive_cbk
|
||||
void route_ping_handle_req(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len);
|
||||
void route_ping_handle_resp(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // ROUTE_PING_H
|
||||
Loaded 100 of 221 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user