Compare commits
54
Commits
v0.1.0-rc2
...
chatgui
| 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 |
No files matched your search
@@ -194,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
|
||||
```
|
||||
|
||||
### Настройка отладки
|
||||
@@ -215,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
|
||||
@@ -245,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
|
||||
@@ -272,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
|
||||
@@ -286,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 фрагментами
|
||||
@@ -308,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`.
|
||||
@@ -360,6 +399,12 @@ SOCKET=14, CONTROL=15, DUMP=16, TRAFFIC=17, DEBUG=18, GENERAL=19, NAT=20
|
||||
- Давай заодно возьму произвольный фрагмент лога и изучу еа предмет явных проблем. Особенно инициализацию и освобождение ресурсов.
|
||||
Сколько времени занимает, нет ли заклиниваний, нет ли ошибок или странностей
|
||||
|
||||
### Диагностические сообщения (DEBUG_WARN DEBUG_ERROR)
|
||||
Сообщения должны говорить что случилось и с какими деталями.
|
||||
Обязательно выводить подробные собощения об ошибках (даже если нужен небольшой дополнительный код чтобы эти детали сформировать и привести в читаемый вид).
|
||||
Полезны подробности об инициализации-завершении которые помогают понять внутренние состояния
|
||||
Диагностические сообщения - важный момент. надо про диагностику помнить.
|
||||
Для хорошей диагностики надо проанализировать как архитектор - какие есть нюансы в архитектуре и что полезно будет видеть в дебаг выводе чтобы понять полную картину происходящего.
|
||||
|
||||
### Прочие правила:
|
||||
- sed для редактирования исходников - запрещено
|
||||
@@ -379,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)
|
||||
@@ -391,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
|
||||
@@ -403,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 |
|
||||
@@ -415,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`
|
||||
|
||||
### Система анимированных эмодзи
|
||||
@@ -516,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`
|
||||
@@ -537,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"
|
||||
@@ -6,6 +6,7 @@
|
||||
# --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
|
||||
@@ -23,8 +24,10 @@ fi
|
||||
CLEAN=0
|
||||
FULL_REBUILD=0
|
||||
USE_ASAN=0
|
||||
USE_HARDENED=0
|
||||
JOBS="-j4"
|
||||
MAKE_ARGS=""
|
||||
HARDEN_FLAGS=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
@@ -35,6 +38,7 @@ while [[ $# -gt 0 ]]; do
|
||||
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
|
||||
;;
|
||||
@@ -50,6 +54,10 @@ while [[ $# -gt 0 ]]; do
|
||||
USE_ASAN=1
|
||||
shift
|
||||
;;
|
||||
--hardened)
|
||||
USE_HARDENED=1
|
||||
shift
|
||||
;;
|
||||
-j*)
|
||||
JOBS="$1"
|
||||
shift
|
||||
@@ -135,6 +143,8 @@ if [ ! -f Makefile ]; then
|
||||
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
|
||||
@@ -151,6 +161,14 @@ if [ "$USE_ASAN" -eq 1 ]; then
|
||||
(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
|
||||
|
||||
+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([[
|
||||
|
||||
+2
-2
@@ -194,8 +194,8 @@ 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 route_bgp.c route_ping.c
|
||||
route_node.c route_node_lmdb.c route_connectivity.c conn_mgr.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
|
||||
|
||||
@@ -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% (обновляется каждый час)
|
||||
|
||||
Сделать подключение к группе
|
||||
+1
-1
@@ -80,6 +80,6 @@ utun поддерживает встроенный ipv4 роутинг, може
|
||||
|
||||
Тесты:
|
||||
# Правила работы с очередями в тестах:
|
||||
- для добавления в очередь надо использовать queue_wait_threshold(q,0,0,arg) и добавлять по одному пакету, каждый раз дожидаясь когда очередь станет пустой.
|
||||
- для добавления в очередь надо использовать queue_waiter_wait и queue_set_threshold, добавлять по одному пакету, каждый раз дожидаясь когда очередь станет пустой.
|
||||
- для получения надо использовать queue_set_callback + queue_resume_callback
|
||||
|
||||
-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
|
||||
+24
-28
@@ -129,6 +129,7 @@ static const struct {
|
||||
{"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}
|
||||
};
|
||||
@@ -137,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) {
|
||||
@@ -163,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 */
|
||||
}
|
||||
|
||||
@@ -221,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) */
|
||||
@@ -281,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;
|
||||
}
|
||||
@@ -344,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;
|
||||
}
|
||||
@@ -396,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);
|
||||
@@ -434,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
|
||||
}
|
||||
|
||||
+12
-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) */
|
||||
@@ -62,7 +63,8 @@ typedef int debug_category_t;
|
||||
#define DEBUG_CATEGORY_ETCPROUTE 22 // ETCP routing
|
||||
#define DEBUG_CATEGORY_BBR 23 // BBR congestion control
|
||||
#define DEBUG_CATEGORY_ETCP_DUMP 24 // ETCP packet dump
|
||||
#define DEBUG_CATEGORY_COUNT 25 // Total number of categories
|
||||
#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 */
|
||||
@@ -76,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 */
|
||||
@@ -108,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);
|
||||
|
||||
@@ -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
|
||||
@@ -412,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>
|
||||
@@ -186,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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -108,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
|
||||
@@ -924,6 +924,7 @@ static void process_epoll_events(struct UASYNC* ua, struct epoll_event* events,
|
||||
// Wakeup fd detected by data.fd < 0 (lower 32 bits of data.u64 = -1)
|
||||
if (events[i].data.fd < 0) {
|
||||
if (events[i].events & EPOLLIN) { drain_wakeup_pipe(ua); }
|
||||
process_posted_tasks(ua);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+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,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` |
|
||||
+61
-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 \
|
||||
@@ -53,10 +55,64 @@ utun_CORE_SOURCES = \
|
||||
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 = \
|
||||
@@ -67,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).
|
||||
если чатовая - то только связность между узлами.
|
||||
|
||||
+73
-7
@@ -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) {
|
||||
@@ -398,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);
|
||||
@@ -716,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;
|
||||
|
||||
@@ -752,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;
|
||||
}
|
||||
|
||||
@@ -775,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;
|
||||
|
||||
@@ -802,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));
|
||||
@@ -809,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);
|
||||
@@ -817,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;
|
||||
}
|
||||
@@ -927,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;
|
||||
@@ -942,6 +987,10 @@ 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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@@ -965,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;
|
||||
}
|
||||
@@ -1003,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);
|
||||
@@ -1097,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");
|
||||
}
|
||||
|
||||
@@ -62,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;
|
||||
@@ -107,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
|
||||
@@ -180,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);
|
||||
|
||||
+151
-182
@@ -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"
|
||||
@@ -32,13 +32,13 @@ 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_config_type, uint8_t target_nat_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 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 void cm_exchange_probe_retry_cb(void* arg);
|
||||
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 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);
|
||||
@@ -71,13 +71,13 @@ 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; }
|
||||
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");
|
||||
@@ -157,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));
|
||||
@@ -167,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;
|
||||
@@ -176,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; }
|
||||
@@ -223,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;
|
||||
@@ -239,8 +239,8 @@ int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id) {
|
||||
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;
|
||||
@@ -270,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;
|
||||
}
|
||||
@@ -362,7 +363,7 @@ static int cm_nat_compatible(struct ETCP_SOCKET* our, uint8_t target_config_type
|
||||
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;
|
||||
@@ -373,30 +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].config_type == CFG_SERVER_TYPE_PUBLIC || metas[j].config_type == CFG_SERVER_TYPE_UNKNOWN
|
||||
|| metas[j].nat_type == NAT_VERIFIED_EIM || metas[j].nat_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;
|
||||
}
|
||||
}
|
||||
@@ -404,17 +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].config_type == CFG_SERVER_TYPE_PRIVATE || metas[j].config_type == CFG_SERVER_TYPE_LOCAL))
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -435,38 +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].config_type != CFG_SERVER_TYPE_PRIVATE && metas[j].config_type != CFG_SERVER_TYPE_LOCAL)) 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->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, 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);
|
||||
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;
|
||||
@@ -474,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].config_type, metas[j].nat_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;
|
||||
}
|
||||
}
|
||||
@@ -524,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);
|
||||
@@ -535,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; }
|
||||
@@ -584,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;
|
||||
}
|
||||
@@ -595,30 +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].config_type == CFG_SERVER_TYPE_PUBLIC || metas[j].config_type == CFG_SERVER_TYPE_UNKNOWN
|
||||
|| metas[j].nat_type == NAT_VERIFIED_EIM || metas[j].nat_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -638,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));
|
||||
@@ -681,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;
|
||||
}
|
||||
@@ -696,9 +645,11 @@ static int cm_start_phase_indirect(struct CONN_MGR_ENTRY* entry) {
|
||||
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");
|
||||
@@ -737,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;
|
||||
@@ -763,11 +714,11 @@ static void cm_compute_intermediaries(struct CONN_MGR_ENTRY* entry, struct CONN_
|
||||
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;
|
||||
@@ -784,7 +735,7 @@ static void cm_exchange_probe_retry_cb(void* arg) {
|
||||
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++; break; }
|
||||
}
|
||||
if (need_probe == 0) {
|
||||
@@ -814,7 +765,7 @@ 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);
|
||||
@@ -903,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; }
|
||||
@@ -924,7 +876,7 @@ 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);
|
||||
}
|
||||
@@ -939,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",
|
||||
@@ -962,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) {
|
||||
@@ -987,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++;
|
||||
@@ -1014,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;
|
||||
@@ -1025,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
|
||||
+48
-25
@@ -10,9 +10,9 @@
|
||||
#include "etcp_connections.h"
|
||||
#include "tun_if.h"
|
||||
#include "route_lib.h"
|
||||
#include "route_bgp.h"
|
||||
#include "topo_group.h"
|
||||
#include "etcp_router.h"
|
||||
#include "route_node.h"
|
||||
#include "topo_node.h"
|
||||
#include "pkt_normalizer.h"
|
||||
#include "../tools/etcpmon/etcpmon_protocol.h"
|
||||
#include "../lib/u_async.h"
|
||||
@@ -100,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);
|
||||
@@ -479,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;
|
||||
@@ -778,13 +784,13 @@ 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 = route_node_format_all(server->instance->bgp, text, text_max);
|
||||
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);
|
||||
@@ -801,7 +807,7 @@ static void handle_client_data(struct control_server* server, struct control_cli
|
||||
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;
|
||||
@@ -1182,13 +1188,14 @@ 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));
|
||||
}
|
||||
@@ -1196,6 +1203,7 @@ static void send_metrics(struct control_server* server, struct control_client* c
|
||||
/* 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;
|
||||
@@ -1211,8 +1219,22 @@ static void send_metrics(struct control_server* server, struct control_client* c
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1364,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;
|
||||
}
|
||||
@@ -1407,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 */
|
||||
@@ -117,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
|
||||
+260
-22
@@ -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"
|
||||
|
||||
@@ -94,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);
|
||||
@@ -194,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;
|
||||
}
|
||||
|
||||
@@ -202,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
|
||||
@@ -217,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;
|
||||
@@ -240,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);
|
||||
@@ -253,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;
|
||||
@@ -262,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] UP links_up=%d initialized=%d", etcp->log_name, etcp->links_up, etcp->initialized);
|
||||
if (etcp->up_cbk) etcp->up_cbk(etcp, etcp->up_arg);
|
||||
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] DOWN links_up=%d", etcp->log_name, etcp->links_up);
|
||||
if (etcp->down_cbk) etcp->down_cbk(etcp, etcp->down_arg);
|
||||
struct etcp_cbk_entry* cbe = etcp->down_cbks;
|
||||
while (cbe) { cbe->fn(etcp, cbe->arg); cbe = cbe->next; }
|
||||
}
|
||||
|
||||
|
||||
@@ -292,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);
|
||||
@@ -325,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);
|
||||
@@ -490,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-статус у всех линков этого соединения
|
||||
@@ -519,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);
|
||||
}
|
||||
|
||||
@@ -734,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);
|
||||
@@ -774,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
|
||||
@@ -970,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);
|
||||
|
||||
@@ -1110,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;
|
||||
@@ -1236,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;
|
||||
}
|
||||
|
||||
@@ -1315,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) {
|
||||
@@ -1368,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 этого пакета (на стороне отправителя, т.е. у нас)
|
||||
|
||||
@@ -1413,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;
|
||||
@@ -1487,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++;
|
||||
@@ -1512,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;
|
||||
@@ -1579,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;
|
||||
}
|
||||
@@ -1588,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) {}
|
||||
}
|
||||
|
||||
@@ -1613,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);
|
||||
}
|
||||
}
|
||||
|
||||
+58
-7
@@ -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
|
||||
@@ -182,13 +226,10 @@ struct ETCP_CONN {
|
||||
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
|
||||
@@ -199,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
|
||||
@@ -246,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
|
||||
|
||||
+51
-12
@@ -6,25 +6,69 @@
|
||||
#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;
|
||||
@@ -45,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) {
|
||||
|
||||
+34
-2
@@ -17,6 +17,11 @@
|
||||
#ifndef ETCP_API_H
|
||||
#define ETCP_API_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../lib/ll_queue.h"
|
||||
|
||||
@@ -41,6 +46,18 @@
|
||||
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 соединение от которого получен пакет
|
||||
@@ -54,7 +71,7 @@ typedef void (*etcp_recv_fn)(struct ETCP_CONN* conn, struct ll_entry* entry);
|
||||
// Forward declaration
|
||||
struct ETCP_CONN;
|
||||
struct UTUN_INSTANCE;
|
||||
struct NODEINFO_Q;
|
||||
struct TOPO_NODEQ;
|
||||
typedef void (*etcp_cbk_fn)(struct ETCP_CONN* conn, void* arg);
|
||||
|
||||
// ---- Background connection initialization ----
|
||||
@@ -64,7 +81,7 @@ typedef void (*etcp_cbk_fn)(struct ETCP_CONN* conn, void* arg);
|
||||
|
||||
typedef void (*etcp_connect_callback_t)(void* arg, struct ETCP_CONN* conn, int type);
|
||||
|
||||
int etcp_connect(struct UTUN_INSTANCE* instance, struct NODEINFO_Q* node,
|
||||
int etcp_connect(struct UTUN_INSTANCE* instance, struct TOPO_NODEQ* node,
|
||||
etcp_connect_callback_t cb, void* arg, uint8_t flags);
|
||||
|
||||
/**
|
||||
@@ -81,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 */
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -132,6 +151,15 @@ 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);
|
||||
|
||||
/**
|
||||
@@ -148,4 +176,8 @@ void etcp_set_routing_exchange_state(struct ETCP_CONN* conn, uint8_t new_state);
|
||||
*/
|
||||
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
|
||||
+74
-39
@@ -1,7 +1,8 @@
|
||||
#include "etcp_api.h"
|
||||
#include "etcp.h"
|
||||
#include "utun_instance.h"
|
||||
#include "route_node.h"
|
||||
#include "topo_node.h"
|
||||
#include "stcp_link.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/u_async.h"
|
||||
@@ -25,10 +26,15 @@ struct ETCP_CONNECT {
|
||||
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;
|
||||
@@ -63,46 +69,70 @@ static void connect_cancel(struct ETCP_CONNECT* ctx) {
|
||||
u_free(ctx);
|
||||
}
|
||||
|
||||
static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct NODEINFO_Q* node) {
|
||||
const struct NODEINFO_IPV4_ADDR* addrs; int ac = get_node_v4_addrs(node, &addrs);
|
||||
for (int i = 0; i < ac; i++) {
|
||||
if (addrs[i].port == 0) continue;
|
||||
{ int zero = 1; for (int j = 0; j < 4; j++) if (addrs[i].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, addrs[i].addr, 4);
|
||||
sin.sin_port = htons(addrs[i].port);
|
||||
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));
|
||||
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)) {
|
||||
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 NODEINFO_Q* node) {
|
||||
const struct NODEINFO_IPV6_ADDR* addrs; int ac = get_node_v6_addrs(node, &addrs);
|
||||
for (int i = 0; i < ac; i++) {
|
||||
if (addrs[i].port == 0) continue;
|
||||
int zero = 1;
|
||||
for (int j = 0; j < 16; j++) if (addrs[i].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, addrs[i].addr, 16);
|
||||
sin6.sin6_port = htons(addrs[i].port);
|
||||
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));
|
||||
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)) {
|
||||
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;
|
||||
@@ -116,6 +146,7 @@ static void connect_initial_timeout_cb(void* arg) {
|
||||
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);
|
||||
@@ -138,8 +169,12 @@ static void connect_settle_timeout_cb(void* arg) {
|
||||
}
|
||||
link = next;
|
||||
}
|
||||
ctx->conn->ready_cbk = NULL;
|
||||
ctx->conn->ready_arg = NULL;
|
||||
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);
|
||||
}
|
||||
@@ -170,10 +205,10 @@ static void connect_ready_cb(struct ETCP_CONN* conn, void* arg) {
|
||||
connect_settle_timeout_cb, "etcp_connect_settle");
|
||||
}
|
||||
|
||||
int etcp_connect(struct UTUN_INSTANCE* inst, struct NODEINFO_Q* node,
|
||||
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;
|
||||
uint64_t node_id = node->node->node_id;
|
||||
|
||||
struct ETCP_CONN* existing = inst->connections;
|
||||
while (existing) {
|
||||
@@ -211,7 +246,7 @@ int etcp_connect(struct UTUN_INSTANCE* inst, struct NODEINFO_Q* node,
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
@@ -227,16 +262,16 @@ int etcp_connect(struct UTUN_INSTANCE* inst, struct NODEINFO_Q* node,
|
||||
cn->cb = cb; cn->arg = arg; cn->flags = flags; ctx->cb_list = cn;
|
||||
}
|
||||
|
||||
conn->ready_cbk = connect_ready_cb;
|
||||
conn->ready_arg = ctx;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
+85
-60
@@ -17,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"
|
||||
@@ -40,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);
|
||||
}
|
||||
|
||||
@@ -133,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;
|
||||
@@ -238,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;
|
||||
}
|
||||
|
||||
@@ -377,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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
@@ -1308,25 +1312,27 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk
|
||||
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;
|
||||
if (link->etcp->instance->bgp) {
|
||||
route_bgp_update_my_nodeinfo(link->etcp->instance->bgp->instance, link->etcp->instance->bgp);
|
||||
struct ROUTE_BGP* bgp = link->etcp->instance->bgp;
|
||||
if (bgp->local_node && bgp->senders_list) {
|
||||
bgp->local_node->node.ver = (bgp->local_node->node.ver % 255) + 1;
|
||||
bgp->local_node->dirty = 1;
|
||||
struct ll_entry* se = bgp->senders_list->head;
|
||||
{ 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 ROUTE_BGP_CONN_ITEM* item = (struct ROUTE_BGP_CONN_ITEM*)se->data;
|
||||
if (item && item->conn) route_bgp_send_nodeinfo(bgp->local_node, item->conn);
|
||||
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;
|
||||
@@ -1365,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;
|
||||
}
|
||||
@@ -1398,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);
|
||||
@@ -1447,7 +1452,11 @@ static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_D
|
||||
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);
|
||||
|
||||
@@ -1517,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;
|
||||
}
|
||||
|
||||
@@ -1544,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)
|
||||
@@ -1569,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;
|
||||
@@ -1592,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;
|
||||
@@ -1611,7 +1625,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
|
||||
errorcode=8;
|
||||
goto ec_fr;
|
||||
}
|
||||
if (pkt_len < ETCP_INIT_REQ_V1_SIZE) {
|
||||
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;
|
||||
@@ -1643,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) {// ищем есть ли подключение к этому пиру
|
||||
@@ -1690,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;
|
||||
}
|
||||
@@ -1706,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
|
||||
@@ -1725,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);
|
||||
}
|
||||
@@ -1752,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;
|
||||
@@ -1814,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);
|
||||
@@ -1822,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);
|
||||
@@ -1830,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;
|
||||
}
|
||||
@@ -1896,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);
|
||||
}
|
||||
@@ -1945,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;
|
||||
}
|
||||
@@ -1988,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;
|
||||
@@ -2000,15 +2025,15 @@ 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_set_routing_exchange_state(etcp_conn, 1); // инициируем обмен маршрутами
|
||||
|
||||
+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
|
||||
+299
-64
@@ -14,7 +14,7 @@ in ---> {Q} -> [src, etcp] --> ... --> [dst,etcp] -> {asm_q} -> {buf q} ---> out
|
||||
#include "etcp_router.h"
|
||||
#include "etcp.h"
|
||||
#include "utun_instance.h"
|
||||
#include "route_bgp.h"
|
||||
#include "topo_group.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
@@ -41,7 +41,7 @@ static void router_close_and_notify(struct ETCP_ROUTER_CONN* rconn);
|
||||
static void router_close_finalize(void* arg);
|
||||
static void free_entry(struct ll_entry* e) { queue_dgram_free(e); queue_entry_free(e); }
|
||||
static int router_verify_signature(struct UTUN_INSTANCE* inst, struct ll_entry* entry, struct SVC_ROUTE_HDR* hdr, size_t* pl_len);
|
||||
static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN* rconn, uint32_t seq, uint64_t src_node_id);
|
||||
static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN* rconn, uint32_t seq, uint64_t src_node_id, uint16_t ack_ts);
|
||||
static void router_forward_transit(struct UTUN_INSTANCE* inst, struct ll_entry* entry, struct SVC_ROUTE_HDR* hdr);
|
||||
static int router_check_peer_restart(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN** prconn, struct SVC_ROUTE_HDR* hdr);
|
||||
static void router_handle_data_packet(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn, uint32_t seq, const uint8_t* pl, size_t pl_len);
|
||||
@@ -49,6 +49,87 @@ static void router_retransmit_one(struct ETCP_ROUTER_CONN* rconn, struct ROUTER_
|
||||
static void router_retrans_schedule(struct ETCP_ROUTER_CONN* rconn);
|
||||
static void router_retrans_timer_cb(void* arg);
|
||||
static void router_incoming_q_cb(struct ll_queue* q, void* arg);
|
||||
static void router_track_inflight_state(struct ETCP_ROUTER_CONN* rconn);
|
||||
static uint32_t router_effective_max_inflight(struct ETCP_ROUTER_CONN* rconn);
|
||||
static void router_update_inflight_limit(struct ETCP_ROUTER_CONN* rconn);
|
||||
static void router_update_minrtt(struct ETCP_ROUTER_CONN* rconn);
|
||||
|
||||
// ====================================================================
|
||||
// Транзитные очереди — per (src, dst) pair, backpressure через waiter на send_input_q
|
||||
|
||||
static struct TRANSIT_QUEUE* transit_queue_find(struct ETCP_CONN* conn,
|
||||
uint64_t src_node_id, uint64_t dst_node_id) {
|
||||
if (!conn->transit_queues) return NULL;
|
||||
uint8_t key[16];
|
||||
memcpy(key, &src_node_id, 8);
|
||||
memcpy(key + 8, &dst_node_id, 8);
|
||||
return (struct TRANSIT_QUEUE*)queue_find_data_by_index(conn->transit_queues, key);
|
||||
}
|
||||
|
||||
static struct TRANSIT_QUEUE* transit_queue_get_or_create(struct ETCP_CONN* conn,
|
||||
uint64_t src_node_id, uint64_t dst_node_id) {
|
||||
if (!conn->transit_queues) {
|
||||
struct UASYNC* ua = conn->instance ? conn->instance->ua : NULL;
|
||||
if (!ua) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "transit_queue_get_or_create: no ua"); return NULL; }
|
||||
conn->transit_queues = queue_new(ua, TRANSIT_QUEUE_HASH, 0, 16, "transit_q_reg");
|
||||
if (!conn->transit_queues) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "transit_queue_get_or_create: queue_new failed"); return NULL; }
|
||||
}
|
||||
struct TRANSIT_QUEUE* tq = transit_queue_find(conn, src_node_id, dst_node_id);
|
||||
if (tq) return tq;
|
||||
size_t data_size = sizeof(struct TRANSIT_QUEUE) - sizeof(struct ll_entry);
|
||||
tq = (struct TRANSIT_QUEUE*)queue_entry_new(data_size);
|
||||
if (!tq) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "transit_queue_get_or_create: queue_entry_new failed");
|
||||
return NULL;
|
||||
}
|
||||
memset(&tq->waiter, 0, sizeof(tq->waiter));
|
||||
tq->conn = conn;
|
||||
tq->q = queue_new(conn->instance->ua, 0, 0, 0, "transit_pair_q");
|
||||
if (!tq->q) { queue_entry_free(&tq->ll); return NULL; }
|
||||
memcpy(tq->ll.data, &src_node_id, 8);
|
||||
memcpy(tq->ll.data + 8, &dst_node_id, 8);
|
||||
queue_data_put_with_index(conn->transit_queues, &tq->ll);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "transit_q: new src=%016llx dst=%016llx",
|
||||
(unsigned long long)src_node_id, (unsigned long long)dst_node_id);
|
||||
return tq;
|
||||
}
|
||||
|
||||
static void transit_queue_destroy(struct ETCP_CONN* conn, struct TRANSIT_QUEUE* tq) {
|
||||
if (conn->send_input_q) queue_waiter_cancel(conn->send_input_q, &tq->waiter);
|
||||
struct ll_entry* e;
|
||||
while ((e = queue_data_get(tq->q)) != NULL) free_entry(e);
|
||||
queue_free(tq->q);
|
||||
queue_remove_data(conn->transit_queues, &tq->ll);
|
||||
queue_entry_free(&tq->ll);
|
||||
}
|
||||
|
||||
static void transit_queue_drain_cb(struct ll_queue* q, void* arg) {
|
||||
struct TRANSIT_QUEUE* tq = (struct TRANSIT_QUEUE*)arg;
|
||||
struct ETCP_CONN* conn = tq->conn;
|
||||
struct ll_entry* e = queue_data_get(tq->q);
|
||||
if (!e) { transit_queue_destroy(conn, tq); return; }
|
||||
int ret = etcp_send(conn, e);
|
||||
if (ret != 0) free_entry(e);
|
||||
if (queue_entry_count(tq->q) > 0)
|
||||
queue_waiter_wait(conn->send_input_q, &tq->waiter, transit_queue_drain_cb, tq);
|
||||
else
|
||||
transit_queue_destroy(conn, tq);
|
||||
}
|
||||
|
||||
void etcp_router_transit_queues_destroy(struct ETCP_CONN* conn) {
|
||||
if (!conn || !conn->transit_queues) return;
|
||||
struct ll_entry* entry;
|
||||
while ((entry = queue_data_get(conn->transit_queues)) != NULL) {
|
||||
struct TRANSIT_QUEUE* tq = (struct TRANSIT_QUEUE*)entry;
|
||||
if (conn->send_input_q) queue_waiter_cancel(conn->send_input_q, &tq->waiter);
|
||||
struct ll_entry* e;
|
||||
while ((e = queue_data_get(tq->q)) != NULL) free_entry(e);
|
||||
queue_free(tq->q);
|
||||
queue_entry_free(&tq->ll);
|
||||
}
|
||||
queue_free(conn->transit_queues);
|
||||
conn->transit_queues = NULL;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// Управление ROUTER_CONN
|
||||
@@ -93,22 +174,23 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t*
|
||||
if (is_signed) f |= ROUTER_FLAG_SIGNED;
|
||||
hdr->flags = f;
|
||||
}
|
||||
hdr->timestamp = get_current_timestamp();
|
||||
if (pl_len > 0) memcpy(dgram + SVC_ROUTE_HDR_SIZE, payload, pl_len);
|
||||
|
||||
struct ETCP_CONN* conn = NULL;
|
||||
struct ROUTE_BGP* bgp = inst->bgp;
|
||||
if (bgp) {
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(bgp, rconn->remote_node_id);
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(inst->topo_groups);
|
||||
if (group) {
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(group, rconn->remote_node_id);
|
||||
if (nq && nq->conn_mgr_type == CONN_TYPE_INDIRECT && nq->conn_mgr_intermediariy_count > 0) {
|
||||
for (uint8_t i = 0; i < nq->conn_mgr_intermediariy_count; i++) {
|
||||
conn = route_bgp_find_conn_for_node(bgp, nq->conn_mgr_intermediaries[i]);
|
||||
conn = topo_group_find_conn_for_node(group, nq->conn_mgr_intermediaries[i]);
|
||||
if (conn) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!conn) conn = route_bgp_find_conn_for_node(bgp, rconn->remote_node_id);
|
||||
if (!conn) conn = topo_group_find_conn_for_node(group, rconn->remote_node_id);
|
||||
if (!conn) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: no route to %016llx svc_id=%u",
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "no route to %016llx svc_id=%u",
|
||||
(unsigned long long)rconn->remote_node_id, rconn->svc_id);
|
||||
u_free(dgram); if (!flag_bits) rconn->tx_seq--; rconn->c_pkts_send_err++; return -1;
|
||||
}
|
||||
@@ -116,17 +198,17 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t*
|
||||
if (is_signed) {
|
||||
struct sc_stream_sign_state sign_state;
|
||||
if (sc_stream_sign_init(&conn->crypto_ctx, &sign_state) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: sign_init failed svc_id=%u", rconn->svc_id);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "sign_init failed svc_id=%u", rconn->svc_id);
|
||||
u_free(dgram); if (!flag_bits) rconn->tx_seq--; rconn->c_pkts_send_err++; return -1;
|
||||
}
|
||||
size_t data_len = SVC_ROUTE_HDR_SIZE + pl_len;
|
||||
if (sc_stream_sign_update(&sign_state, dgram, data_len) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: sign_update failed svc_id=%u", rconn->svc_id);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "sign_update failed svc_id=%u", rconn->svc_id);
|
||||
sc_stream_sign_cleanup(&sign_state); u_free(dgram); if (!flag_bits) rconn->tx_seq--; rconn->c_pkts_send_err++; return -1;
|
||||
}
|
||||
size_t actual_sig_len = SC_SIGN_SIZE;
|
||||
if (sc_stream_sign_final(&sign_state, dgram + data_len, &actual_sig_len) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: sign_final failed svc_id=%u", rconn->svc_id);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "sign_final failed svc_id=%u", rconn->svc_id);
|
||||
u_free(dgram); if (!flag_bits) rconn->tx_seq--; rconn->c_pkts_send_err++; return -1;
|
||||
}
|
||||
}
|
||||
@@ -147,7 +229,7 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t*
|
||||
if (!flag_bits) rconn->tx_seq--;
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
rconn->c_pkts_send_err++;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_one: etcp_send failed ret=%d svc_id=%u seq=%u", ret, rconn->svc_id, seq);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_send failed ret=%d svc_id=%u seq=%u", ret, rconn->svc_id, seq);
|
||||
} else {
|
||||
rconn->c_pkts_sent++;
|
||||
if (!flag_bits && pl_len > 0) {
|
||||
@@ -170,13 +252,14 @@ static int router_send_one_flags(struct ETCP_ROUTER_CONN* rconn, const uint8_t*
|
||||
if (!rconn->retrans_timer) router_retrans_schedule(rconn);
|
||||
}
|
||||
}
|
||||
if (!flag_bits) router_track_inflight_state(rconn);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Отправка без conn (RST для неизвестного src)
|
||||
static void router_send_to(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t svc_id, uint8_t flag_bits) {
|
||||
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, dst);
|
||||
struct ETCP_CONN* conn = topo_group_find_conn_for_node(topo_groups_get_default(inst->topo_groups), dst);
|
||||
if (!conn) return;
|
||||
struct SVC_ROUTE_HDR* hdr = (struct SVC_ROUTE_HDR*)u_malloc(SVC_ROUTE_HDR_SIZE);
|
||||
if (!hdr) return;
|
||||
@@ -186,16 +269,17 @@ static void router_send_to(struct UTUN_INSTANCE* inst, uint64_t dst, uint8_t svc
|
||||
hdr->seq = 0;
|
||||
hdr->flags = flag_bits;
|
||||
hdr->svc_id = svc_id;
|
||||
hdr->timestamp = get_current_timestamp();
|
||||
struct ll_entry* entry = queue_entry_new(0);
|
||||
if (!entry) { u_free(hdr); return; }
|
||||
entry->dgram = (uint8_t*)hdr;
|
||||
entry->len = SVC_ROUTE_HDR_SIZE;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE, "router_send_to: flags=0x%02x → %016llx svc_id=%u",
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE, "flags=0x%02x → %016llx svc_id=%u",
|
||||
flag_bits, (unsigned long long)dst, svc_id);
|
||||
int ret = etcp_send(conn, entry);
|
||||
if (ret != 0) {
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_send_to: etcp_send failed ret=%d", ret);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_send failed ret=%d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +314,7 @@ static void router_close_and_notify(struct ETCP_ROUTER_CONN* rconn) {
|
||||
rconn->closed = 1;
|
||||
router_send_close_to_service(rconn);
|
||||
// Отправляем CLOSE удалённой стороне
|
||||
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, rconn->remote_node_id);
|
||||
struct ETCP_CONN* conn = topo_group_find_conn_for_node(topo_groups_get_default(inst->topo_groups), rconn->remote_node_id);
|
||||
if (conn) router_send_one_flags(rconn, NULL, 0, ROUTER_FLAG_CLOSE, 0);
|
||||
// Таймеры
|
||||
if (rconn->ack_timer) { uasync_cancel_timeout(inst->ua, rconn->ack_timer); rconn->ack_timer = NULL; }
|
||||
@@ -276,14 +360,14 @@ static void router_drain_send_q(struct ETCP_ROUTER_CONN* rconn) {
|
||||
if (sq > 0) DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE, "SEND_Q_DRAIN: svc_id=%u send_q=%d inflight=%d",
|
||||
rconn->svc_id, sq, (int32_t)(rconn->tx_seq - rconn->tx_acked));
|
||||
while (1) {
|
||||
if ((int32_t)(rconn->tx_seq - rconn->tx_acked) >= ROUTER_MAX_INFLIGHT) break;
|
||||
if ((int32_t)(rconn->tx_seq - rconn->tx_acked) >= (int32_t)router_effective_max_inflight(rconn)) break;
|
||||
struct ll_entry* e = queue_data_get(rconn->send_q);
|
||||
if (!e) { rconn->send_blocked = 0; break; }
|
||||
u_check(e, "drain:after_get", "router");
|
||||
int s_err = router_send_one(rconn, e->dgram, e->len);
|
||||
u_check(e, "drain:after_send", "router");
|
||||
queue_dgram_free(e); queue_entry_free(e);
|
||||
if (s_err != 0 && (int32_t)(rconn->tx_seq - rconn->tx_acked) >= ROUTER_MAX_INFLIGHT) break;
|
||||
if (s_err != 0 && (int32_t)(rconn->tx_seq - rconn->tx_acked) >= (int32_t)router_effective_max_inflight(rconn)) break;
|
||||
}
|
||||
if (rconn->send_blocked && !rconn->send_resume_timer)
|
||||
rconn->send_resume_timer = uasync_set_timeout(rconn->inst->ua,
|
||||
@@ -305,7 +389,7 @@ static void router_no_route_retry_cb(void* arg) {
|
||||
struct ETCP_ROUTER_CONN* rconn = (struct ETCP_ROUTER_CONN*)arg;
|
||||
if (rconn->closed) return;
|
||||
rconn->no_route_timer = NULL;
|
||||
struct ETCP_CONN* c = route_bgp_find_conn_for_node(rconn->inst->bgp, rconn->remote_node_id);
|
||||
struct ETCP_CONN* c = topo_group_find_conn_for_node(topo_groups_get_default(rconn->inst->topo_groups), rconn->remote_node_id);;
|
||||
if (c) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "router: route appeared for %016llx svc_id=%u, draining send_q=%d",
|
||||
(unsigned long long)rconn->remote_node_id, rconn->svc_id, queue_entry_count(rconn->send_q));
|
||||
@@ -333,19 +417,20 @@ static void router_retransmit_one(struct ETCP_ROUTER_CONN* rconn, struct ROUTER_
|
||||
hdr->seq = inf->seq;
|
||||
hdr->svc_id = rconn->svc_id;
|
||||
hdr->flags = (rconn->sess_id << ROUTER_SESS_ID_SHIFT);
|
||||
hdr->timestamp = get_current_timestamp();
|
||||
|
||||
struct ETCP_CONN* conn = NULL;
|
||||
struct ROUTE_BGP* bgp = inst->bgp;
|
||||
if (bgp) {
|
||||
struct NODEINFO_Q* nq = nodeinfo_find_by_id(bgp, rconn->remote_node_id);
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(inst->topo_groups);
|
||||
if (group) {
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(group, rconn->remote_node_id);
|
||||
if (nq && nq->conn_mgr_type == CONN_TYPE_INDIRECT && nq->conn_mgr_intermediariy_count > 0) {
|
||||
for (uint8_t i = 0; i < nq->conn_mgr_intermediariy_count; i++) {
|
||||
conn = route_bgp_find_conn_for_node(bgp, nq->conn_mgr_intermediaries[i]);
|
||||
conn = topo_group_find_conn_for_node(group, nq->conn_mgr_intermediaries[i]);
|
||||
if (conn) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!conn) conn = route_bgp_find_conn_for_node(bgp, rconn->remote_node_id);
|
||||
if (!conn) conn = topo_group_find_conn_for_node(group, rconn->remote_node_id);
|
||||
if (!conn) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_retransmit: no route to %016llx svc_id=%u",
|
||||
(unsigned long long)rconn->remote_node_id, rconn->svc_id);
|
||||
@@ -423,7 +508,7 @@ static int router_enqueue_send(struct ETCP_ROUTER_CONN* rconn, const uint8_t* pa
|
||||
return -1;
|
||||
}
|
||||
struct ll_entry* qe = queue_entry_new(0);
|
||||
if (!qe) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router_enqueue_send: queue_entry_new failed"); return -1; }
|
||||
if (!qe) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "queue_entry_new failed"); return -1; }
|
||||
qe->dgram = u_malloc(pl_len);
|
||||
if (!qe->dgram) { queue_entry_free(qe); return -1; }
|
||||
qe->len = pl_len;
|
||||
@@ -439,6 +524,90 @@ static int router_enqueue_send(struct ETCP_ROUTER_CONN* rconn, const uint8_t* pa
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// minRTT helpers
|
||||
// ====================================================================
|
||||
|
||||
static uint32_t router_effective_max_inflight(struct ETCP_ROUTER_CONN* rconn) {
|
||||
return rconn->inflight_limit;
|
||||
}
|
||||
|
||||
static void router_update_inflight_limit(struct ETCP_ROUTER_CONN* rconn) {
|
||||
uint32_t new_limit = rconn->minrtt_probe ? MINRTT_PROBE_MAX_INFLIGHT : rconn->max_inflight;
|
||||
if (new_limit == rconn->inflight_limit) return;
|
||||
uint32_t old = rconn->inflight_limit;
|
||||
rconn->inflight_limit = new_limit;
|
||||
uint32_t inflight = rconn->tx_seq - rconn->tx_acked;
|
||||
|
||||
if (new_limit < old && inflight >= new_limit) {
|
||||
if (!rconn->send_blocked) rconn->send_blocked = 1;
|
||||
}
|
||||
if (new_limit > old && (rconn->send_blocked || queue_entry_count(rconn->send_q) > 0)) {
|
||||
router_drain_send_q(rconn);
|
||||
}
|
||||
}
|
||||
|
||||
void router_set_max_inflight(struct ETCP_ROUTER_CONN* rconn, uint16_t new_max) {
|
||||
if (!rconn) return;
|
||||
if (new_max < MINRTT_PROBE_MAX_INFLIGHT) new_max = MINRTT_PROBE_MAX_INFLIGHT;
|
||||
rconn->max_inflight = new_max;
|
||||
if (!rconn->minrtt_probe) router_update_inflight_limit(rconn);
|
||||
router_track_inflight_state(rconn);
|
||||
}
|
||||
|
||||
static void router_track_inflight_state(struct ETCP_ROUTER_CONN* rconn) {
|
||||
uint32_t inflight = rconn->tx_seq - rconn->tx_acked;
|
||||
uint32_t threshold = rconn->max_inflight / 2;
|
||||
uint8_t is_loaded = (inflight >= threshold);
|
||||
if (is_loaded && !rconn->inflight_was_loaded) {
|
||||
rconn->last_inflight_loaded_tb = get_time_tb();
|
||||
rconn->inflight_was_loaded = 1;
|
||||
} else if (!is_loaded && rconn->inflight_was_loaded) {
|
||||
rconn->last_inflight_unloaded_tb = get_time_tb();
|
||||
rconn->inflight_was_loaded = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void router_update_minrtt(struct ETCP_ROUTER_CONN* rconn) {
|
||||
uint64_t now = get_time_tb();
|
||||
uint16_t w_idx = rconn->minrtt_window_idx;
|
||||
uint8_t w_cnt = rconn->minrtt_window_count;
|
||||
|
||||
// Probe check: oldest entry freshness
|
||||
uint8_t prev_probe = rconn->minrtt_probe;
|
||||
if (w_cnt > 0) {
|
||||
uint8_t oldest = (w_idx - w_cnt + MINRTT_WINDOW_SIZE) % MINRTT_WINDOW_SIZE;
|
||||
uint64_t oldest_age = now - rconn->minrtt_window_tb[oldest];
|
||||
if (w_cnt >= MINRTT_WINDOW_SIZE && oldest_age < MINRTT_PROBE_TIMEOUT_TB) {
|
||||
rconn->minrtt_probe = 0;
|
||||
} else if (oldest_age >= MINRTT_PROBE_TIMEOUT_TB) {
|
||||
rconn->minrtt_probe = 1;
|
||||
}
|
||||
}
|
||||
if (prev_probe != rconn->minrtt_probe) router_update_inflight_limit(rconn);
|
||||
|
||||
// Update minRTT if channel is unloaded long enough
|
||||
if (!rconn->inflight_was_loaded) {
|
||||
uint64_t unloaded_dur = now - rconn->last_inflight_unloaded_tb;
|
||||
uint64_t min_dur = (uint64_t)rconn->rtt * 2;
|
||||
if (min_dur < MINRTT_UNLOADED_MIN_TB) min_dur = MINRTT_UNLOADED_MIN_TB;
|
||||
if (unloaded_dur >= min_dur) {
|
||||
rconn->minrtt_window[w_idx] = rconn->rtt;
|
||||
rconn->minrtt_window_tb[w_idx] = now;
|
||||
rconn->minrtt_window_idx = (w_idx + 1) % MINRTT_WINDOW_SIZE;
|
||||
if (w_cnt < MINRTT_WINDOW_SIZE) w_cnt++;
|
||||
rconn->minrtt_window_count = w_cnt;
|
||||
uint32_t sum = 0, cnt = 0;
|
||||
for (int i = 0; i < MINRTT_WINDOW_SIZE; i++) {
|
||||
if (rconn->minrtt_window[i] > 0) { sum += rconn->minrtt_window[i]; cnt++; }
|
||||
}
|
||||
rconn->minrtt = cnt > 0 ? (uint16_t)(sum / cnt) : MINRTT_DEFAULT_TB;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE, "MINRTT_UPD: svc_id=%u minrtt=%u (rtt=%u cnt=%u probe=%d unloaded=%lu)",
|
||||
rconn->svc_id, rconn->minrtt, rconn->rtt, cnt, rconn->minrtt_probe, (unsigned long)unloaded_dur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// ACK timers
|
||||
// ====================================================================
|
||||
@@ -449,7 +618,7 @@ static void router_ack_do_send(struct ETCP_ROUTER_CONN* rconn) {
|
||||
|
||||
static void router_send_ack(struct ETCP_ROUTER_CONN* rconn) {
|
||||
struct UTUN_INSTANCE* inst = rconn->inst;
|
||||
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, rconn->remote_node_id);
|
||||
struct ETCP_CONN* conn = topo_group_find_conn_for_node(topo_groups_get_default(inst->topo_groups), rconn->remote_node_id);
|
||||
if (!conn) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_ack: no route to %016llx svc_id=%u",
|
||||
(unsigned long long)rconn->remote_node_id, rconn->svc_id);
|
||||
@@ -464,6 +633,13 @@ static void router_send_ack(struct ETCP_ROUTER_CONN* rconn) {
|
||||
hdr->seq = rconn->rx_seq;
|
||||
hdr->svc_id = rconn->svc_id;
|
||||
hdr->flags = (rconn->sess_id << ROUTER_SESS_ID_SHIFT);
|
||||
if (rconn->last_recv_updated) {
|
||||
uint64_t now = get_time_tb();
|
||||
hdr->timestamp = rconn->last_recv_pkt_ts + (uint32_t)(now - rconn->last_recv_pkt_local_tb);
|
||||
rconn->last_recv_updated = 0;
|
||||
} else {
|
||||
hdr->timestamp = 0;
|
||||
}
|
||||
|
||||
struct ll_entry* entry = queue_entry_new(0);
|
||||
if (!entry) { u_free(hdr); return; }
|
||||
@@ -592,7 +768,7 @@ static void router_incoming_q_cb(struct ll_queue* q, void* arg) {
|
||||
|
||||
queue_data_put_with_index(rconn->recv_q, rq);
|
||||
|
||||
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(rconn->inst->bgp, rconn->remote_node_id);
|
||||
struct ETCP_CONN* conn = topo_group_find_conn_for_node(topo_groups_get_default(rconn->inst->topo_groups), rconn->remote_node_id);;
|
||||
if (seq == rconn->rx_seq) router_try_assembly(rconn, conn);
|
||||
router_schedule_ack(rconn);
|
||||
queue_resume_callback(q);
|
||||
@@ -612,8 +788,8 @@ static int router_verify_signature(struct UTUN_INSTANCE* inst, struct ll_entry*
|
||||
}
|
||||
size_t actual_payload = *pl_len - SC_SIGN_SIZE;
|
||||
uint8_t* sig = (uint8_t*)entry->dgram + SVC_ROUTE_HDR_SIZE + actual_payload;
|
||||
struct ROUTE_BGP* bgp = inst->bgp;
|
||||
struct NODEINFO_Q* nq = bgp ? nodeinfo_find_by_id(bgp, hdr->src_node_id) : NULL;
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(inst->topo_groups);
|
||||
struct TOPO_NODEQ* nq = group ? topo_node_find_by_id(group, hdr->src_node_id) : NULL;
|
||||
if (!nq) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: SIGNED packet from unknown node %016llx — dropping",
|
||||
(unsigned long long)hdr->src_node_id);
|
||||
@@ -621,14 +797,14 @@ static int router_verify_signature(struct UTUN_INSTANCE* inst, struct ll_entry*
|
||||
}
|
||||
{
|
||||
uint8_t zero[SC_PUBKEY_SIZE] = {0};
|
||||
if (memcmp(nq->node.ed25519_public_key, zero, SC_PUBKEY_SIZE) == 0) {
|
||||
if (memcmp(nq->node->ed25519_public_key, zero, SC_PUBKEY_SIZE) == 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router: no Ed25519 pubkey for node %016llx — dropping",
|
||||
(unsigned long long)hdr->src_node_id);
|
||||
free_entry(entry); return -1;
|
||||
}
|
||||
}
|
||||
struct sc_stream_sign_state vfy_state;
|
||||
if (sc_stream_sign_verify_init(&vfy_state, nq->node.ed25519_public_key) != SC_OK) {
|
||||
if (sc_stream_sign_verify_init(&vfy_state, nq->node->ed25519_public_key) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router: sign_verify_init failed for node %016llx",
|
||||
(unsigned long long)hdr->src_node_id);
|
||||
free_entry(entry); return -1;
|
||||
@@ -653,9 +829,16 @@ static int router_verify_signature(struct UTUN_INSTANCE* inst, struct ll_entry*
|
||||
}
|
||||
|
||||
static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN* rconn,
|
||||
uint32_t seq, uint64_t src_node_id) {
|
||||
uint32_t seq, uint64_t src_node_id, uint16_t ack_ts) {
|
||||
(void)src_node_id;
|
||||
if (!rconn) return;
|
||||
if (ack_ts != 0) {
|
||||
uint16_t new_rtt = get_current_timestamp() - ack_ts;
|
||||
int d_rtt = (int)new_rtt - (int)rconn->rtt;
|
||||
if (d_rtt < 0) d_rtt = -d_rtt;
|
||||
rconn->rtt_jitter += ((int32_t)(d_rtt * 65536 - rconn->rtt_jitter)) / 32;
|
||||
rconn->rtt = new_rtt;
|
||||
}
|
||||
if ((int32_t)(seq - rconn->tx_acked) >= 0) {
|
||||
uint32_t old_acked = rconn->tx_acked;
|
||||
rconn->tx_acked = seq;
|
||||
@@ -671,6 +854,8 @@ static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CON
|
||||
}
|
||||
rconn->last_ack_changed_tb = get_time_tb();
|
||||
rconn->no_ack_count = 0;
|
||||
router_track_inflight_state(rconn);
|
||||
router_update_minrtt(rconn);
|
||||
if (queue_entry_count(rconn->inflight_q) == 0 && rconn->retrans_timer) {
|
||||
uasync_cancel_timeout(rconn->inst->ua, rconn->retrans_timer);
|
||||
rconn->retrans_timer = NULL;
|
||||
@@ -690,19 +875,33 @@ static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CON
|
||||
|
||||
static void router_forward_transit(struct UTUN_INSTANCE* inst, struct ll_entry* entry,
|
||||
struct SVC_ROUTE_HDR* hdr) {
|
||||
struct ETCP_CONN* next = route_bgp_find_conn_for_node(inst->bgp, hdr->dst_node_id);
|
||||
struct ETCP_CONN* next = topo_group_find_conn_for_node(topo_groups_get_default(inst->topo_groups), hdr->dst_node_id);
|
||||
if (!next) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "etcp_router: no route to %016llx svc_id=%u, dropping",
|
||||
(unsigned long long)hdr->dst_node_id, hdr->svc_id);
|
||||
free_entry(entry); return;
|
||||
}
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCPROUTE, "etcp_router: forwarding svc_id=%u → %016llx via %s",
|
||||
hdr->svc_id, (unsigned long long)hdr->dst_node_id, next->log_name);
|
||||
int fwd_err = etcp_send(next, entry);
|
||||
if (fwd_err != 0) {
|
||||
free_entry(entry);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_router: transit etcp_send failed ret=%d", fwd_err);
|
||||
struct TRANSIT_QUEUE* tq = transit_queue_find(next, hdr->src_node_id, hdr->dst_node_id);
|
||||
|
||||
if (!tq && next->send_input_q && queue_entry_count(next->send_input_q) == 0) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCPROUTE, "etcp_router: forwarding svc_id=%u → %016llx via %s",
|
||||
hdr->svc_id, (unsigned long long)hdr->dst_node_id, next->log_name);
|
||||
int ret = etcp_send(next, entry);
|
||||
if (ret != 0) free_entry(entry);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tq) {
|
||||
tq = transit_queue_get_or_create(next, hdr->src_node_id, hdr->dst_node_id);
|
||||
if (!tq) { free_entry(entry); return; }
|
||||
}
|
||||
int was_empty = (queue_entry_count(tq->q) == 0);
|
||||
queue_data_put(tq->q, entry);
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCPROUTE, "etcp_router: queued transit svc_id=%u src=%016llx dst=%016llx q=%d",
|
||||
hdr->svc_id, (unsigned long long)hdr->src_node_id,
|
||||
(unsigned long long)hdr->dst_node_id, queue_entry_count(tq->q));
|
||||
if (was_empty && next->send_input_q)
|
||||
queue_waiter_wait(next->send_input_q, &tq->waiter, transit_queue_drain_cb, tq);
|
||||
}
|
||||
|
||||
static int router_check_peer_restart(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN** prconn,
|
||||
@@ -739,7 +938,7 @@ static void router_handle_data_packet(struct ETCP_ROUTER_CONN* rconn, struct ETC
|
||||
|
||||
{// SEQ out of bounds
|
||||
int32_t d = (int32_t)(seq - rconn->rx_seq);
|
||||
if (d > ROUTER_MAX_INFLIGHT || d < -ROUTER_MAX_INFLIGHT) {
|
||||
if (d > (int32_t)rconn->max_inflight || d < -(int32_t)rconn->max_inflight) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "router: seq=%u out of bounds, rx_seq=%u (d=%d), dropping",
|
||||
seq, rconn->rx_seq, d);
|
||||
rconn->c_oob_dropped++; return;
|
||||
@@ -794,7 +993,7 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
if (rconn) router_close_and_notify(rconn);
|
||||
free_entry(entry); return;
|
||||
}
|
||||
if (pl_len == 0) { router_handle_ack(inst, rconn, hdr->seq, hdr->src_node_id); free_entry(entry); return; }
|
||||
if (pl_len == 0) { router_handle_ack(inst, rconn, hdr->seq, hdr->src_node_id, hdr->timestamp); free_entry(entry); return; }
|
||||
|
||||
if (!rconn) {
|
||||
rconn = etcp_router_conn_get(inst, hdr->src_node_id, hdr->svc_id);
|
||||
@@ -803,6 +1002,9 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
}
|
||||
if (router_check_peer_restart(inst, &rconn, hdr) != 0) { free_entry(entry); return; }
|
||||
|
||||
rconn->last_recv_pkt_ts = hdr->timestamp;
|
||||
rconn->last_recv_pkt_local_tb = get_time_tb();
|
||||
rconn->last_recv_updated = 1;
|
||||
router_handle_data_packet(rconn, conn, hdr->seq, pl, pl_len);
|
||||
free_entry(entry);
|
||||
}
|
||||
@@ -812,17 +1014,17 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry)
|
||||
// ====================================================================
|
||||
|
||||
int etcp_router_init(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_router_init: NULL instance"); return -1; }
|
||||
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "NULL instance"); return -1; }
|
||||
memset(&inst->router_bindings, 0, sizeof(inst->router_bindings));
|
||||
inst->router_conns = queue_new(inst->ua,
|
||||
ROUTER_CONN_HASH_SIZE, 0, 9, "router_conns");
|
||||
if (!inst->router_conns) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_router_init: queue_new(router_conns) failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "queue_new(router_conns) failed");
|
||||
return -1;
|
||||
}
|
||||
int ret = etcp_bind(inst, ETCP_ID_SVC_ROUTE, etcp_router_recv_cb);
|
||||
if (ret != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_router_init: etcp_bind failed, ret=%d", ret);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_bind failed, ret=%d", ret);
|
||||
queue_free(inst->router_conns); inst->router_conns = NULL;
|
||||
return -1;
|
||||
}
|
||||
@@ -847,46 +1049,47 @@ void etcp_router_destroy(struct UTUN_INSTANCE* inst) {
|
||||
}
|
||||
|
||||
int etcp_router_bind(struct UTUN_INSTANCE* inst, uint8_t svc_id, etcp_recv_fn callback) {
|
||||
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_router_bind: NULL instance"); return -1; }
|
||||
if (!callback) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_router_bind: NULL callback for svc_id=%u", svc_id); return -1; }
|
||||
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "NULL instance"); return -1; }
|
||||
if (!callback) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "NULL callback for svc_id=%u", svc_id); return -1; }
|
||||
if (inst->router_bindings.callbacks[svc_id])
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "etcp_router_bind: overwriting svc_id=%u", svc_id);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "overwriting svc_id=%u", svc_id);
|
||||
inst->router_bindings.callbacks[svc_id] = callback;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "etcp_router_bind: svc_id=%u → cb=%p", svc_id, (void*)callback);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "svc_id=%u → cb=%p", svc_id, (void*)callback);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int etcp_router_unbind(struct UTUN_INSTANCE* inst, uint8_t svc_id) {
|
||||
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_router_unbind: NULL instance"); return -1; }
|
||||
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "NULL instance"); return -1; }
|
||||
if (!inst->router_bindings.callbacks[svc_id]) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "etcp_router_unbind: svc_id=%u not bound", svc_id);
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "svc_id=%u not bound", svc_id);
|
||||
return -1;
|
||||
}
|
||||
inst->router_bindings.callbacks[svc_id] = NULL;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "etcp_router_unbind: svc_id=%u", svc_id);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCPROUTE, "svc_id=%u", svc_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t dst_node_id, struct ll_entry* entry, int force) {
|
||||
if (!inst || !entry) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_route_send: NULL inst=%p entry=%p", (void*)inst, (void*)entry);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "NULL inst=%p entry=%p", (void*)inst, (void*)entry);
|
||||
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return -1;
|
||||
}
|
||||
if (!entry->dgram || entry->len < 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "etcp_route_send: empty entry"); queue_dgram_free(entry); queue_entry_free(entry); return -1;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCPROUTE, "empty entry: dgram=%p len=%u dst=0x%016llx force=%d",
|
||||
(void*)entry->dgram, entry->len, (unsigned long long)dst_node_id, force); queue_dgram_free(entry); queue_entry_free(entry); return -1;
|
||||
}
|
||||
uint8_t svc_id = entry->dgram[0];
|
||||
size_t payload_len = entry->len - 1;
|
||||
|
||||
if (dst_node_id == inst->node_id) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCPROUTE, "etcp_route_send: loopback svc_id=%u len=%zu", svc_id, payload_len);
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ETCPROUTE, "loopback svc_id=%u len=%zu", svc_id, payload_len);
|
||||
if (inst->router_bindings.callbacks[svc_id])
|
||||
inst->router_bindings.callbacks[svc_id](NULL, entry);
|
||||
else { queue_dgram_free(entry); queue_entry_free(entry); }
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ETCP_CONN* etcp_conn = route_bgp_find_conn_for_node(inst->bgp, dst_node_id);
|
||||
struct ETCP_CONN* etcp_conn = topo_group_find_conn_for_node(topo_groups_get_default(inst->topo_groups), dst_node_id);
|
||||
if (!etcp_conn) {
|
||||
struct ETCP_ROUTER_CONN* rconn = etcp_router_conn_get(inst, dst_node_id, svc_id);
|
||||
if (!rconn) { queue_dgram_free(entry); queue_entry_free(entry); return -1; }
|
||||
@@ -905,7 +1108,7 @@ int etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t dst_node_id, struct ll_
|
||||
struct ETCP_ROUTER_CONN* rconn = etcp_router_conn_get(inst, dst_node_id, svc_id);
|
||||
if (!rconn) { queue_dgram_free(entry); queue_entry_free(entry); return -1; }
|
||||
|
||||
if ((int32_t)(rconn->tx_seq - rconn->tx_acked) >= ROUTER_MAX_INFLIGHT) {
|
||||
if ((int32_t)(rconn->tx_seq - rconn->tx_acked) >= (int32_t)router_effective_max_inflight(rconn)) {
|
||||
int eq_ret = router_enqueue_send(rconn, entry->dgram + 1, payload_len, force);
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
return eq_ret;
|
||||
@@ -917,8 +1120,8 @@ int etcp_route_send(struct UTUN_INSTANCE* inst, uint64_t dst_node_id, struct ll_
|
||||
}
|
||||
|
||||
int etcp_router_input_q_count(struct UTUN_INSTANCE* inst, uint64_t node_id) {
|
||||
if (!inst || !inst->bgp) return -1;
|
||||
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, node_id);
|
||||
if (!inst || !topo_groups_get_default(inst->topo_groups)) return -1;
|
||||
struct ETCP_CONN* conn = topo_group_find_conn_for_node(topo_groups_get_default(inst->topo_groups), node_id);
|
||||
if (!conn || !conn->normalizer || !conn->normalizer->input) return -1;
|
||||
return queue_entry_count(conn->normalizer->input);
|
||||
}
|
||||
@@ -926,16 +1129,16 @@ int etcp_router_input_q_count(struct UTUN_INSTANCE* inst, uint64_t node_id) {
|
||||
void etcp_router_waiter_register(struct UTUN_INSTANCE* inst, uint64_t peer_node_id,
|
||||
struct queue_waiter_handle* h,
|
||||
queue_threshold_callback_fn callback, void* arg) {
|
||||
if (!inst || !inst->bgp || !h) return;
|
||||
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, peer_node_id);
|
||||
if (!inst || !topo_groups_get_default(inst->topo_groups) || !h) return;
|
||||
struct ETCP_CONN* conn = topo_group_find_conn_for_node(topo_groups_get_default(inst->topo_groups), peer_node_id);
|
||||
if (!conn || !conn->normalizer || !conn->normalizer->input) return;
|
||||
queue_waiter_wait(conn->normalizer->input, h, callback, arg);
|
||||
}
|
||||
|
||||
void etcp_router_waiter_cancel(struct UTUN_INSTANCE* inst, uint64_t peer_node_id,
|
||||
struct queue_waiter_handle* h) {
|
||||
if (!inst || !inst->bgp || !h) return;
|
||||
struct ETCP_CONN* conn = route_bgp_find_conn_for_node(inst->bgp, peer_node_id);
|
||||
if (!inst || !topo_groups_get_default(inst->topo_groups) || !h) return;
|
||||
struct ETCP_CONN* conn = topo_group_find_conn_for_node(topo_groups_get_default(inst->topo_groups), peer_node_id);
|
||||
if (!conn || !conn->normalizer || !conn->normalizer->input) return;
|
||||
queue_waiter_cancel(conn->normalizer->input, h);
|
||||
}
|
||||
@@ -1017,6 +1220,22 @@ void etcp_router_conn_restart(struct UTUN_INSTANCE* inst, uint64_t remote_node_i
|
||||
rconn->c_stale_ack = 0;
|
||||
rconn->c_sign_fail = 0;
|
||||
rconn->last_ack_changed_tb = 0;
|
||||
rconn->rtt = 0;
|
||||
rconn->rtt_jitter = 0;
|
||||
rconn->max_inflight = ROUTER_MAX_INFLIGHT;
|
||||
rconn->inflight_limit = ROUTER_MAX_INFLIGHT;
|
||||
rconn->minrtt = MINRTT_DEFAULT_TB;
|
||||
memset(rconn->minrtt_window, 0, sizeof(rconn->minrtt_window));
|
||||
memset(rconn->minrtt_window_tb, 0, sizeof(rconn->minrtt_window_tb));
|
||||
rconn->minrtt_window_idx = 0;
|
||||
rconn->minrtt_window_count = 0;
|
||||
rconn->minrtt_probe = 0;
|
||||
rconn->last_inflight_loaded_tb = 0;
|
||||
rconn->last_inflight_unloaded_tb = 0;
|
||||
rconn->inflight_was_loaded = 0;
|
||||
rconn->last_recv_pkt_ts = 0;
|
||||
rconn->last_recv_pkt_local_tb = 0;
|
||||
rconn->last_recv_updated = 0;
|
||||
rconn->no_route = 0;
|
||||
rconn->no_ack_count = 0;
|
||||
rconn->closed = 0;
|
||||
@@ -1055,6 +1274,22 @@ struct ETCP_ROUTER_CONN* etcp_router_conn_get(struct UTUN_INSTANCE* inst,
|
||||
rconn->remote_node_id = remote_node_id;
|
||||
rconn->svc_id = svc_id;
|
||||
rconn->last_dgram_ts = get_current_timestamp();
|
||||
rconn->rtt = 0;
|
||||
rconn->rtt_jitter = 0;
|
||||
rconn->max_inflight = ROUTER_MAX_INFLIGHT;
|
||||
rconn->inflight_limit = ROUTER_MAX_INFLIGHT;
|
||||
rconn->minrtt = MINRTT_DEFAULT_TB;
|
||||
memset(rconn->minrtt_window, 0, sizeof(rconn->minrtt_window));
|
||||
memset(rconn->minrtt_window_tb, 0, sizeof(rconn->minrtt_window_tb));
|
||||
rconn->minrtt_window_idx = 0;
|
||||
rconn->minrtt_window_count = 0;
|
||||
rconn->minrtt_probe = 0;
|
||||
rconn->last_inflight_loaded_tb = 0;
|
||||
rconn->last_inflight_unloaded_tb = 0;
|
||||
rconn->inflight_was_loaded = 0;
|
||||
rconn->last_recv_pkt_ts = 0;
|
||||
rconn->last_recv_pkt_local_tb = 0;
|
||||
rconn->last_recv_updated = 0;
|
||||
rconn->tx_seq = 0;
|
||||
rconn->rx_seq = 0;
|
||||
rconn->tx_acked = 0;
|
||||
@@ -1111,7 +1346,7 @@ int etcp_router_conn_send(struct ETCP_ROUTER_CONN* rconn,
|
||||
(void*)rconn, (const void*)data, len);
|
||||
return -1;
|
||||
}
|
||||
if ((int32_t)(rconn->tx_seq - rconn->tx_acked) >= ROUTER_MAX_INFLIGHT) {
|
||||
if ((int32_t)(rconn->tx_seq - rconn->tx_acked) >= (int32_t)router_effective_max_inflight(rconn)) {
|
||||
return router_enqueue_send(rconn, data, len, 0);
|
||||
}
|
||||
return router_send_one(rconn, data, len);
|
||||
@@ -1124,7 +1359,7 @@ int etcp_router_conn_send_signed(struct ETCP_ROUTER_CONN* rconn,
|
||||
(void*)rconn, (const void*)data, len);
|
||||
return -1;
|
||||
}
|
||||
if ((int32_t)(rconn->tx_seq - rconn->tx_acked) >= ROUTER_MAX_INFLIGHT) {
|
||||
if ((int32_t)(rconn->tx_seq - rconn->tx_acked) >= (int32_t)router_effective_max_inflight(rconn)) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_conn_send_signed: inflight full svc_id=%u", rconn->svc_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
+55
-1
@@ -9,9 +9,16 @@
|
||||
#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) / ETCP_RT_ID_SVC_ROUTE
|
||||
@@ -20,9 +27,10 @@ struct SVC_ROUTE_HDR {
|
||||
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
|
||||
@@ -33,12 +41,41 @@ struct SVC_ROUTE_HDR {
|
||||
#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)
|
||||
@@ -100,6 +137,13 @@ struct ETCP_ROUTER_CONN {
|
||||
#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)
|
||||
@@ -144,6 +188,9 @@ 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),
|
||||
@@ -174,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;
|
||||
|
||||
@@ -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
|
||||
+16
-14
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -2,6 +2,11 @@
|
||||
#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"
|
||||
@@ -80,4 +85,8 @@ 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
|
||||
@@ -67,9 +67,9 @@ 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; }
|
||||
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);
|
||||
@@ -573,15 +573,15 @@ struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, str
|
||||
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 (!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, "tcp_proxy_client_create: no proxy mode enabled"); return NULL;
|
||||
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;
|
||||
@@ -589,7 +589,7 @@ struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, str
|
||||
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; }
|
||||
|
||||
if (need_tun) {
|
||||
p->tun = tun_init_nat(ua, tun_name, tun_ip, mtu > 0 ? mtu : 1500, test_mode);
|
||||
@@ -597,7 +597,7 @@ struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, str
|
||||
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; }
|
||||
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++) {
|
||||
|
||||
@@ -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"
|
||||
@@ -82,4 +87,8 @@ struct tcp_proxy_client* tcp_proxy_client_create(struct UTUN_INSTANCE* inst, str
|
||||
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
|
||||
@@ -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"
|
||||
@@ -73,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
|
||||
@@ -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
|
||||
-1237
File diff suppressed because it is too large.
Load diff
-227
@@ -1,227 +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 ROUTE_SUBCMD_TABLE_COMPLETE 0x0B // завершение начальной синхронизации таблицы
|
||||
|
||||
#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
|
||||
+54
-113
@@ -13,8 +13,8 @@
|
||||
#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
|
||||
@@ -22,7 +22,7 @@
|
||||
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];
|
||||
@@ -68,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;
|
||||
}
|
||||
@@ -123,7 +123,7 @@ 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) {
|
||||
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))
|
||||
@@ -150,7 +150,7 @@ 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 (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 {
|
||||
@@ -251,22 +251,22 @@ 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) return;
|
||||
if (ctx->nq) {
|
||||
struct NODE_CONNECTIVITY* c = &ctx->nq->connectivity;
|
||||
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:
|
||||
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 ADDR_TYPE_NAT:
|
||||
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 ADDR_TYPE_REAL:
|
||||
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();
|
||||
@@ -277,7 +277,7 @@ static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok) {
|
||||
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,
|
||||
(unsigned long long)ctx->nq->node->node_id,
|
||||
c->interface_status, c->nat_status, c->real_status);
|
||||
}
|
||||
}
|
||||
@@ -287,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;
|
||||
}
|
||||
|
||||
@@ -308,150 +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;
|
||||
|
||||
ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list;
|
||||
nq->connectivity.probe_list = ctx;
|
||||
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;
|
||||
|
||||
ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list;
|
||||
nq->connectivity.probe_list = ctx;
|
||||
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;
|
||||
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);
|
||||
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
|
||||
+40
-81
@@ -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"
|
||||
|
||||
@@ -110,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;
|
||||
}
|
||||
@@ -134,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);
|
||||
@@ -145,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;
|
||||
}
|
||||
@@ -277,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,632 +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 "route_lib.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;
|
||||
}
|
||||
|
||||
static int is_node_connectivity_active(struct NODE_CONNECTIVITY* c) {
|
||||
return c->probe_status != PROBE_STATUS_NONE
|
||||
|| c->interface_status != PROBE_RESULT_UNKNOWN
|
||||
|| c->nat_status != PROBE_RESULT_UNKNOWN
|
||||
|| c->real_status != PROBE_RESULT_UNKNOWN
|
||||
|| c->probe_start_time != 0
|
||||
|| c->ping_req_time != 0;
|
||||
}
|
||||
|
||||
static const char* probe_status_str(uint8_t s) {
|
||||
switch (s) { case 0: return "NONE"; case 1: return "IN_PROGRESS"; case 2: return "DONE"; default: return "?"; }
|
||||
}
|
||||
static const char* probe_result_str(uint8_t s) {
|
||||
switch (s) { case 0: return "UNKNOWN"; case 1: return "REACHABLE"; case 2: return "UNREACHABLE"; default: return "?"; }
|
||||
}
|
||||
static const char* conn_mgr_type_str(uint8_t t) {
|
||||
switch (t) { case 1: return "DIRECT"; case 2: return "REVERSE"; case 3: return "INDIRECT"; default: return "NONE"; }
|
||||
}
|
||||
|
||||
void route_node_dump_all(struct ROUTE_BGP* bgp) {
|
||||
if (!bgp || !bgp->nodes) return;
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "=== BGP Nodes dump: %d nodes ===", queue_entry_count(bgp->nodes));
|
||||
|
||||
int idx = 0;
|
||||
struct ll_entry* e = bgp->nodes->head;
|
||||
while (e) {
|
||||
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e;
|
||||
struct NODEINFO* ni = &nq->node;
|
||||
int is_local = (bgp->local_node == nq);
|
||||
const char* name_s = (ni->node_name_len > 0) ? (const char*)((const uint8_t*)ni + sizeof(struct NODEINFO)) : "";
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "--- Node #%d: id=%016llx name=\"%.*s\" ver=%u dirty=%u alien=%u%s ---",
|
||||
idx, (unsigned long long)ni->node_id, ni->node_name_len, name_s,
|
||||
ni->ver, nq->dirty, nq->alien, is_local ? " [LOCAL]" : "");
|
||||
|
||||
if (nq->best_socket) DEBUG_INFO(DEBUG_CATEGORY_BGP, " best_socket=%p", (void*)nq->best_socket);
|
||||
|
||||
log_dump(DEBUG_LEVEL_INFO, DEBUG_CATEGORY_BGP, " pubkey ", ni->public_key, SC_PUBKEY_SIZE);
|
||||
log_dump(DEBUG_LEVEL_INFO, DEBUG_CATEGORY_BGP, " ed25519", ni->ed25519_public_key, SC_PUBKEY_SIZE);
|
||||
|
||||
const struct NODEINFO_IPV4_SOCKET_META* v4sm;
|
||||
int v4sm_cnt = get_node_v4_sockets_meta(nq, &v4sm);
|
||||
if (v4sm_cnt > 0) for (int i = 0; i < v4sm_cnt; i++) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, " v4sock[%d]: id=%u config=%u nat=%u", i, v4sm[i].id, v4sm[i].config_type, v4sm[i].nat_type);
|
||||
}
|
||||
|
||||
const struct NODEINFO_IPV4_ADDR* v4a;
|
||||
int v4a_cnt = get_node_v4_addrs(nq, &v4a);
|
||||
if (v4a_cnt > 0) {
|
||||
const char* atn[] = {"INTERFACE","NAT","REAL"};
|
||||
for (int i = 0; i < v4a_cnt; i++) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, " v4addr[%d]: %s:%u %s sock=%u",
|
||||
i, ip_to_str(v4a[i].addr, AF_INET).str, v4a[i].port,
|
||||
(v4a[i].type<=2)?atn[v4a[i].type]:"?", v4a[i].socket_id);
|
||||
}
|
||||
}
|
||||
|
||||
const struct NODEINFO_IPV6_SOCKET_META* v6sm;
|
||||
int v6sm_cnt = get_node_v6_sockets_meta(nq, &v6sm);
|
||||
if (v6sm_cnt > 0) for (int i = 0; i < v6sm_cnt; i++) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, " v6sock[%d]: id=%u config=%u nat=%u", i, v6sm[i].id, v6sm[i].config_type, v6sm[i].nat_type);
|
||||
}
|
||||
|
||||
const struct NODEINFO_IPV6_ADDR* v6a;
|
||||
int v6a_cnt = get_node_v6_addrs(nq, &v6a);
|
||||
if (v6a_cnt > 0) {
|
||||
for (int i = 0; i < v6a_cnt; i++) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, " v6addr[%d]: %s:%u type=%u sock=%u",
|
||||
i, ip_to_str(v6a[i].addr, AF_INET6).str, v6a[i].port,
|
||||
v6a[i].type, v6a[i].socket_id);
|
||||
}
|
||||
}
|
||||
|
||||
const struct NODEINFO_IPV4_SUBNET* sub4;
|
||||
int sub4_cnt = get_node_routes(nq, &sub4);
|
||||
if (sub4_cnt > 0) for (int i = 0; i < sub4_cnt; i++) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, " v4sub[%d]: %s/%d", i,
|
||||
ip_to_str(sub4[i].addr, AF_INET).str, sub4[i].prefix_length);
|
||||
}
|
||||
|
||||
const struct NODEINFO_IPV6_SUBNET* sub6;
|
||||
int sub6_cnt = get_node_v6_routes(nq, &sub6);
|
||||
if (sub6_cnt > 0) for (int i = 0; i < sub6_cnt; i++) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, " v6sub[%d]: %s/%d", i,
|
||||
ip_to_str(sub6[i].addr, AF_INET6).str, sub6[i].prefix_length);
|
||||
}
|
||||
|
||||
if (ni->tranzit_nodes > 0) {
|
||||
const uint8_t* dyn = node_dyn_start(ni);
|
||||
dyn = skip_name(dyn, ni->node_name_len);
|
||||
dyn = skip_v4_sockets_meta(dyn, ni->local_v4_sockets);
|
||||
dyn = skip_v4_addrs(dyn, ni->local_v4_addrs);
|
||||
dyn = skip_v6_sockets_meta(dyn, ni->local_v6_sockets);
|
||||
dyn = skip_v6_addrs(dyn, ni->local_v6_addrs);
|
||||
dyn = skip_v4_subnets(dyn, ni->local_v4_subnets);
|
||||
const struct NODEINFO_IPV6_SUBNET* sub6_dyn = (const struct NODEINFO_IPV6_SUBNET*)dyn;
|
||||
dyn += ni->local_v6_subnets * sizeof(struct NODEINFO_IPV6_SUBNET);
|
||||
const struct NODEINFO_TRANZIT_NODE* tr = (const struct NODEINFO_TRANZIT_NODE*)dyn;
|
||||
for (uint8_t i = 0; i < ni->tranzit_nodes; i++) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, " tranzit[%d]: node=%016llx rtt=%u linkq=%u",
|
||||
i, (unsigned long long)tr[i].node_id, tr[i].rtt, tr[i].link_q);
|
||||
}
|
||||
}
|
||||
|
||||
int path_idx = 0;
|
||||
if (nq->paths) {
|
||||
struct ll_entry* pe = nq->paths->head;
|
||||
while (pe) {
|
||||
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)pe;
|
||||
uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH));
|
||||
char hop_buf[256] = "";
|
||||
int hop_pos = 0;
|
||||
for (uint8_t hi = 0; hi < path->hop_count && hop_pos < (int)sizeof(hop_buf)-20; hi++) {
|
||||
hop_pos += snprintf(hop_buf + hop_pos, sizeof(hop_buf) - hop_pos,
|
||||
"%s%016llx", hi ? "->" : "", (unsigned long long)hop[hi]);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, " path[%d]: conn=%s hop_count=%u hops=[%s]",
|
||||
path_idx, path->conn ? path->conn->log_name : "NULL",
|
||||
path->hop_count, hop_buf);
|
||||
path_idx++;
|
||||
pe = pe->next;
|
||||
}
|
||||
}
|
||||
|
||||
struct NODE_CONNECTIVITY* c = &nq->connectivity;
|
||||
if (is_node_connectivity_active(c)) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, " connectivity: probe=%s iface=%s(%u) nat=%s(%u) real=%s(%u) pend=%u iface_rtt=%u nat_rtt=%u real_rtt=%u start_tb=%llu ping_req_tb=%llu",
|
||||
probe_status_str(c->probe_status),
|
||||
probe_result_str(c->interface_status), c->interface_min_rtt,
|
||||
probe_result_str(c->nat_status), c->nat_min_rtt,
|
||||
probe_result_str(c->real_status), c->real_min_rtt,
|
||||
c->pending_count, c->interface_min_rtt, c->nat_min_rtt, c->real_min_rtt,
|
||||
(unsigned long long)c->probe_start_time, (unsigned long long)c->ping_req_time);
|
||||
}
|
||||
|
||||
if (nq->conn_mgr_type != CONN_TYPE_NONE) {
|
||||
char interm_buf[200] = "";
|
||||
int ipos = 0;
|
||||
for (uint8_t i = 0; i < nq->conn_mgr_intermediariy_count && ipos < (int)sizeof(interm_buf)-19; i++) {
|
||||
ipos += snprintf(interm_buf + ipos, sizeof(interm_buf) - ipos,
|
||||
"%s%016llx", i ? "," : "", (unsigned long long)nq->conn_mgr_intermediaries[i]);
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, " conn_mgr: type=%s intermediates=[%s]",
|
||||
conn_mgr_type_str(nq->conn_mgr_type), interm_buf);
|
||||
}
|
||||
|
||||
e = e->next;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
int route_node_format_all(struct ROUTE_BGP* bgp, char* buf, size_t buf_size) {
|
||||
if (!bgp || !bgp->nodes || !buf || buf_size == 0) return 0;
|
||||
int pos = 0;
|
||||
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0,
|
||||
"=== BGP Nodes dump: %d nodes ===\n", queue_entry_count(bgp->nodes));
|
||||
|
||||
int idx = 0;
|
||||
struct ll_entry* e = bgp->nodes->head;
|
||||
while (e && (size_t)pos < buf_size) {
|
||||
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e;
|
||||
struct NODEINFO* ni = &nq->node;
|
||||
int is_local = (bgp->local_node == nq);
|
||||
const char* name_s = (ni->node_name_len > 0) ? (const char*)((const uint8_t*)ni + sizeof(struct NODEINFO)) : "";
|
||||
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0,
|
||||
"--- Node #%d: id=%016llx name=\"%.*s\" ver=%u dirty=%u alien=%u%s ---\n",
|
||||
idx, (unsigned long long)ni->node_id, ni->node_name_len, name_s,
|
||||
ni->ver, nq->dirty, nq->alien, is_local ? " [LOCAL]" : "");
|
||||
|
||||
if (nq->best_socket)
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0,
|
||||
" best_socket=%p\n", (void*)nq->best_socket);
|
||||
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, " pubkey: ");
|
||||
for (int k = 0; k < SC_PUBKEY_SIZE; k++)
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, "%02x", ni->public_key[k]);
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, "\n");
|
||||
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, " ed25519: ");
|
||||
for (int k = 0; k < SC_PUBKEY_SIZE; k++)
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, "%02x", ni->ed25519_public_key[k]);
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, "\n");
|
||||
|
||||
#define FMT_ADD(fmt, ...) \
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, fmt, ##__VA_ARGS__)
|
||||
|
||||
const struct NODEINFO_IPV4_SOCKET_META* v4sm;
|
||||
int v4sm_cnt = get_node_v4_sockets_meta(nq, &v4sm);
|
||||
for (int i = 0; i < v4sm_cnt; i++)
|
||||
FMT_ADD(" v4sock[%d]: id=%u config=%u nat=%u\n", i, v4sm[i].id, v4sm[i].config_type, v4sm[i].nat_type);
|
||||
|
||||
const struct NODEINFO_IPV4_ADDR* v4a;
|
||||
int v4a_cnt = get_node_v4_addrs(nq, &v4a);
|
||||
if (v4a_cnt > 0) {
|
||||
const char* atn[] = {"INTERFACE","NAT","REAL"};
|
||||
for (int i = 0; i < v4a_cnt; i++)
|
||||
FMT_ADD(" v4addr[%d]: %s:%u %s sock=%u\n",
|
||||
i, ip_to_str(v4a[i].addr, AF_INET).str, v4a[i].port,
|
||||
(v4a[i].type<=2)?atn[v4a[i].type]:"?", v4a[i].socket_id);
|
||||
}
|
||||
|
||||
const struct NODEINFO_IPV6_SOCKET_META* v6sm;
|
||||
int v6sm_cnt = get_node_v6_sockets_meta(nq, &v6sm);
|
||||
for (int i = 0; i < v6sm_cnt; i++)
|
||||
FMT_ADD(" v6sock[%d]: id=%u config=%u nat=%u\n", i, v6sm[i].id, v6sm[i].config_type, v6sm[i].nat_type);
|
||||
|
||||
const struct NODEINFO_IPV6_ADDR* v6a;
|
||||
int v6a_cnt = get_node_v6_addrs(nq, &v6a);
|
||||
for (int i = 0; i < v6a_cnt; i++)
|
||||
FMT_ADD(" v6addr[%d]: %s:%u type=%u sock=%u\n",
|
||||
i, ip_to_str(v6a[i].addr, AF_INET6).str, v6a[i].port,
|
||||
v6a[i].type, v6a[i].socket_id);
|
||||
|
||||
const struct NODEINFO_IPV4_SUBNET* sub4;
|
||||
int sub4_cnt = get_node_routes(nq, &sub4);
|
||||
for (int i = 0; i < sub4_cnt; i++)
|
||||
FMT_ADD(" v4sub[%d]: %s/%d\n", i,
|
||||
ip_to_str(sub4[i].addr, AF_INET).str, sub4[i].prefix_length);
|
||||
|
||||
const struct NODEINFO_IPV6_SUBNET* sub6;
|
||||
int sub6_cnt = get_node_v6_routes(nq, &sub6);
|
||||
for (int i = 0; i < sub6_cnt; i++)
|
||||
FMT_ADD(" v6sub[%d]: %s/%d\n", i,
|
||||
ip_to_str(sub6[i].addr, AF_INET6).str, sub6[i].prefix_length);
|
||||
|
||||
if (ni->tranzit_nodes > 0) {
|
||||
const uint8_t* dyn = node_dyn_start(ni);
|
||||
dyn = skip_name(dyn, ni->node_name_len);
|
||||
dyn = skip_v4_sockets_meta(dyn, ni->local_v4_sockets);
|
||||
dyn = skip_v4_addrs(dyn, ni->local_v4_addrs);
|
||||
dyn = skip_v6_sockets_meta(dyn, ni->local_v6_sockets);
|
||||
dyn = skip_v6_addrs(dyn, ni->local_v6_addrs);
|
||||
dyn = skip_v4_subnets(dyn, ni->local_v4_subnets);
|
||||
dyn += ni->local_v6_subnets * sizeof(struct NODEINFO_IPV6_SUBNET);
|
||||
const struct NODEINFO_TRANZIT_NODE* tr = (const struct NODEINFO_TRANZIT_NODE*)dyn;
|
||||
for (uint8_t i = 0; i < ni->tranzit_nodes; i++)
|
||||
FMT_ADD(" tranzit[%d]: node=%016llx rtt=%u linkq=%u\n",
|
||||
i, (unsigned long long)tr[i].node_id, tr[i].rtt, tr[i].link_q);
|
||||
}
|
||||
|
||||
if (nq->paths) {
|
||||
int path_idx = 0;
|
||||
struct ll_entry* pe = nq->paths->head;
|
||||
while (pe) {
|
||||
struct NODEINFO_PATH* path = (struct NODEINFO_PATH*)pe;
|
||||
uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct NODEINFO_PATH));
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0,
|
||||
" path[%d]: conn=%s hop_count=%u hops=[",
|
||||
path_idx, path->conn ? path->conn->log_name : "NULL", path->hop_count);
|
||||
for (uint8_t hi = 0; hi < path->hop_count; hi++)
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0,
|
||||
"%s%016llx", hi ? "->" : "", (unsigned long long)hop[hi]);
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, "]\n");
|
||||
path_idx++;
|
||||
pe = pe->next;
|
||||
}
|
||||
}
|
||||
|
||||
struct NODE_CONNECTIVITY* c = &nq->connectivity;
|
||||
if (is_node_connectivity_active(c)) {
|
||||
FMT_ADD(" connectivity: probe=%s iface=%s(%u) nat=%s(%u) real=%s(%u) pend=%u iface_rtt=%u nat_rtt=%u real_rtt=%u start_tb=%llu ping_req_tb=%llu\n",
|
||||
probe_status_str(c->probe_status),
|
||||
probe_result_str(c->interface_status), c->interface_min_rtt,
|
||||
probe_result_str(c->nat_status), c->nat_min_rtt,
|
||||
probe_result_str(c->real_status), c->real_min_rtt,
|
||||
c->pending_count, c->interface_min_rtt, c->nat_min_rtt, c->real_min_rtt,
|
||||
(unsigned long long)c->probe_start_time, (unsigned long long)c->ping_req_time);
|
||||
}
|
||||
|
||||
if (nq->conn_mgr_type != CONN_TYPE_NONE) {
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0,
|
||||
" conn_mgr: type=%s intermediates=[",
|
||||
conn_mgr_type_str(nq->conn_mgr_type));
|
||||
for (uint8_t i = 0; i < nq->conn_mgr_intermediariy_count; i++)
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0,
|
||||
"%s%016llx", i ? "," : "",
|
||||
(unsigned long long)nq->conn_mgr_intermediaries[i]);
|
||||
pos += snprintf(buf + pos, buf_size > (size_t)pos ? buf_size - pos : 0, "]\n");
|
||||
}
|
||||
|
||||
#undef FMT_ADD
|
||||
e = e->next;
|
||||
idx++;
|
||||
}
|
||||
|
||||
if ((size_t)pos >= buf_size) {
|
||||
pos = (int)buf_size - 14;
|
||||
if (pos < 0) pos = 0;
|
||||
pos += snprintf(buf + pos, buf_size - pos, "\n[TRUNCATED]\n");
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
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->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; }
|
||||
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->type == CFG_SERVER_TYPE_UNKNOWN)
|
||||
&& 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) {
|
||||
if (instance->rt) route_delete(instance->rt, 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->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; }
|
||||
if (e_sock->local_addr.ss_family == AF_INET) {
|
||||
meta[midx].id = e_sock->sock_id;
|
||||
meta[midx].config_type = e_sock->type;
|
||||
meta[midx].nat_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->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; }
|
||||
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->type == CFG_SERVER_TYPE_UNKNOWN)
|
||||
&& 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->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; }
|
||||
if (e_sock->local_addr.ss_family == AF_INET6) {
|
||||
meta6[m6idx].id = e_sock->sock_id;
|
||||
meta6[m6idx].config_type = e_sock->type;
|
||||
meta6[m6idx].nat_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->type == CFG_SERVER_TYPE_PRIVATE || e_sock->type == CFG_SERVER_TYPE_LOCAL) { e_sock = e_sock->next; continue; }
|
||||
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);
|
||||
if (instance->rt) {
|
||||
if (!route_insert(instance->rt, bgp->local_node))
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_bgp_update_my_nodeinfo: failed to re-insert local routes after update");
|
||||
}
|
||||
} else {
|
||||
bgp->local_node->last_ver = bgp->local_node->node.ver;
|
||||
}
|
||||
return vc;
|
||||
}
|
||||
@@ -1,196 +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)
|
||||
void* probe_list; // linked list of active conn_probe_ctx (для cancel)
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 config_type; // CFG_SERVER_TYPE_PUBLIC/NAT/PRIVATE
|
||||
uint8_t nat_type; // NAT_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 config_type; // CFG_SERVER_TYPE_PUBLIC/NAT/PRIVATE
|
||||
uint8_t nat_type; // NAT_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);
|
||||
|
||||
void route_node_dump_all(struct ROUTE_BGP* bgp);
|
||||
int route_node_format_all(struct ROUTE_BGP* bgp, char* buf, size_t buf_size);
|
||||
|
||||
#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
|
||||
+16
-16
@@ -2,7 +2,7 @@
|
||||
#include "../lib/platform_compat.h"
|
||||
#include "routing.h"
|
||||
#include "route_lib.h"
|
||||
#include "route_node.h"
|
||||
#include "topo_node.h"
|
||||
#include "tun_if.h"
|
||||
#include "packet_dump.h"
|
||||
#include "etcp.h"
|
||||
@@ -138,13 +138,13 @@ void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, uint64_t
|
||||
return;
|
||||
}
|
||||
|
||||
struct NODEINFO_Q* nq = route->v_node_info;
|
||||
if (!nq || nq->node.hop_count == 0) {
|
||||
struct TOPO_NODEQ* nq = route->v_node_info;
|
||||
if (!nq || !nq->node || nq->hop_count == 0) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "Local route to %s", ip_to_str(&addr, AF_INET).str);
|
||||
} else {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "route_pkt: sending %zu bytes to node %016llx dst=%s",
|
||||
ip_len, (unsigned long long)nq->node.node_id, ip_to_str(&addr, AF_INET).str);
|
||||
int send_err = etcp_route_send(instance, nq->node.node_id, entry, 1);
|
||||
ip_len, (unsigned long long)nq->node->node_id, ip_to_str(&addr, AF_INET).str);
|
||||
int send_err = etcp_route_send(instance, nq->node->node_id, entry, 1);
|
||||
if (send_err != 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_ROUTING, "route_pkt: etcp_route_send failed: dst=%s err=%d",
|
||||
ip_to_str(&addr, AF_INET).str, send_err);
|
||||
@@ -180,7 +180,7 @@ void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, uint64_t
|
||||
static void routing_pkt_from_etcp_cb(struct ETCP_CONN* conn, struct ll_entry* pkt) {
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_ROUTING, "");
|
||||
if (!conn || !pkt) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_pkt_from_etcp_cb: invalid arguments: conn=%p pkt=%p",
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "invalid arguments: conn=%p pkt=%p",
|
||||
(void*)conn, (void*)pkt);
|
||||
if (pkt) {
|
||||
queue_entry_free(pkt);
|
||||
@@ -191,7 +191,7 @@ static void routing_pkt_from_etcp_cb(struct ETCP_CONN* conn, struct ll_entry* pk
|
||||
|
||||
struct UTUN_INSTANCE* instance = conn->instance;
|
||||
if (!instance) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_pkt_from_etcp_cb: connection has no instance: conn=%p pkt_len=%zu",
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "connection has no instance: conn=%p pkt_len=%zu",
|
||||
(void*)conn, pkt->len);
|
||||
queue_entry_free(pkt);
|
||||
queue_dgram_free(pkt);
|
||||
@@ -207,7 +207,7 @@ static void routing_pkt_from_etcp_cb(struct ETCP_CONN* conn, struct ll_entry* pk
|
||||
static void routing_pkt_from_tun_cb(struct ll_queue* q, void* arg) {
|
||||
struct UTUN_INSTANCE* instance = (struct UTUN_INSTANCE*)arg;
|
||||
if (!instance) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_pkt_from_tun_cb: invalid instance arg=%p", arg);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "invalid instance arg=%p", arg);
|
||||
queue_resume_callback(q);
|
||||
return;
|
||||
}
|
||||
@@ -227,14 +227,14 @@ static void routing_pkt_from_tun_cb(struct ll_queue* q, void* arg) {
|
||||
// Initialize routing module for instance (route table only, bind happens via routing_bind)
|
||||
int routing_create(struct UTUN_INSTANCE* instance) {
|
||||
if (!instance) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_create: instance is NULL");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "instance is NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create route table
|
||||
instance->rt = route_table_create();
|
||||
if (!instance->rt) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_create: failed to create route table for node %016llx",
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "failed to create route table for node %016llx",
|
||||
(unsigned long long)instance->node_id);
|
||||
return -1;
|
||||
}
|
||||
@@ -248,11 +248,11 @@ int routing_create(struct UTUN_INSTANCE* instance) {
|
||||
// Bind DATA handler via etcp_router (must be called after etcp_router_init)
|
||||
int routing_bind(struct UTUN_INSTANCE* instance) {
|
||||
if (!instance) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_bind: instance is NULL");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "instance is NULL");
|
||||
return -1;
|
||||
}
|
||||
if (etcp_router_bind(instance, ETCP_RT_ID_DATA, routing_pkt_from_etcp_cb) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_bind: failed to bind ETCP_RT_ID_DATA via etcp_router for node %016llx",
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "failed to bind ETCP_RT_ID_DATA via etcp_router for node %016llx",
|
||||
(unsigned long long)instance->node_id);
|
||||
return -1;
|
||||
}
|
||||
@@ -282,25 +282,25 @@ void routing_destroy(struct UTUN_INSTANCE* instance) {
|
||||
void routing_add_conn(struct ETCP_CONN* etcp) {
|
||||
(void)etcp;
|
||||
// No-op: connections are now handled via routing table entries
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "routing_add_conn: deprecated, no action taken");
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "deprecated, no action taken");
|
||||
}
|
||||
|
||||
// Unregister ETCP connection from routing (deprecated, kept for compatibility)
|
||||
void routing_del_conn(struct ETCP_CONN* etcp) {
|
||||
(void)etcp;
|
||||
// No-op: connections are now handled via routing table entries
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "routing_del_conn: deprecated, no action taken");
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "deprecated, no action taken");
|
||||
}
|
||||
|
||||
// Set TUN interface for routing
|
||||
void routing_set_tun(struct UTUN_INSTANCE* instance) {
|
||||
if (!instance) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_set_tun: instance is NULL");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "instance is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!instance->tun) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "routing_set_tun: instance %016llx has no TUN",
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "instance %016llx has no TUN",
|
||||
(unsigned long long)instance->node_id);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef ROUTING_H
|
||||
#define ROUTING_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -56,4 +61,8 @@ void routing_set_tun(struct UTUN_INSTANCE* instance);
|
||||
*/
|
||||
void route_pkt(struct UTUN_INSTANCE* instance, struct ll_entry* entry, uint64_t src_node_id);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // ROUTING_H
|
||||
+56
-56
@@ -129,7 +129,7 @@ static void sc_stream_derive_nonce(const uint8_t *session_key, uint32_t stream_i
|
||||
static int sc_validate_key(const uint8_t *public_key) {
|
||||
uint8_t zero[SC_PUBKEY_SIZE] = {0};
|
||||
if (memcmp(public_key, zero, SC_PUBKEY_SIZE) == 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_validate_key: all-zero public key");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "all-zero public key");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -151,35 +151,35 @@ sc_status_t sc_generate_keypair(struct SC_MYKEYS *pk) {
|
||||
|| priv_len != SC_PRIVKEY_SIZE
|
||||
|| EVP_PKEY_get_raw_public_key(pkey, pk->public_key, &pub_len) <= 0
|
||||
|| pub_len != SC_PUBKEY_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_generate_keypair: failed to extract raw keys");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "failed to extract raw keys");
|
||||
EVP_PKEY_free(pkey);
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
EVP_PKEY_free(pkey);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_generate_keypair: generated valid X25519 keypair");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "generated valid X25519 keypair");
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_key, const char *private_key) {
|
||||
if (!mykeys || !public_key || !private_key) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: invalid arguments");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid arguments");
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: public_key len=%zu, private_key len=%zu",
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "public_key len=%zu, private_key len=%zu",
|
||||
strlen(public_key), strlen(private_key));
|
||||
if (hex_to_binary(public_key, mykeys->public_key, SC_PUBKEY_SIZE)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: failed to convert public key from hex");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "failed to convert public key from hex");
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
if (hex_to_binary(private_key, mykeys->private_key, SC_PRIVKEY_SIZE)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: failed to convert private key from hex");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "failed to convert private key from hex");
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
if (sc_validate_key(mykeys->public_key) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: public key validation failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "public key validation failed");
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_init_local_keys: keys initialized successfully");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "keys initialized successfully");
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
@@ -187,32 +187,32 @@ sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const uint8_t *peer_public
|
||||
uint8_t peer_public_key[SC_PUBKEY_SIZE];
|
||||
if (mode) {
|
||||
if (hex_to_binary((const char*)peer_public_key_h, peer_public_key, SC_PUBKEY_SIZE)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid hex key format");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid hex key format");
|
||||
return SC_ERR_INVALID_ARG;
|
||||
}
|
||||
} else {
|
||||
memcpy(peer_public_key, peer_public_key_h, SC_PUBKEY_SIZE);
|
||||
}
|
||||
if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid ctx"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->initialized) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: ctx not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (sc_validate_key(peer_public_key) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: invalid key"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->pk) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: no private key"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid ctx"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->initialized) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "ctx not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (sc_validate_key(peer_public_key) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid key"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->pk) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "no private key"); return SC_ERR_NOT_INITIALIZED; }
|
||||
|
||||
EVP_PKEY *my_pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, ctx->pk->private_key, SC_PRIVKEY_SIZE);
|
||||
if (!my_pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: EVP_PKEY_new_raw_private_key failed"); return SC_ERR_CRYPTO; }
|
||||
if (!my_pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_PKEY_new_raw_private_key failed"); return SC_ERR_CRYPTO; }
|
||||
EVP_PKEY *peer_pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, peer_public_key, SC_PUBKEY_SIZE);
|
||||
if (!peer_pkey) { EVP_PKEY_free(my_pkey); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: EVP_PKEY_new_raw_public_key failed"); return SC_ERR_CRYPTO; }
|
||||
if (!peer_pkey) { EVP_PKEY_free(my_pkey); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_PKEY_new_raw_public_key failed"); return SC_ERR_CRYPTO; }
|
||||
|
||||
EVP_PKEY_CTX *derive_ctx = EVP_PKEY_CTX_new(my_pkey, NULL);
|
||||
if (!derive_ctx) { EVP_PKEY_free(my_pkey); EVP_PKEY_free(peer_pkey); return SC_ERR_CRYPTO; }
|
||||
if (EVP_PKEY_derive_init(derive_ctx) <= 0) {
|
||||
EVP_PKEY_CTX_free(derive_ctx); EVP_PKEY_free(my_pkey); EVP_PKEY_free(peer_pkey);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: EVP_PKEY_derive_init failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_PKEY_derive_init failed");
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
if (EVP_PKEY_derive_set_peer(derive_ctx, peer_pkey) <= 0) {
|
||||
EVP_PKEY_CTX_free(derive_ctx); EVP_PKEY_free(my_pkey); EVP_PKEY_free(peer_pkey);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: EVP_PKEY_derive_set_peer failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_PKEY_derive_set_peer failed");
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const uint8_t *peer_public
|
||||
size_t secret_len = SC_SHARED_SECRET_SIZE;
|
||||
if (EVP_PKEY_derive(derive_ctx, shared_secret, &secret_len) <= 0 || secret_len != SC_SHARED_SECRET_SIZE) {
|
||||
EVP_PKEY_CTX_free(derive_ctx); EVP_PKEY_free(my_pkey); EVP_PKEY_free(peer_pkey);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: X25519 derive failed secret_len=%zu", secret_len);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "X25519 derive failed secret_len=%zu", secret_len);
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const uint8_t *peer_public
|
||||
EVP_PKEY_CTX_free(derive_ctx);
|
||||
EVP_PKEY_free(my_pkey);
|
||||
EVP_PKEY_free(peer_pkey);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_set_peer_public_key: X25519 key exchange complete");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "X25519 key exchange complete");
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
@@ -328,10 +328,10 @@ sc_status_t sc_decrypt(sc_context_t *ctx, const uint8_t *ciphertext, size_t ciph
|
||||
sc_status_t sc_compute_public_key_from_private(const uint8_t *private_key, uint8_t *public_key) {
|
||||
if (!private_key || !public_key) return SC_ERR_INVALID_ARG;
|
||||
EVP_PKEY *pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, private_key, SC_PRIVKEY_SIZE);
|
||||
if (!pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_compute_public_key_from_private: EVP_PKEY_new_raw_private_key failed"); return SC_ERR_CRYPTO; }
|
||||
if (!pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_PKEY_new_raw_private_key failed"); return SC_ERR_CRYPTO; }
|
||||
size_t pub_len = SC_PUBKEY_SIZE;
|
||||
if (EVP_PKEY_get_raw_public_key(pkey, public_key, &pub_len) <= 0 || pub_len != SC_PUBKEY_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_compute_public_key_from_private: EVP_PKEY_get_raw_public_key failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_PKEY_get_raw_public_key failed");
|
||||
EVP_PKEY_free(pkey); return SC_ERR_CRYPTO;
|
||||
}
|
||||
EVP_PKEY_free(pkey);
|
||||
@@ -341,34 +341,34 @@ sc_status_t sc_compute_public_key_from_private(const uint8_t *private_key, uint8
|
||||
// --- Streaming cipher (AES-128-CTR) ---
|
||||
|
||||
sc_status_t sc_stream_init(sc_context_t *ctx, struct sc_stream_state *state, uint32_t stream_id) {
|
||||
if (!ctx || !state) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->session_ready) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: session not ready"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (!ctx || !state) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->session_ready) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "session not ready"); return SC_ERR_NOT_INITIALIZED; }
|
||||
uint8_t nonce[SC_STREAM_NONCE_SIZE];
|
||||
sc_stream_derive_nonce(ctx->session_key, stream_id, nonce);
|
||||
uint8_t iv[16];
|
||||
memcpy(iv, nonce, SC_STREAM_NONCE_SIZE);
|
||||
memset(iv + SC_STREAM_NONCE_SIZE, 0, 4);
|
||||
EVP_CIPHER_CTX *ectx = EVP_CIPHER_CTX_new();
|
||||
if (!ectx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: EVP_CIPHER_CTX_new failed"); return SC_ERR_CRYPTO; }
|
||||
if (!ectx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_CIPHER_CTX_new failed"); return SC_ERR_CRYPTO; }
|
||||
if (EVP_EncryptInit_ex(ectx, EVP_aes_128_ctr(), NULL, ctx->session_key, iv) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: EVP_EncryptInit_ex failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_EncryptInit_ex failed");
|
||||
EVP_CIPHER_CTX_free(ectx); return SC_ERR_CRYPTO;
|
||||
}
|
||||
state->ectx = ectx;
|
||||
state->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_stream_init: stream_id=%u nonce=%02x%02x%02x%02x...",
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "stream_id=%u nonce=%02x%02x%02x%02x...",
|
||||
stream_id, nonce[0], nonce[1], nonce[2], nonce[3]);
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_xor(struct sc_stream_state *state, uint8_t *data, size_t data_len) {
|
||||
if (data_len == 0) return SC_OK;
|
||||
if (!state || !data) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_xor: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_xor: not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (!state || !data) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
int outlen;
|
||||
if (EVP_EncryptUpdate((EVP_CIPHER_CTX *)state->ectx, data, &outlen, data, (int)data_len) != 1
|
||||
|| (size_t)outlen != data_len) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_xor: EVP_EncryptUpdate failed outlen=%d datalen=%zu", outlen, data_len);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_EncryptUpdate failed outlen=%d datalen=%zu", outlen, data_len);
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
return SC_OK;
|
||||
@@ -390,17 +390,17 @@ static int sc_derive_ed25519_key(const uint8_t *x25519_privkey, EVP_PKEY **pkey_
|
||||
uint8_t hash[SHA512_DIGEST_LENGTH];
|
||||
SHA512(x25519_privkey, SC_PRIVKEY_SIZE, hash);
|
||||
*pkey_out = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, hash, SC_PRIVKEY_SIZE);
|
||||
if (!*pkey_out) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_derive_ed25519_key: EVP_PKEY_new_raw_private_key failed"); return SC_ERR_CRYPTO; }
|
||||
if (!*pkey_out) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_PKEY_new_raw_private_key failed"); return SC_ERR_CRYPTO; }
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_derive_ed25519_pubkey(const uint8_t *x25519_privkey, uint8_t *ed25519_pubkey_out) {
|
||||
if (!x25519_privkey || !ed25519_pubkey_out) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_derive_ed25519_pubkey: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!x25519_privkey || !ed25519_pubkey_out) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
EVP_PKEY *pkey = NULL;
|
||||
if (sc_derive_ed25519_key(x25519_privkey, &pkey) != SC_OK) return SC_ERR_CRYPTO;
|
||||
size_t pub_len = SC_PUBKEY_SIZE;
|
||||
if (EVP_PKEY_get_raw_public_key(pkey, ed25519_pubkey_out, &pub_len) <= 0 || pub_len != SC_PUBKEY_SIZE) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_derive_ed25519_pubkey: EVP_PKEY_get_raw_public_key failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_PKEY_get_raw_public_key failed");
|
||||
EVP_PKEY_free(pkey); return SC_ERR_CRYPTO;
|
||||
}
|
||||
EVP_PKEY_free(pkey);
|
||||
@@ -408,65 +408,65 @@ sc_status_t sc_derive_ed25519_pubkey(const uint8_t *x25519_privkey, uint8_t *ed2
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_sign_init(sc_context_t *ctx, struct sc_stream_sign_state *state) {
|
||||
if (!ctx || !state) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_init: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->initialized || !ctx->pk) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_init: ctx not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (!ctx || !state) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!ctx->initialized || !ctx->pk) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "ctx not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
memset(state, 0, sizeof(*state));
|
||||
if (sc_derive_ed25519_key(ctx->pk->private_key, (EVP_PKEY**)&state->pkey) != SC_OK) return SC_ERR_CRYPTO;
|
||||
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
|
||||
if (!md_ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_init: EVP_MD_CTX_new failed"); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO; }
|
||||
if (!md_ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_MD_CTX_new failed"); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO; }
|
||||
if (EVP_DigestInit_ex(md_ctx, EVP_sha512(), NULL) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_init: EVP_DigestInit_ex failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_DigestInit_ex failed");
|
||||
EVP_MD_CTX_free(md_ctx); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO;
|
||||
}
|
||||
state->md_ctx = md_ctx;
|
||||
state->is_sign = 1;
|
||||
state->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_init: ok");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "ok");
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_sign_verify_init(struct sc_stream_sign_state *state, const uint8_t *ed25519_pubkey) {
|
||||
if (!state || !ed25519_pubkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify_init: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state || !ed25519_pubkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
memset(state, 0, sizeof(*state));
|
||||
state->pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, ed25519_pubkey, SC_PUBKEY_SIZE);
|
||||
if (!state->pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify_init: EVP_PKEY_new_raw_public_key failed"); return SC_ERR_CRYPTO; }
|
||||
if (!state->pkey) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_PKEY_new_raw_public_key failed"); return SC_ERR_CRYPTO; }
|
||||
EVP_MD_CTX *md_ctx = EVP_MD_CTX_new();
|
||||
if (!md_ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify_init: EVP_MD_CTX_new failed"); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO; }
|
||||
if (!md_ctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_MD_CTX_new failed"); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO; }
|
||||
if (EVP_DigestInit_ex(md_ctx, EVP_sha512(), NULL) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify_init: EVP_DigestInit_ex failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_DigestInit_ex failed");
|
||||
EVP_MD_CTX_free(md_ctx); EVP_PKEY_free((EVP_PKEY*)state->pkey); return SC_ERR_CRYPTO;
|
||||
}
|
||||
state->md_ctx = md_ctx;
|
||||
state->is_sign = 0;
|
||||
state->initialized = 1;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify_init: ok");
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CRYPTO, "ok");
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_sign_update(struct sc_stream_sign_state *state, const uint8_t *data, size_t len) {
|
||||
if (len == 0) return SC_OK;
|
||||
if (!state || !data) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_update: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_update: not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (!state || !data) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "not initialized"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (EVP_DigestUpdate((EVP_MD_CTX *)state->md_ctx, data, len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_update: EVP_DigestUpdate failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_DigestUpdate failed");
|
||||
return SC_ERR_CRYPTO;
|
||||
}
|
||||
return SC_OK;
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_sign_final(struct sc_stream_sign_state *state, uint8_t *sig_out, size_t *sig_len) {
|
||||
if (!state || !sig_out || !sig_len) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_final: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized || !state->is_sign) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_final: not initialized for sign"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (!state || !sig_out || !sig_len) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized || !state->is_sign) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "not initialized for sign"); return SC_ERR_NOT_INITIALIZED; }
|
||||
uint8_t hash[EVP_MAX_MD_SIZE]; unsigned hash_len = sizeof(hash);
|
||||
if (EVP_DigestFinal_ex((EVP_MD_CTX *)state->md_ctx, hash, &hash_len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_final: EVP_DigestFinal_ex failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_DigestFinal_ex failed");
|
||||
sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO;
|
||||
}
|
||||
EVP_MD_CTX *sctx = EVP_MD_CTX_new();
|
||||
if (!sctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_final: EVP_MD_CTX_new failed"); sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO; }
|
||||
if (!sctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_MD_CTX_new failed"); sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO; }
|
||||
if (EVP_DigestSignInit(sctx, NULL, NULL, NULL, (EVP_PKEY*)state->pkey) != 1
|
||||
|| EVP_DigestSign(sctx, sig_out, sig_len, hash, hash_len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_final: EVP_DigestSign failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_DigestSign failed");
|
||||
EVP_MD_CTX_free(sctx); sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO;
|
||||
}
|
||||
EVP_MD_CTX_free(sctx);
|
||||
@@ -475,18 +475,18 @@ sc_status_t sc_stream_sign_final(struct sc_stream_sign_state *state, uint8_t *si
|
||||
}
|
||||
|
||||
sc_status_t sc_stream_sign_verify(struct sc_stream_sign_state *state, const uint8_t *sig, size_t sig_len) {
|
||||
if (!state || !sig) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify: invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized || state->is_sign) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify: not initialized for verify"); return SC_ERR_NOT_INITIALIZED; }
|
||||
if (!state || !sig) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "invalid args"); return SC_ERR_INVALID_ARG; }
|
||||
if (!state->initialized || state->is_sign) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "not initialized for verify"); return SC_ERR_NOT_INITIALIZED; }
|
||||
uint8_t hash[EVP_MAX_MD_SIZE]; unsigned hash_len = sizeof(hash);
|
||||
if (EVP_DigestFinal_ex((EVP_MD_CTX *)state->md_ctx, hash, &hash_len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify: EVP_DigestFinal_ex failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_DigestFinal_ex failed");
|
||||
sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO;
|
||||
}
|
||||
EVP_MD_CTX *vctx = EVP_MD_CTX_new();
|
||||
if (!vctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify: EVP_MD_CTX_new failed"); sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO; }
|
||||
if (!vctx) { DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_MD_CTX_new failed"); sc_stream_sign_cleanup(state); return SC_ERR_CRYPTO; }
|
||||
if (EVP_DigestVerifyInit(vctx, NULL, NULL, NULL, (EVP_PKEY*)state->pkey) != 1
|
||||
|| EVP_DigestVerify(vctx, sig, sig_len, hash, hash_len) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "sc_stream_sign_verify: EVP_DigestVerify failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "EVP_DigestVerify failed");
|
||||
EVP_MD_CTX_free(vctx); sc_stream_sign_cleanup(state); return SC_ERR_AUTH_FAILED;
|
||||
}
|
||||
EVP_MD_CTX_free(vctx);
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef SECURE_CHANNEL_H
|
||||
#define SECURE_CHANNEL_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -106,4 +111,8 @@ void sc_stream_sign_cleanup(struct sc_stream_sign_state *state);
|
||||
|
||||
sc_status_t sc_derive_ed25519_pubkey(const uint8_t *x25519_privkey, uint8_t *ed25519_pubkey_out);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // SECURE_CHANNEL_H
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef STCP_H
|
||||
#define STCP_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include "secure_channel.h"
|
||||
#include "crc32.h"
|
||||
#include "../lib/u_async.h"
|
||||
@@ -77,4 +82,8 @@ void stcp_conn_set_tx_queue(struct stcp_conn *c, struct ll_queue *q);
|
||||
void stcp_conn_set_rx_queue(struct stcp_conn *c, struct ll_queue *q);
|
||||
void stcp_conn_set_on_close(struct stcp_conn *c, void (*cb)(struct stcp_conn *conn, int err, void *arg), void *arg);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+7
-6
@@ -44,7 +44,7 @@ static int encrypt_and_crc(struct stcp_conn *c, const uint8_t *data, size_t data
|
||||
output[2 + data_len + 2] = (uint8_t)(crc >> 16);
|
||||
output[2 + data_len + 3] = (uint8_t)(crc >> 24);
|
||||
if (sc_stream_xor(&c->stream_send, output + 2, data_len + 4) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "encrypt_and_crc: stream_xor failed len=%zu", data_len);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stream_xor failed len=%zu", data_len);
|
||||
return -1;
|
||||
}
|
||||
*output_len = 2 + data_len + 4;
|
||||
@@ -52,12 +52,12 @@ static int encrypt_and_crc(struct stcp_conn *c, const uint8_t *data, size_t data
|
||||
}
|
||||
|
||||
static int decrypt_and_check(uint8_t *data, size_t len, struct sc_stream_state *stream, size_t *out_len) {
|
||||
if (sc_stream_xor(stream, data, len) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "decrypt_and_check: stream_xor failed"); return -1; }
|
||||
if (sc_stream_xor(stream, data, len) != SC_OK) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stream_xor failed"); return -1; }
|
||||
size_t data_len = len - SC_CRC32_SIZE;
|
||||
uint32_t recv_crc = ((uint32_t)data[data_len]) | ((uint32_t)data[data_len + 1] << 8) |
|
||||
((uint32_t)data[data_len + 2] << 16) | ((uint32_t)data[data_len + 3] << 24);
|
||||
uint32_t calc_crc = crc32_calc(data, data_len);
|
||||
if (recv_crc != calc_crc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "decrypt_and_check: CRC mismatch"); return -1; }
|
||||
if (recv_crc != calc_crc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "CRC mismatch"); return -1; }
|
||||
*out_len = data_len;
|
||||
return 0;
|
||||
}
|
||||
@@ -79,7 +79,7 @@ static int client_try_send(struct stcp_conn *c, const uint8_t *data, size_t len)
|
||||
if (sent < 0) {
|
||||
int e = socket_get_error();
|
||||
if (e == ERR_AGAIN || e == ERR_WOULDBLOCK) { c->send_buf = (uint8_t *)data; c->send_len = len; c->send_offset = 0; uasync_set_socket_write(c->ua, c->socket_id, 1); return 0; }
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "client_try_send: send failed err=%d", e);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "send failed err=%d", e);
|
||||
u_free((uint8_t *)data); client_do_close(c, e); return -1;
|
||||
}
|
||||
if ((size_t)sent < len) { c->send_buf = (uint8_t *)data; c->send_len = len; c->send_offset = (size_t)sent; uasync_set_socket_write(c->ua, c->socket_id, 1); return 0; }
|
||||
@@ -110,6 +110,7 @@ static void client_tx_queue_cb(struct ll_queue *q, void *arg) {
|
||||
if (buf && !encrypt_and_crc(c, e->dgram, e->len, buf, &need)) client_try_send(c, buf, need);
|
||||
else if (buf) u_free(buf);
|
||||
}
|
||||
queue_dgram_free(e);
|
||||
queue_entry_free(e);
|
||||
queue_resume_callback(q);
|
||||
}
|
||||
@@ -245,9 +246,9 @@ static void client_connect_write_cb(socket_t sock, void *arg) {
|
||||
struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uint16_t port,
|
||||
struct SC_MYKEYS *keys, const uint8_t *peer_pubkey,
|
||||
stcp_ready_cb ready_cb, void *arg) {
|
||||
if (!ua || !addr || !keys || !peer_pubkey || !ready_cb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_client_connect: invalid args"); return NULL; }
|
||||
if (!ua || !addr || !keys || !peer_pubkey || !ready_cb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "invalid args"); return NULL; }
|
||||
struct stcp_client *cli = u_calloc(1, sizeof(struct stcp_client));
|
||||
if (!cli) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_client_connect: calloc failed"); return NULL; }
|
||||
if (!cli) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "calloc failed"); return NULL; }
|
||||
cli->ua = ua; cli->ready_cb = ready_cb; cli->ready_arg = arg;
|
||||
memcpy(cli->peer_pubkey, peer_pubkey, SC_PUBKEY_SIZE);
|
||||
struct stcp_conn *c = &cli->conn;
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef STCP_CLIENT_H
|
||||
#define STCP_CLIENT_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include "stcp.h"
|
||||
|
||||
typedef void (*stcp_ready_cb)(struct stcp_conn *conn, void *arg);
|
||||
@@ -12,4 +17,8 @@ struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uin
|
||||
void stcp_client_destroy(struct stcp_client *cli);
|
||||
struct stcp_conn *stcp_client_get_conn(struct stcp_client *cli);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+25
-3
@@ -5,6 +5,7 @@
|
||||
#include "stcp_client.h"
|
||||
#include "secure_channel.h"
|
||||
#include "etcp.h"
|
||||
#include "etcp_router.h"
|
||||
#include "utun_instance.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/ll_queue.h"
|
||||
@@ -73,6 +74,9 @@ static void server_accept_cb(struct stcp_conn *conn, void *arg) {
|
||||
queue_set_callback(rx, link_rx_cb, link);
|
||||
stcp_conn_set_rx_queue(conn, rx);
|
||||
link->tx_queue = queue_new(conn->ua, 0, 0, 0, "stx");
|
||||
queue_set_threshold(link->tx_queue, 0, 0);
|
||||
queue_set_waiter_defer(link->tx_queue, 1);
|
||||
link->etcp_conn.send_input_q = link->tx_queue;
|
||||
stcp_conn_set_tx_queue(conn, link->tx_queue);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_link: server accepted connection");
|
||||
@@ -94,6 +98,9 @@ static void client_ready_cb(struct stcp_conn *conn, void *arg) {
|
||||
queue_set_callback(rx, link_rx_cb, link);
|
||||
stcp_conn_set_rx_queue(conn, rx);
|
||||
link->tx_queue = queue_new(conn->ua, 0, 0, 0, "ctx");
|
||||
queue_set_threshold(link->tx_queue, 0, 0);
|
||||
queue_set_waiter_defer(link->tx_queue, 1);
|
||||
link->etcp_conn.send_input_q = link->tx_queue;
|
||||
stcp_conn_set_tx_queue(conn, link->tx_queue);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_link: client connected");
|
||||
@@ -112,7 +119,7 @@ struct stcp_server *stcp_server_listen(struct stcp_link_config *cfg, uint16_t po
|
||||
ss->on_link_arg = arg;
|
||||
ss->srv = stcp_server_create(cfg->ua, port, cfg->my_keys, server_accept_cb, ss);
|
||||
if (!ss->srv) { u_free(ss); return NULL; }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_server_listen: port=%u", port);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "port=%u", port);
|
||||
return ss;
|
||||
}
|
||||
|
||||
@@ -140,7 +147,7 @@ struct stcp_link *stcp_link_connect(struct stcp_link_config *cfg) {
|
||||
struct secure_channel sc_tmp;
|
||||
sc_init_ctx(&sc_tmp, cfg->my_keys);
|
||||
if (sc_set_peer_public_key(&sc_tmp, cfg->peer_pubkey, 1) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_link_connect: invalid peer pubkey hex");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "invalid peer pubkey hex");
|
||||
u_free(link); return NULL;
|
||||
}
|
||||
memcpy(link->peer_pubkey, sc_tmp.peer_public_key, SC_PUBKEY_SIZE);
|
||||
@@ -154,12 +161,27 @@ struct stcp_link *stcp_link_connect(struct stcp_link_config *cfg) {
|
||||
|
||||
link->etcp_conn.instance = cfg->inst;
|
||||
link->etcp_conn.transport_link = (void *)link;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_link_connect: connecting to %s:%u", addr_str, port);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "connecting to %s:%u", addr_str, port);
|
||||
return link;
|
||||
}
|
||||
|
||||
void stcp_link_close(struct stcp_link *link) {
|
||||
if (!link) return;
|
||||
struct ll_queue* tq_reg = link->etcp_conn.transit_queues;
|
||||
if (tq_reg) {
|
||||
struct ll_entry* te;
|
||||
while ((te = queue_data_get(tq_reg)) != NULL) {
|
||||
struct TRANSIT_QUEUE* tq = (struct TRANSIT_QUEUE*)te;
|
||||
if (link->etcp_conn.send_input_q) queue_waiter_cancel(link->etcp_conn.send_input_q, &tq->waiter);
|
||||
struct ll_entry* pe;
|
||||
while ((pe = queue_data_get(tq->q)) != NULL) { queue_dgram_free(pe); queue_entry_free(pe); }
|
||||
queue_free(tq->q);
|
||||
queue_entry_free(&tq->ll);
|
||||
}
|
||||
queue_free(tq_reg);
|
||||
link->etcp_conn.transit_queues = NULL;
|
||||
link->etcp_conn.send_input_q = NULL;
|
||||
}
|
||||
if (link->conn) {
|
||||
if (link->conn->rx_queue) queue_free(link->conn->rx_queue);
|
||||
if (link->tx_queue) queue_free(link->tx_queue);
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef STCP_LINK_H
|
||||
#define STCP_LINK_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include "secure_channel.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include "../lib/socket_compat.h"
|
||||
@@ -45,4 +50,8 @@ typedef void (*stcp_link_cb)(struct stcp_link *link, void *arg);
|
||||
void stcp_link_set_on_ready(struct stcp_link *link, stcp_link_cb cb, void *arg);
|
||||
void stcp_link_set_on_close(struct stcp_link *link, void (*cb)(struct stcp_link *link, int err, void *arg), void *arg);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+27
-26
@@ -47,7 +47,7 @@ static int stcp_encrypt_and_crc(struct stcp_conn *c, const uint8_t *data, size_t
|
||||
output[2 + data_len + 3] = (uint8_t)(crc >> 24);
|
||||
size_t enc_len = 2 + data_len + 4;
|
||||
if (sc_stream_xor(&c->stream_send, output + 2, data_len + 4) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_encrypt_and_crc: stream_xor failed len=%zu", data_len);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stream_xor failed len=%zu", data_len);
|
||||
return -1;
|
||||
}
|
||||
*output_len = enc_len;
|
||||
@@ -57,7 +57,7 @@ static int stcp_encrypt_and_crc(struct stcp_conn *c, const uint8_t *data, size_t
|
||||
static int stcp_decrypt_and_check(uint8_t *plaintext, size_t plaintext_len,
|
||||
struct sc_stream_state *stream, size_t *out_len) {
|
||||
if (sc_stream_xor(stream, plaintext, plaintext_len) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_decrypt_and_check: stream_xor failed len=%zu", plaintext_len);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stream_xor failed len=%zu", plaintext_len);
|
||||
return -1;
|
||||
}
|
||||
size_t data_len = plaintext_len - 4;
|
||||
@@ -65,7 +65,7 @@ static int stcp_decrypt_and_check(uint8_t *plaintext, size_t plaintext_len,
|
||||
((uint32_t)plaintext[data_len + 2] << 16) | ((uint32_t)plaintext[data_len + 3] << 24);
|
||||
uint32_t calc_crc = crc32_calc(plaintext, data_len);
|
||||
if (recv_crc != calc_crc) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_decrypt_and_check: CRC mismatch recv=%08x calc=%08x", recv_crc, calc_crc);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "CRC mismatch recv=%08x calc=%08x", recv_crc, calc_crc);
|
||||
return -1;
|
||||
}
|
||||
*out_len = data_len;
|
||||
@@ -90,12 +90,12 @@ static void stcp_conn_do_close(struct stcp_conn *c, int err) {
|
||||
}
|
||||
|
||||
static void stcp_conn_send_message(struct stcp_conn *c, const uint8_t *data, size_t len) {
|
||||
if (c->state != STCP_STATE_DATA) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_conn_send_message: not in DATA state"); return; }
|
||||
if (c->state != STCP_STATE_DATA) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "not in DATA state"); return; }
|
||||
size_t max_enc = STCP_MAX_MSG_SIZE;
|
||||
if (len > max_enc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_conn_send_message: data too long %zu", len); return; }
|
||||
if (len > max_enc) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "data too long %zu", len); return; }
|
||||
size_t need = 2 + len + 4;
|
||||
uint8_t *buf = u_malloc(need);
|
||||
if (!buf) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_conn_send_message: malloc(%zu) failed", need); return; }
|
||||
if (!buf) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "malloc(%zu) failed", need); return; }
|
||||
if (stcp_encrypt_and_crc(c, data, len, buf, &need)) { u_free(buf); return; }
|
||||
stcp_conn_try_send(c, buf, need);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ static void stcp_conn_send_message(struct stcp_conn *c, const uint8_t *data, siz
|
||||
static int stcp_conn_try_send(struct stcp_conn *c, const uint8_t *data, size_t len) {
|
||||
if (c->sock == SOCKET_INVALID) return -1;
|
||||
if (c->send_buf) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_conn_try_send: send_buf already busy");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "send_buf already busy");
|
||||
return -1;
|
||||
}
|
||||
ssize_t sent = send(c->sock, data, len, 0);
|
||||
@@ -116,7 +116,7 @@ static int stcp_conn_try_send(struct stcp_conn *c, const uint8_t *data, size_t l
|
||||
uasync_set_socket_write(c->ua, c->socket_id, 1);
|
||||
return 0;
|
||||
}
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_conn_try_send: send failed err=%d", err);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "send failed err=%d", err);
|
||||
u_free((uint8_t *)data);
|
||||
stcp_conn_do_close(c, err);
|
||||
return -1;
|
||||
@@ -139,7 +139,7 @@ static void server_conn_write_cb(socket_t sock, void *arg) {
|
||||
if (sent < 0) {
|
||||
int err = socket_get_error();
|
||||
if (err == ERR_AGAIN || err == ERR_WOULDBLOCK) return;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "server_conn_write_cb: send failed err=%d", err);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "send failed err=%d", err);
|
||||
stcp_conn_do_close(c, err);
|
||||
return;
|
||||
}
|
||||
@@ -156,6 +156,7 @@ static void tx_queue_cb(struct ll_queue *q, void *arg) {
|
||||
struct ll_entry *e = queue_data_get(q);
|
||||
if (!e) { queue_resume_callback(q); return; }
|
||||
if (e->dgram) stcp_conn_send_message(c, e->dgram, e->len);
|
||||
queue_dgram_free(e);
|
||||
queue_entry_free(e);
|
||||
queue_resume_callback(q);
|
||||
}
|
||||
@@ -164,16 +165,16 @@ static int derive_session_and_streams(struct stcp_conn *c, const uint8_t *peer_p
|
||||
struct secure_channel sc;
|
||||
sc_init_ctx(&sc, &c->my_keys);
|
||||
if (sc_set_peer_public_key(&sc, peer_pubkey, SC_PEER_PUBKEY_BIN) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "derive_session: ECDH failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "ECDH failed");
|
||||
return -1;
|
||||
}
|
||||
memcpy(c->session_key, sc.session_key, SC_SESSION_KEY_SIZE);
|
||||
if (sc_stream_init(&sc, &c->stream_send, STCP_STREAM_SERVER_SEND) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "derive_session: stream_send init failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "stream_send init failed");
|
||||
return -1;
|
||||
}
|
||||
if (sc_stream_init(&sc, &c->stream_recv, STCP_STREAM_CLIENT_SEND) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "derive_session: stream_recv init failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "stream_recv init failed");
|
||||
return -1;
|
||||
}
|
||||
memcpy(c->peer_pubkey, peer_pubkey, SC_PUBKEY_SIZE);
|
||||
@@ -191,7 +192,7 @@ static void process_client_handshake(struct stcp_conn *c) {
|
||||
memcpy(enc_hs, c->recv_buf + SC_PUBKEY_ENC_SIZE, STCP_HS_ENC_CLIENT);
|
||||
size_t hs_data_len;
|
||||
if (stcp_decrypt_and_check(enc_hs, STCP_HS_ENC_CLIENT, &c->stream_recv, &hs_data_len)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "process_client_handshake: decrypt/CRC failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "decrypt/CRC failed");
|
||||
stcp_conn_do_close(c, 2); return;
|
||||
}
|
||||
uint16_t padding_size = (uint16_t)enc_hs[0] | ((uint16_t)enc_hs[1] << 8);
|
||||
@@ -207,13 +208,13 @@ static void finish_client_handshake(struct stcp_conn *c) {
|
||||
|
||||
uint8_t salt2[SC_PUBKEY_ENC_SALT_SIZE];
|
||||
if (random_bytes(salt2, SC_PUBKEY_ENC_SALT_SIZE) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "finish_client_handshake: random_bytes failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "random_bytes failed");
|
||||
stcp_conn_do_close(c, 3); return;
|
||||
}
|
||||
uint16_t padding = 8;
|
||||
size_t total_resp = SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER + padding;
|
||||
uint8_t *resp = u_malloc(total_resp);
|
||||
if (!resp) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "finish_client_handshake: malloc failed"); stcp_conn_do_close(c, 3); return; }
|
||||
if (!resp) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "malloc failed"); stcp_conn_do_close(c, 3); return; }
|
||||
memcpy(resp, salt2, SC_PUBKEY_ENC_SALT_SIZE);
|
||||
sc_obfuscate_pubkey(salt2, c->peer_pubkey, c->my_keys.public_key, resp + SC_PUBKEY_ENC_SALT_SIZE);
|
||||
|
||||
@@ -224,7 +225,7 @@ static void finish_client_handshake(struct stcp_conn *c) {
|
||||
enc_dst[3] = (uint8_t)(crc >> 0); enc_dst[4] = (uint8_t)(crc >> 8);
|
||||
enc_dst[5] = (uint8_t)(crc >> 16); enc_dst[6] = (uint8_t)(crc >> 24);
|
||||
if (sc_stream_xor(&c->stream_send, enc_dst, STCP_HS_ENC_SERVER) != SC_OK) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "finish_client_handshake: encrypt failed");
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "encrypt failed");
|
||||
u_free(resp); stcp_conn_do_close(c, 3); return;
|
||||
}
|
||||
for (int i = 0; i < padding; i++) resp[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER + i] = (uint8_t)(salt2[0] ^ i);
|
||||
@@ -242,24 +243,24 @@ static void server_conn_read_cb(socket_t sock, void *arg) {
|
||||
if (c->recv_buf_cap == 0) {
|
||||
c->recv_buf_cap = STCP_RECV_BUF_INIT;
|
||||
c->recv_buf = u_malloc(c->recv_buf_cap);
|
||||
if (!c->recv_buf) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "server_conn_read_cb: malloc failed"); stcp_conn_do_close(c, ENOMEM); return; }
|
||||
if (!c->recv_buf) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "malloc failed"); stcp_conn_do_close(c, ENOMEM); return; }
|
||||
}
|
||||
if (c->recv_buf_len + 4096 > c->recv_buf_cap) {
|
||||
size_t new_cap = c->recv_buf_cap * 2;
|
||||
if (new_cap > STCP_RECV_BUF_MAX) new_cap = STCP_RECV_BUF_MAX;
|
||||
if (new_cap <= c->recv_buf_cap) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "server_conn_read_cb: recv buf full"); stcp_conn_do_close(c, ENOBUFS); return; }
|
||||
if (new_cap <= c->recv_buf_cap) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "recv buf full"); stcp_conn_do_close(c, ENOBUFS); return; }
|
||||
uint8_t *nb = u_realloc(c->recv_buf, new_cap);
|
||||
if (!nb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "server_conn_read_cb: realloc failed"); stcp_conn_do_close(c, ENOMEM); return; }
|
||||
if (!nb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "realloc failed"); stcp_conn_do_close(c, ENOMEM); return; }
|
||||
c->recv_buf = nb; c->recv_buf_cap = new_cap;
|
||||
}
|
||||
ssize_t n = recv(sock, c->recv_buf + c->recv_buf_len, c->recv_buf_cap - c->recv_buf_len, 0);
|
||||
if (n < 0) {
|
||||
int err = socket_get_error();
|
||||
if (err == ERR_AGAIN || err == ERR_WOULDBLOCK) return;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "server_conn_read_cb: recv failed err=%d", err);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "recv failed err=%d", err);
|
||||
stcp_conn_do_close(c, err); return;
|
||||
}
|
||||
if (n == 0) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "server_conn_read_cb: EOF"); stcp_conn_do_close(c, 0); return; }
|
||||
if (n == 0) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "EOF"); stcp_conn_do_close(c, 0); return; }
|
||||
c->recv_buf_len += (size_t)n;
|
||||
stcp_conn_process_recv(c);
|
||||
}
|
||||
@@ -339,16 +340,16 @@ static void server_accept_cb(socket_t listen_sock, void *arg) {
|
||||
struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port,
|
||||
struct SC_MYKEYS *keys,
|
||||
stcp_connect_cb connect_cb, void *arg) {
|
||||
if (!ua || !keys || !connect_cb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_server_create: invalid args"); return NULL; }
|
||||
if (!ua || !keys || !connect_cb) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "invalid args"); return NULL; }
|
||||
struct stcp_server *srv = u_calloc(1, sizeof(struct stcp_server));
|
||||
if (!srv) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_server_create: calloc failed"); return NULL; }
|
||||
if (!srv) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "calloc failed"); return NULL; }
|
||||
srv->ua = ua;
|
||||
srv->my_keys = *keys;
|
||||
srv->connect_cb = connect_cb;
|
||||
srv->cb_arg = arg;
|
||||
|
||||
srv->listen_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (srv->listen_sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_server_create: socket failed"); u_free(srv); return NULL; }
|
||||
if (srv->listen_sock == SOCKET_INVALID) { DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "socket failed"); u_free(srv); return NULL; }
|
||||
socket_set_nonblocking(srv->listen_sock);
|
||||
int reuse = 1;
|
||||
setsockopt(srv->listen_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuse, sizeof(reuse));
|
||||
@@ -358,11 +359,11 @@ struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port,
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
addr.sin_port = htons(port);
|
||||
if (bind(srv->listen_sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_server_create: bind port=%u failed err=%d", port, socket_get_error());
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "bind port=%u failed err=%d", port, socket_get_error());
|
||||
socket_close_wrapper(srv->listen_sock); u_free(srv); return NULL;
|
||||
}
|
||||
if (listen(srv->listen_sock, 16) < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_server_create: listen failed err=%d", socket_get_error());
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "listen failed err=%d", socket_get_error());
|
||||
socket_close_wrapper(srv->listen_sock); u_free(srv); return NULL;
|
||||
}
|
||||
srv->listen_id = uasync_add_socket_t(ua, srv->listen_sock, server_accept_cb, NULL, NULL, srv);
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#ifndef STCP_SERVER_H
|
||||
#define STCP_SERVER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#include "stcp.h"
|
||||
|
||||
typedef void (*stcp_connect_cb)(struct stcp_conn *conn, void *arg);
|
||||
@@ -11,4 +16,8 @@ struct stcp_server *stcp_server_create(struct UASYNC *ua, uint16_t port,
|
||||
stcp_connect_cb connect_cb, void *arg);
|
||||
void stcp_server_destroy(struct stcp_server *srv);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,846 @@
|
||||
/**
|
||||
* @file topo_group.c
|
||||
* @brief BGP-like обмен топологией узлов (группы)
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "../lib/platform_compat.h"
|
||||
#include "../lib/debug_config.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/memory_pool.h"
|
||||
#include "utun_instance.h"
|
||||
#include "etcp_api.h"
|
||||
#include "etcp.h"
|
||||
#include "etcp_connections.h"
|
||||
#include "etcp_debug.h"
|
||||
#include "config_parser.h"
|
||||
#include "topo_node.h"
|
||||
#include "topo_node_lmdb.h"
|
||||
#include "route_lib.h"
|
||||
#include "topo_group.h"
|
||||
#include "route_ping.h"
|
||||
#include "route_connectivity.h"
|
||||
#include "control_server.h"
|
||||
#ifdef USE_SQLITE
|
||||
#include "../../tools/chatgui/transport/topo_node_sqlite.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define TOPO_GROUP_UTUN 0x8000000000000000ULL
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Вспомогательные функции
|
||||
// ============================================================================
|
||||
|
||||
static void topo_group_send_table_request(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
|
||||
if (!group || !conn) return;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending table request to %s", conn->log_name);
|
||||
struct TOPOMSG_TABLE_REQ* req = u_calloc(1, sizeof(struct TOPOMSG_TABLE_REQ));
|
||||
if (!req) return;
|
||||
req->cmd = ETCP_ID_TOPO_ENTRY;
|
||||
req->subcmd = TOPO_SUBCMD_REQUEST_TABLE;
|
||||
struct ll_entry* e = queue_entry_new(0);
|
||||
if (!e) { u_free(req); return; }
|
||||
e->dgram = (uint8_t*)req; e->len = sizeof(struct TOPOMSG_TABLE_REQ);
|
||||
if (etcp_send(conn, e) != 0) { u_free(req); queue_entry_free(e); }
|
||||
}
|
||||
|
||||
static void topo_group_send_table_complete(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
|
||||
if (!group || !conn) return;
|
||||
struct TOPOMSG_TABLE_REQ* req = u_calloc(1, sizeof(struct TOPOMSG_TABLE_REQ));
|
||||
if (!req) return;
|
||||
req->cmd = ETCP_ID_TOPO_ENTRY;
|
||||
req->subcmd = TOPO_SUBCMD_TABLE_COMPLETE;
|
||||
struct ll_entry* e = queue_entry_new(0);
|
||||
if (!e) { u_free(req); return; }
|
||||
e->dgram = (uint8_t*)req; e->len = sizeof(struct TOPOMSG_TABLE_REQ);
|
||||
if (etcp_send(conn, e) != 0) { u_free(req); queue_entry_free(e); }
|
||||
}
|
||||
|
||||
static void topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN* conn);
|
||||
static bool topo_group_should_send_to(const struct TOPO_NODEQ* nq, uint64_t target_id);
|
||||
static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn);
|
||||
static void topo_group_handle_request_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn);
|
||||
|
||||
static void nodeinfo_dump_log(const uint8_t* data, size_t len) {
|
||||
if (!data || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return;
|
||||
struct TOPOMSG_NODEINFO_PKT* pkt = (struct TOPOMSG_NODEINFO_PKT*)data;
|
||||
struct TOPOMSG_NODE* ni = &pkt->node;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "NODEINFO nid=%016llx ver=%u v4s=%u v4a=%u hops=%u",
|
||||
(unsigned long long)ni->node_id, ni->ver,
|
||||
ni->local_v4_sockets, ni->local_v4_addrs, ni->hop_count);
|
||||
}
|
||||
|
||||
static char* nodeinfo_format(const uint8_t* data, size_t len) {
|
||||
if (!data || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return NULL;
|
||||
struct TOPOMSG_NODEINFO_PKT* pkt = (struct TOPOMSG_NODEINFO_PKT*)data;
|
||||
struct TOPOMSG_NODE* ni = &pkt->node;
|
||||
uint64_t node_id = ni->node_id;
|
||||
const uint8_t* dyn = data + sizeof(struct TOPOMSG_NODEINFO_PKT);
|
||||
size_t off = 0;
|
||||
char name_buf[64] = {0};
|
||||
size_t nl = ni->node_name_len;
|
||||
if (nl > 0 && nl < 63 && off + nl < len - sizeof(struct TOPOMSG_NODEINFO_PKT)) {
|
||||
memcpy(name_buf, dyn + off, nl); off += nl;
|
||||
}
|
||||
off += ni->local_v4_sockets * sizeof(struct TOPOMSG_SOCKMETA4);
|
||||
off += ni->local_v4_addrs * sizeof(struct TOPOMSG_ADDR4);
|
||||
off += ni->local_v6_sockets * sizeof(struct TOPOMSG_SOCKMETA6);
|
||||
off += ni->local_v6_addrs * sizeof(struct TOPOMSG_ADDR6);
|
||||
char subs_buf[512] = {0};
|
||||
if (ni->local_v4_subnets > 0) {
|
||||
const struct TOPOMSG_SUBNET4* subs = (const struct TOPOMSG_SUBNET4*)(dyn + off);
|
||||
size_t sl = 0;
|
||||
for (uint8_t i = 0; i < ni->local_v4_subnets && sl < 400; i++) {
|
||||
uint32_t addr = (subs[i].addr[0]<<24)|(subs[i].addr[1]<<16)|(subs[i].addr[2]<<8)|subs[i].addr[3];
|
||||
char tmp[64]; snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%u ",(addr>>24)&255,(addr>>16)&255,(addr>>8)&255,addr&255,subs[i].prefix_length);
|
||||
strcat(subs_buf,tmp); sl += strlen(tmp);
|
||||
}
|
||||
}
|
||||
int need = snprintf(NULL,0,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v4a=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v4_addrs,(unsigned)ni->hop_count);
|
||||
char* buf = u_malloc(need+1); if (!buf) return NULL;
|
||||
snprintf(buf,need+1,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v4a=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v4_addrs,(unsigned)ni->hop_count);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const char* group_subcmd_name(uint8_t subcmd) {
|
||||
switch (subcmd) {
|
||||
case TOPO_SUBCMD_NODEINFO: return "NODEINFO";
|
||||
case TOPO_SUBCMD_REQUEST_TABLE: return "REQ_TABLE";
|
||||
case TOPO_SUBCMD_WITHDRAW: return "WITHDRAW";
|
||||
case TOPO_SUBCMD_PING_REQ: return "PING_REQ";
|
||||
case TOPO_SUBCMD_PING_RESP: return "PING_RESP";
|
||||
case TOPO_SUBCMD_NAT_INFO: return "NAT_INFO";
|
||||
case TOPO_SUBCMD_NAT_CHECK_REQ: return "NAT_CHECK_REQ";
|
||||
case TOPO_SUBCMD_TABLE_COMPLETE: return "TABLE_COMPLETE";
|
||||
case TOPO_SUBCMD_ERR_GROUP_MISMATCH: return "ERR_GROUP_MISMATCH";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Broadcast / Withdraw
|
||||
// ============================================================================
|
||||
|
||||
static void topo_group_broadcast_withdraw(struct TOPO_GROUP* group, uint64_t node_id, uint64_t wd_source, struct ETCP_CONN* exclude) {
|
||||
if (!group) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group is NULL"); return; }
|
||||
struct TOPOMSG_WITHDRAW_PKT* pkt = u_calloc(1, sizeof(struct TOPOMSG_WITHDRAW_PKT));
|
||||
if (!pkt) return;
|
||||
pkt->cmd = ETCP_ID_TOPO_ENTRY; pkt->subcmd = TOPO_SUBCMD_WITHDRAW;
|
||||
pkt->node_id = node_id; pkt->wd_source = wd_source;
|
||||
struct ll_entry* e = group->senders_list ? group->senders_list->head : NULL;
|
||||
while (e) {
|
||||
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)e->data;
|
||||
if (item && item->conn && item->conn != exclude) {
|
||||
struct ll_entry* copy = queue_entry_new(0);
|
||||
if (copy) { copy->dgram = u_malloc(sizeof(*pkt)); if (copy->dgram) memcpy(copy->dgram, pkt, sizeof(*pkt)); copy->len = sizeof(*pkt); etcp_send(item->conn, copy); }
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
u_free(pkt);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Приём пакетов
|
||||
// ============================================================================
|
||||
|
||||
static void topo_group_handle_nat_info(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len);
|
||||
static void topo_group_handle_nat_check_req(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len);
|
||||
|
||||
static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* entry) {
|
||||
if (!from_conn || !entry || entry->len < 2) { if (entry) { queue_dgram_free(entry); queue_entry_free(entry); } return; }
|
||||
struct UTUN_INSTANCE* instance = from_conn->instance;
|
||||
if (!instance || !instance->topo_groups) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid instance/topo_groups"); queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(instance->topo_groups);
|
||||
if (!group) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "no default group"); queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
uint8_t* data = entry->dgram; uint8_t cmd = data[0]; uint8_t subcmd = data[1];
|
||||
if (cmd != ETCP_ID_TOPO_ENTRY) { queue_dgram_free(entry); queue_entry_free(entry); return; }
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP recv %s from %s len=%zu", group_subcmd_name(subcmd), from_conn->log_name, entry->len);
|
||||
|
||||
if (subcmd == TOPO_SUBCMD_NODEINFO) { nodeinfo_dump_log(data, entry->len); topo_group_process_nodeinfo(group, from_conn, data, entry->len); }
|
||||
else if (subcmd == TOPO_SUBCMD_WITHDRAW) topo_group_process_withdraw(group, from_conn, data, entry->len);
|
||||
else if (subcmd == TOPO_SUBCMD_REQUEST_TABLE) topo_group_handle_request_table(group, from_conn);
|
||||
else if (subcmd == TOPO_SUBCMD_PING_REQ) route_ping_handle_req(group, from_conn, data, entry->len);
|
||||
else if (subcmd == TOPO_SUBCMD_PING_RESP) route_ping_handle_resp(group, from_conn, data, entry->len);
|
||||
else if (subcmd == TOPO_SUBCMD_NAT_INFO) topo_group_handle_nat_info(group, from_conn, data, entry->len);
|
||||
else if (subcmd == TOPO_SUBCMD_NAT_CHECK_REQ) topo_group_handle_nat_check_req(group, from_conn, data, entry->len);
|
||||
else if (subcmd == TOPO_SUBCMD_TABLE_COMPLETE) { etcp_set_routing_exchange_state(from_conn, 3); DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP initial sync complete with %s", from_conn->log_name); }
|
||||
else if (subcmd == TOPO_SUBCMD_ERR_GROUP_MISMATCH) {
|
||||
if (entry->len >= sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH)) {
|
||||
struct TOPOMSG_ERR_GROUP_MISMATCH* err = (struct TOPOMSG_ERR_GROUP_MISMATCH*)data;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Group type mismatch from %s: expected_type=%u received_flags=0x%02x",
|
||||
from_conn->log_name, err->expected_type, err->received_flags);
|
||||
}
|
||||
}
|
||||
|
||||
queue_dgram_free(entry); queue_entry_free(entry);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Init / Destroy / New / Remove conn
|
||||
// ============================================================================
|
||||
|
||||
static void topo_group_on_conn_up(struct ETCP_CONN* conn, void* arg) {
|
||||
(void)arg;
|
||||
if (conn && conn->instance && conn->instance->topo_groups) {
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(conn->instance->topo_groups);
|
||||
if (group) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP peer UP: %s peer=%016llx", conn->log_name, (unsigned long long)conn->peer_node_id); topo_group_new_conn(group, conn); }
|
||||
}
|
||||
}
|
||||
static void topo_group_on_conn_down(struct ETCP_CONN* conn, void* arg) {
|
||||
(void)arg;
|
||||
if (conn && conn->instance && conn->instance->topo_groups) {
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(conn->instance->topo_groups);
|
||||
if (group) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP peer DOWN: %s peer=%016llx", conn->log_name, (unsigned long long)conn->peer_node_id); topo_group_remove_conn(group, conn); }
|
||||
}
|
||||
}
|
||||
static void topo_group_etcp_conn_cbk(struct ETCP_CONN* conn, void* arg) {
|
||||
(void)arg;
|
||||
if (conn && conn->instance && conn->instance->topo_groups) {
|
||||
struct TOPO_GROUP* group = topo_groups_get_default(conn->instance->topo_groups);
|
||||
if (group) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP set callbacks: %s", conn->log_name); etcp_conn_add_up_cbk(conn, topo_group_on_conn_up, group); etcp_conn_add_down_cbk(conn, topo_group_on_conn_down, group); }
|
||||
}
|
||||
}
|
||||
|
||||
static struct TOPO_GROUP* topo_group_create(struct UTUN_INSTANCE* instance, uint64_t group_id, uint8_t group_type) {
|
||||
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "instance is NULL"); return NULL; }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "group_id=%016llx node_id=%016llx", (unsigned long long)group_id, (unsigned long long)instance->node_id);
|
||||
|
||||
struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_GROUP));
|
||||
if (!qe) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc failed"); return NULL; }
|
||||
struct TOPO_GROUP* group = (struct TOPO_GROUP*)qe;
|
||||
memset((uint8_t*)group + sizeof(struct ll_entry), 0, sizeof(*group) - sizeof(struct ll_entry));
|
||||
group->group_id = group_id;
|
||||
group->group_type = group_type;
|
||||
group->instance = instance;
|
||||
group->next_ping_req_id = 1;
|
||||
|
||||
sc_derive_ed25519_pubkey(instance->my_keys.private_key, group->ed25519_public_key);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Ed25519 pubkey derived from X25519 private key");
|
||||
|
||||
if (group_type == TOPO_GROUP_TYPE_UTUN && instance->config && instance->config->global.db_path[0])
|
||||
topo_node_lmdb_init(group, instance->config->global.db_path);
|
||||
|
||||
group->nodes = queue_new(instance->ua, BGP_NODES_HASH_SIZE, offsetof(struct TOPO_NODEQ, hash_node_id) - sizeof(struct ll_entry), 8, "group_nodes");
|
||||
if (!group->nodes) { queue_entry_free(qe); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "nodes queue creation failed"); return NULL; }
|
||||
|
||||
group->senders_list = queue_new(instance->ua, 0, 0, 0, "group_senders");
|
||||
if (!group->senders_list) { queue_free(group->nodes); queue_entry_free(qe); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "senders_list queue creation failed"); return NULL; }
|
||||
|
||||
group->local_node = NULL;
|
||||
topo_group_update_my_nodeinfo(instance, group);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "TOPO group created: group_id=%016llx", (unsigned long long)group_id);
|
||||
return group;
|
||||
}
|
||||
|
||||
static void topo_group_destroy(struct TOPO_GROUP* group) {
|
||||
if (!group) return;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "group_id=%016llx", (unsigned long long)group->group_id);
|
||||
|
||||
struct ll_entry* e;
|
||||
while ((e = queue_data_get(group->senders_list)) != NULL) queue_entry_free(e);
|
||||
queue_free(group->senders_list);
|
||||
|
||||
if (group->local_node) { topo_node_free_lists(group, group->local_node); u_free(group->local_node); }
|
||||
|
||||
if (group->group_type == TOPO_GROUP_TYPE_UTUN) topo_node_lmdb_destroy(group);
|
||||
|
||||
if (group->nodes) queue_free(group->nodes);
|
||||
}
|
||||
|
||||
struct TOPO_GROUPS* topo_groups_init(struct UTUN_INSTANCE* instance) {
|
||||
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "instance is NULL"); return NULL; }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node_id=%016llx", (unsigned long long)instance->node_id);
|
||||
|
||||
struct TOPO_GROUPS* g = u_calloc(1, sizeof(struct TOPO_GROUPS));
|
||||
if (!g) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc failed"); return NULL; }
|
||||
g->instance = instance;
|
||||
|
||||
g->group_list = queue_new(instance->ua, 16, 0, 8, "topo_groups");
|
||||
if (!g->group_list) { u_free(g); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group_list queue creation failed"); return NULL; }
|
||||
|
||||
g->v4_sock_meta_pool = memory_pool_init(sizeof(struct TOPO_SOCKMETA4), "to_v4sock");
|
||||
g->v4_addr_pool = memory_pool_init(sizeof(struct TOPO_ADDR4), "to_v4addr");
|
||||
g->v6_sock_meta_pool = memory_pool_init(sizeof(struct TOPO_SOCKMETA6), "to_v6sock");
|
||||
g->v6_addr_pool = memory_pool_init(sizeof(struct TOPO_ADDR6), "to_v6addr");
|
||||
g->v4_subnet_pool = memory_pool_init(sizeof(struct TOPO_SUBNET4), "to_v4sub");
|
||||
g->v6_subnet_pool = memory_pool_init(sizeof(struct TOPO_SUBNET6), "to_v6sub");
|
||||
|
||||
instance->topo_groups = g;
|
||||
|
||||
struct TOPO_GROUP* default_group = topo_group_create(instance, TOPO_GROUP_UTUN, TOPO_GROUP_TYPE_UTUN);
|
||||
if (!default_group) {
|
||||
memory_pool_destroy(g->v4_sock_meta_pool); memory_pool_destroy(g->v4_addr_pool);
|
||||
memory_pool_destroy(g->v6_sock_meta_pool); memory_pool_destroy(g->v6_addr_pool);
|
||||
memory_pool_destroy(g->v4_subnet_pool); memory_pool_destroy(g->v6_subnet_pool);
|
||||
queue_free(g->group_list); u_free(g);
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "failed to create default group");
|
||||
return NULL;
|
||||
}
|
||||
queue_data_put_with_index(g->group_list, &default_group->ll);
|
||||
|
||||
etcp_bind(instance, ETCP_ID_TOPO_ENTRY, topo_group_receive_cbk);
|
||||
etcp_add_new_conn_cbk(instance, topo_group_etcp_conn_cbk, NULL);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "TOPO groups initialized (with default group)");
|
||||
return g;
|
||||
}
|
||||
|
||||
void topo_groups_destroy(struct UTUN_INSTANCE* instance) {
|
||||
if (!instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "instance is NULL"); return; }
|
||||
if (!instance->topo_groups) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "topo_groups is NULL"); return; }
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node_id=%016llx", (unsigned long long)instance->node_id);
|
||||
|
||||
etcp_unbind(instance, ETCP_ID_TOPO_ENTRY);
|
||||
route_connectivity_cancel_all(instance);
|
||||
|
||||
struct TOPO_GROUPS* g = instance->topo_groups;
|
||||
struct ll_entry* ge;
|
||||
while ((ge = queue_data_get(g->group_list)) != NULL) { struct TOPO_GROUP* grp = (struct TOPO_GROUP*)ge; topo_group_destroy(grp); queue_entry_free(ge); }
|
||||
queue_free(g->group_list);
|
||||
|
||||
memory_pool_destroy(g->v4_sock_meta_pool);
|
||||
memory_pool_destroy(g->v4_addr_pool);
|
||||
memory_pool_destroy(g->v6_sock_meta_pool);
|
||||
memory_pool_destroy(g->v6_addr_pool);
|
||||
memory_pool_destroy(g->v4_subnet_pool);
|
||||
memory_pool_destroy(g->v6_subnet_pool);
|
||||
|
||||
u_free(g); instance->topo_groups = NULL;
|
||||
}
|
||||
|
||||
struct TOPO_GROUP* topo_groups_get_default(struct TOPO_GROUPS* g) {
|
||||
return topo_groups_find(g, TOPO_GROUP_UTUN);
|
||||
}
|
||||
|
||||
struct TOPO_GROUP* topo_groups_find(struct TOPO_GROUPS* g, uint64_t group_id) {
|
||||
if (!g || !g->group_list) return NULL;
|
||||
struct ll_entry* e = queue_find_data_by_index(g->group_list, &group_id);
|
||||
return e ? (struct TOPO_GROUP*)e : NULL;
|
||||
}
|
||||
|
||||
struct TOPO_GROUP* topo_groups_create_group(struct TOPO_GROUPS* g, uint64_t group_id, uint8_t group_type, const char* channel_id) {
|
||||
if (!g || !g->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return NULL; }
|
||||
if (topo_groups_find(g, group_id)) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group_id=%016llx already exists", (unsigned long long)group_id); return NULL; }
|
||||
struct TOPO_GROUP* group = topo_group_create(g->instance, group_id, group_type);
|
||||
if (!group) return NULL;
|
||||
if (channel_id) { strncpy(group->channel_id, channel_id, sizeof(group->channel_id) - 1); group->channel_id[sizeof(group->channel_id) - 1] = '\0'; }
|
||||
queue_data_put_with_index(g->group_list, &group->ll);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "Group created: group_id=%016llx type=%u ch_id=%s", (unsigned long long)group_id, group_type, group->channel_id);
|
||||
return group;
|
||||
}
|
||||
|
||||
#ifdef USE_SQLITE
|
||||
void topo_groups_set_sqlite_db(struct TOPO_GROUPS* g, sqlite3* db) {
|
||||
if (!g) return;
|
||||
g->topo_sqlite_db = db;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "topo_sqlite_db set");
|
||||
}
|
||||
#endif
|
||||
|
||||
void topo_group_set_nat_check_local(struct TOPO_GROUP* group, int allow) { if (!group) return; group->allow_nat_check_local = allow ? 1 : 0; }
|
||||
|
||||
void topo_groups_set_node_updated_cb(struct TOPO_GROUPS* groups, topo_node_updated_fn fn) {
|
||||
if (groups) groups->node_updated_cb = fn;
|
||||
}
|
||||
|
||||
void topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
|
||||
if (!group || !conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return; }
|
||||
if (!conn->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "conn->instance is NULL"); return; }
|
||||
if (!conn->instance->rt) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "conn->instance->rt is NULL"); return; }
|
||||
|
||||
struct TOPO_NODEQ* peer_nq = topo_node_find_by_id(group, conn->peer_node_id);
|
||||
topo_group_add_to_senders(group, conn);
|
||||
if (peer_nq && peer_nq->alien) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "peer 0x%016llx is alien, skipping route exchange", (unsigned long long)conn->peer_node_id); return; }
|
||||
|
||||
struct ETCP_CONN* c = conn->instance->connections;
|
||||
while (c) { struct ETCP_LINK* l = c->links; while (l) { if (l->initialized && l->conn && l->nat_check_status < NAT_CHECK_IN_PROGRESS) topo_group_start_link_nat_check(group, l); l = l->next; } c = c->next; }
|
||||
|
||||
topo_group_send_table_request(group, conn);
|
||||
}
|
||||
|
||||
void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
|
||||
if (!group || !conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return; }
|
||||
if (!conn->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid instance"); return; }
|
||||
|
||||
bool found_in_list = false;
|
||||
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 == conn) { found_in_list = true; break; } e = e->next; }
|
||||
if (!found_in_list) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "conn already removed or not in list, skipping"); return; }
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "peer=%016llx", (unsigned long long)conn->peer_node_id);
|
||||
|
||||
route_ping_cancel_for_conn(group, conn);
|
||||
struct ETCP_LINK* lnk = conn->links; while (lnk) { if (lnk->nat_check_status == NAT_CHECK_IN_PROGRESS) lnk->nat_check_status = NAT_CHECK_NONE; lnk = lnk->next; }
|
||||
|
||||
struct ROUTE_TABLE* rt = conn->instance->rt;
|
||||
int nodes_removed = 0;
|
||||
struct ll_entry* node_entry = group->nodes ? group->nodes->head : NULL;
|
||||
while (node_entry) {
|
||||
struct ll_entry* next = node_entry->next;
|
||||
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)node_entry;
|
||||
if (topo_group_remove_path(nq, conn) == 1) {
|
||||
if (group->group_type != TOPO_GROUP_TYPE_CHAT && rt) route_delete(rt, nq);
|
||||
if (conn->instance && conn->instance->control_srv) control_server_notify_node_removed(conn->instance->control_srv, nq->hash_node_id);
|
||||
nq->dirty = 1;
|
||||
route_connectivity_cancel_node(conn->instance, nq);
|
||||
if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; }
|
||||
uint64_t key = nq->hash_node_id;
|
||||
topo_node_free_lists(group, nq);
|
||||
struct ll_entry* entry = node_entry;
|
||||
if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); }
|
||||
nodes_removed++;
|
||||
etcp_router_conn_close_all_for_node(group->instance, key);
|
||||
uint64_t wd_src = (key == conn->peer_node_id) ? conn->instance->node_id : conn->peer_node_id;
|
||||
topo_group_broadcast_withdraw(group, key, wd_src, NULL);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "Removed node %016llx after link down", (unsigned long long)key);
|
||||
}
|
||||
node_entry = next;
|
||||
}
|
||||
|
||||
e = group->senders_list->head;
|
||||
while (e) { struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)e->data; if (item->conn == conn) { queue_remove_data(group->senders_list, e); queue_entry_free(e); break; } e = e->next; }
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP peer removed: %s nodes=%d", conn->log_name, nodes_removed);
|
||||
}
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// NODEINFO BASED IMPLEMENTATION
|
||||
// ============================================================================
|
||||
|
||||
struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64_t node_id) {
|
||||
if (!group) return NULL;
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(group, node_id);
|
||||
if (!nq || !nq->paths || !nq->paths->head) return NULL;
|
||||
struct ll_entry* e = nq->paths->head;
|
||||
struct TOPO_NODEPATH* best_live = NULL; uint8_t best_live_hops = 255;
|
||||
struct TOPO_NODEPATH* best_any = NULL; uint8_t best_any_hops = 255;
|
||||
while (e) {
|
||||
struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)e;
|
||||
if (path->conn) { if (path->conn->links_up && path->hop_count < best_live_hops) { best_live = path; best_live_hops = path->hop_count; } if (path->hop_count < best_any_hops) { best_any = path; best_any_hops = path->hop_count; } }
|
||||
e = e->next;
|
||||
}
|
||||
if (!best_live && best_any) DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "find_conn_for_node %016llx: no live paths, fallback to best (hops=%d links_up=%d)", (unsigned long long)node_id, best_any_hops, best_any->conn->links_up);
|
||||
return best_live ? best_live->conn : (best_any ? best_any->conn : NULL);
|
||||
}
|
||||
|
||||
int topo_group_add_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count) {
|
||||
if (!nq || !conn || hop_count > MAX_HOPS || !hop_list) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "add_path: invalid args"); return -1; }
|
||||
if (!nq->paths) { nq->paths = queue_new(conn->instance->ua, 0, 0, 0, "node_paths"); if (!nq->paths) return -1; }
|
||||
size_t path_size = sizeof(struct TOPO_NODEPATH) - sizeof(struct ll_entry) + hop_count * 8;
|
||||
struct ll_entry* pe = queue_entry_new(path_size); if (!pe) return -1;
|
||||
struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)pe;
|
||||
path->conn = conn; path->hop_count = hop_count;
|
||||
memcpy((uint64_t*)((uint8_t*)path + sizeof(struct TOPO_NODEPATH)), hop_list, hop_count * 8);
|
||||
queue_data_put(nq->paths, pe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int topo_group_remove_path_by_hop(struct TOPO_NODEQ* nq, uint64_t wd_source) {
|
||||
if (!nq || !nq->paths) return 0;
|
||||
int removed = 0;
|
||||
struct ll_entry* e = nq->paths->head;
|
||||
while (e) {
|
||||
struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)e;
|
||||
uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct TOPO_NODEPATH));
|
||||
bool has_wd = false; for (uint8_t i = 0; i < path->hop_count; i++) if (hop[i] == wd_source) { has_wd = true; break; }
|
||||
if (has_wd) { struct ll_entry* next = e->next; queue_remove_data(nq->paths, e); queue_entry_free(e); removed++; e = next; continue; }
|
||||
e = e->next;
|
||||
}
|
||||
if (removed > 0 && nq->paths && queue_entry_count(nq->paths) == 0) { queue_free(nq->paths); nq->paths = NULL; return 1; }
|
||||
return removed;
|
||||
}
|
||||
|
||||
int topo_group_remove_path(struct TOPO_NODEQ* nq, struct ETCP_CONN* conn) {
|
||||
if (!nq || !conn || !nq->paths) return -1;
|
||||
struct ll_entry* e = nq->paths->head;
|
||||
while (e) { struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)e; if (path->conn == conn) { queue_remove_data(nq->paths, e); queue_entry_free(e); break; } e = e->next; }
|
||||
int remaining = nq->paths ? queue_entry_count(nq->paths) : 0;
|
||||
if (remaining == 0) { if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; } return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
// NAT check context
|
||||
static void nat_link_check_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok, void* arg) {
|
||||
(void)avg_rtt; (void)count_sent; (void)count_ok;
|
||||
struct nat_check_arg* na = (struct nat_check_arg*)arg;
|
||||
if (!na || !na->link) { u_free(na); return; }
|
||||
struct ETCP_LINK* link = na->link;
|
||||
if (link->nat_check_status != NAT_CHECK_IN_PROGRESS) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "nat check cb: link status=%d, skip (conn down?)", link->nat_check_status); u_free(na); return; }
|
||||
link->nat_type = success ? NAT_TYPE_EIM : NAT_TYPE_STRICT;
|
||||
link->nat_check_status = success ? NAT_CHECK_EIM : NAT_CHECK_STRICT;
|
||||
if (link->etcp) topo_group_send_nat_info(link->etcp, link->remote_socket_id, na->nat_ip, na->nat_port, link->nat_type);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT check result: type=%s for conn=%s nat=%s:%u", success ? "EIM" : "STRICT", link->etcp->log_name, ip_to_str(&na->nat_ip, AF_INET).str, na->nat_port);
|
||||
u_free(na);
|
||||
}
|
||||
|
||||
static struct ETCP_CONN* topo_group_find_third_node(struct TOPO_GROUP* group, struct ETCP_CONN* exclude) {
|
||||
if (!group || !group->senders_list) return NULL;
|
||||
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 && item->conn && item->conn != exclude && item->conn->links && item->conn->links->remote_only_local==0 && item->conn->links->remote_type != CFG_SERVER_TYPE_PRIVATE && item->conn->links->remote_type != CFG_SERVER_TYPE_LOCAL) return item->conn; e = e->next; }
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void topo_group_start_link_nat_check(struct TOPO_GROUP* group, struct ETCP_LINK* link) {
|
||||
if (!group || !link || !link->conn || !link->etcp) return;
|
||||
if (link->nat_type == NAT_TYPE_DIRECT) return;
|
||||
if (link->nat_check_status == NAT_CHECK_IN_PROGRESS) return;
|
||||
if (link->conn->type == CFG_SERVER_TYPE_PRIVATE || link->conn->type == CFG_SERVER_TYPE_LOCAL) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "skip nat check for private/local socket link"); return; }
|
||||
struct ETCP_CONN* third_conn = topo_group_find_third_node(group, link->etcp);
|
||||
if (!third_conn) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "no third node found for link"); return; }
|
||||
|
||||
uint32_t target_ip; uint16_t target_port;
|
||||
if (link->nat_ip != 0 && link->nat_port != 0) { target_ip = link->nat_ip; target_port = link->nat_port; }
|
||||
else { struct sockaddr_in* sin = (struct sockaddr_in*)&link->remote_addr; target_ip = sin->sin_addr.s_addr; target_port = ntohs(sin->sin_port); }
|
||||
|
||||
if (!group->allow_nat_check_local && is_local_subnet(target_ip)) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "local connection: %s, skip nat check", ip_to_str(&target_ip, AF_INET).str); return; }
|
||||
|
||||
struct nat_check_arg* arg = u_calloc(1, sizeof(struct nat_check_arg));
|
||||
if (!arg) return; arg->link = link; arg->nat_ip = target_ip; arg->nat_port = target_port;
|
||||
const uint8_t* pubkey = link->etcp->crypto_ctx.peer_key_set ? link->etcp->crypto_ctx.peer_public_key : NULL;
|
||||
if (!pubkey) { u_free(arg); DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "no peer pubkey for link=%p", (void*)link); return; }
|
||||
int ret = route_ping_send_req_addr(group, third_conn, target_ip, target_port, 3, 500, 1000, 5000, nat_link_check_cb, arg, pubkey);
|
||||
if (ret == 0) { link->nat_check_status = NAT_CHECK_IN_PROGRESS; DEBUG_INFO(DEBUG_CATEGORY_BGP, "ping remote request via=%s (%s:%d)", third_conn->log_name, ip_to_str(&target_ip, AF_INET).str, target_port); }
|
||||
else { u_free(arg); DEBUG_WARN(DEBUG_CATEGORY_BGP, "failed to start for link=%s (%s:%d)", third_conn->log_name, ip_to_str(&target_ip, AF_INET).str, target_port); }
|
||||
}
|
||||
|
||||
// ===== NODEINFO process =====
|
||||
|
||||
static void topo_group_send_err_group_mismatch(struct TOPO_GROUP* group, struct ETCP_CONN* conn, uint8_t expected_type, uint8_t received_flags) {
|
||||
if (!group || !conn) return;
|
||||
struct TOPOMSG_ERR_GROUP_MISMATCH* pkt = u_calloc(1, sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH));
|
||||
if (!pkt) return;
|
||||
pkt->cmd = ETCP_ID_TOPO_ENTRY; pkt->subcmd = TOPO_SUBCMD_ERR_GROUP_MISMATCH;
|
||||
pkt->expected_type = expected_type; pkt->received_flags = received_flags;
|
||||
struct ll_entry* e = queue_entry_new(0);
|
||||
if (!e) { u_free(pkt); return; }
|
||||
e->dgram = (uint8_t*)pkt; e->len = sizeof(struct TOPOMSG_ERR_GROUP_MISMATCH);
|
||||
if (etcp_send(conn, e) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send ERR_GROUP_MISMATCH failed"); u_free(pkt); queue_entry_free(e); }
|
||||
}
|
||||
|
||||
int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from, const uint8_t* data, size_t len) {
|
||||
if (!group || !from || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return -1;
|
||||
|
||||
struct TOPOMSG_NODEINFO_PKT* pkt = (struct TOPOMSG_NODEINFO_PKT*)data;
|
||||
struct TOPOMSG_NODE* ni = &pkt->node;
|
||||
|
||||
// Проверка соответствия типа группы
|
||||
uint8_t expected_flags = (group->group_type == TOPO_GROUP_TYPE_CHAT) ? 0 : TOPO_FLAG_SEND_SUBNETS;
|
||||
if ((ni->flags & TOPO_FLAG_SEND_SUBNETS) != expected_flags) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "Group type mismatch with %s: expected_flags=0x%02x received_flags=0x%02x (local_type=%u)",
|
||||
from->log_name, expected_flags, ni->flags, group->group_type);
|
||||
topo_group_send_err_group_mismatch(group, from, group->group_type, ni->flags);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dyn = topo_node_dyn_size(ni);
|
||||
if ((size_t)dyn + sizeof(struct TOPOMSG_NODEINFO_PKT) != len) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "Incorrect packet size (%s)", from->log_name); return -1; }
|
||||
|
||||
uint64_t node_id = ni->node_id;
|
||||
if (ni->hop_count >= MAX_HOPS) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "NODEINFO from %s dropped: too many hops (%d)", from->log_name, ni->hop_count); return -1; }
|
||||
if (node_id == group->instance->node_id) return 0;
|
||||
|
||||
struct TOPO_NODEQ* nodeinfo1 = topo_node_find_by_id(group, node_id);
|
||||
uint8_t new_ver = ni->ver;
|
||||
|
||||
if (nodeinfo1 && (int8_t)(nodeinfo1->last_ver - new_ver) >= 0) {
|
||||
int new_hops = ni->hop_count + 1;
|
||||
if (new_hops <= MAX_HOPS) {
|
||||
uint64_t hop_list[MAX_HOPS];
|
||||
const uint8_t* hop_src = (const uint8_t*)ni + sizeof(struct TOPOMSG_NODE) + dyn - ni->hop_count * 8;
|
||||
memcpy(hop_list, hop_src, ni->hop_count * 8);
|
||||
hop_list[ni->hop_count] = from->peer_node_id;
|
||||
topo_group_remove_path(nodeinfo1, from);
|
||||
topo_group_add_path(nodeinfo1, from, hop_list, new_hops);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NODEINFO: node=%016llx ver=%d v4s=%d v4a=%d v6s=%d v6a=%d hops=%d from=%s",
|
||||
(unsigned long long)node_id, new_ver, ni->local_v4_sockets, ni->local_v4_addrs,
|
||||
ni->local_v6_sockets, ni->local_v6_addrs, ni->hop_count, from->log_name);
|
||||
|
||||
struct ll_queue* paths = NULL;
|
||||
int is_new_node = 0;
|
||||
if (nodeinfo1) {
|
||||
topo_node_free_lists(group, nodeinfo1);
|
||||
paths = nodeinfo1->paths; nodeinfo1->paths = NULL;
|
||||
} else { is_new_node = 1; }
|
||||
|
||||
const uint8_t* ser_data = data + 2;
|
||||
size_t ser_len = len - 2;
|
||||
struct TOPO_NODE* new_ni = NULL;
|
||||
struct TOPO_NODESUBNETS* new_subnets = NULL;
|
||||
struct TOPOMSG_TRANZIT* new_tranzit = NULL; uint8_t new_tranzit_count = 0;
|
||||
uint64_t* new_hop_list = NULL; uint8_t new_hop_count = 0;
|
||||
if (topo_node_deserialize(group, ser_data, ser_len, &new_ni, &new_subnets, &new_tranzit, &new_tranzit_count, &new_hop_list, &new_hop_count) != 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "NODEINFO deserialize failed from %s", from->log_name);
|
||||
if (nodeinfo1) { queue_remove_data(group->nodes, &nodeinfo1->ll); queue_free(paths); queue_entry_free(&nodeinfo1->ll); }
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nodeinfo1) {
|
||||
nodeinfo1->node = new_ni; topo_node_ref(new_ni);
|
||||
nodeinfo1->hash_node_id = new_ni->node_id;
|
||||
nodeinfo1->subnets = new_subnets;
|
||||
u_free(nodeinfo1->tranzit_data); nodeinfo1->tranzit_data = new_tranzit;
|
||||
nodeinfo1->tranzit_count = new_tranzit_count;
|
||||
u_free(nodeinfo1->hop_list); nodeinfo1->hop_list = new_hop_list;
|
||||
nodeinfo1->hop_count = new_hop_count;
|
||||
} else {
|
||||
struct ll_entry* qe = queue_entry_new(sizeof(struct TOPO_NODEQ));
|
||||
if (!qe) { topo_node_unref(new_ni); u_free(new_tranzit); u_free(new_hop_list); u_free(new_subnets); return -1; }
|
||||
nodeinfo1 = (struct TOPO_NODEQ*)qe;
|
||||
memset((uint8_t*)nodeinfo1 + sizeof(struct ll_entry), 0, sizeof(*nodeinfo1) - sizeof(struct ll_entry));
|
||||
nodeinfo1->node = new_ni; topo_node_ref(new_ni);
|
||||
nodeinfo1->hash_node_id = new_ni->node_id;
|
||||
nodeinfo1->subnets = new_subnets;
|
||||
nodeinfo1->tranzit_data = new_tranzit; nodeinfo1->tranzit_count = new_tranzit_count;
|
||||
nodeinfo1->hop_list = new_hop_list; nodeinfo1->hop_count = new_hop_count;
|
||||
nodeinfo1->connectivity.probe_status = PROBE_STATUS_NONE;
|
||||
nodeinfo1->connectivity.interface_status = PROBE_RESULT_UNKNOWN;
|
||||
nodeinfo1->connectivity.nat_status = PROBE_RESULT_UNKNOWN;
|
||||
nodeinfo1->connectivity.real_status = PROBE_RESULT_UNKNOWN;
|
||||
nodeinfo1->connectivity.ping_req_time = 0;
|
||||
nodeinfo1->alien = 0;
|
||||
queue_data_put_with_index(group->nodes, &nodeinfo1->ll);
|
||||
}
|
||||
|
||||
nodeinfo1->paths = paths; nodeinfo1->last_ver = new_ver;
|
||||
|
||||
uint64_t hop_list[MAX_HOPS];
|
||||
if (nodeinfo1->hop_count > 0) memcpy(hop_list, nodeinfo1->hop_list, nodeinfo1->hop_count * 8);
|
||||
hop_list[nodeinfo1->hop_count] = from->peer_node_id;
|
||||
nodeinfo1->hop_count++;
|
||||
|
||||
u_free(nodeinfo1->hop_list);
|
||||
nodeinfo1->hop_list = u_malloc(nodeinfo1->hop_count * 8);
|
||||
memcpy(nodeinfo1->hop_list, hop_list, nodeinfo1->hop_count * 8);
|
||||
|
||||
topo_group_remove_path_by_hop(nodeinfo1, from->peer_node_id);
|
||||
topo_group_add_path(nodeinfo1, from, hop_list, nodeinfo1->hop_count);
|
||||
|
||||
if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance->rt) route_insert(group->instance->rt, nodeinfo1);
|
||||
if (node_id != group->instance->node_id) {
|
||||
#ifdef USE_SQLITE
|
||||
if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->instance->topo_groups->topo_sqlite_db) {
|
||||
topo_node_sqlite_node_put(group->instance->topo_groups->topo_sqlite_db, nodeinfo1);
|
||||
if (group->channel_id[0]) topo_node_sqlite_member_put(group->instance->topo_groups->topo_sqlite_db, group->channel_id, node_id, NULL, NULL);
|
||||
} else
|
||||
#endif
|
||||
topo_node_lmdb_put(group, nodeinfo1);
|
||||
}
|
||||
if (group->instance->control_srv) control_server_notify_node_change(group->instance->control_srv, nodeinfo1);
|
||||
|
||||
if (group->instance->topo_groups->node_updated_cb)
|
||||
group->instance->topo_groups->node_updated_cb(group->instance, node_id, ni->public_key, ni->ed25519_public_key);
|
||||
|
||||
int hop_count = nodeinfo1->hop_count;
|
||||
struct ll_entry* se = group->senders_list ? group->senders_list->head : NULL;
|
||||
while (se) {
|
||||
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)se->data;
|
||||
if (item->conn) {
|
||||
uint64_t id = item->conn->peer_node_id;
|
||||
if (id != node_id) {
|
||||
int found = 0; for (int i = 0; i < hop_count; i++) if (hop_list[i] == id) found = 1;
|
||||
if (found == 0) topo_group_send_nodeinfo(group, nodeinfo1, item->conn);
|
||||
}
|
||||
}
|
||||
se = se->next;
|
||||
}
|
||||
|
||||
int socks_changed = 1;
|
||||
if (!is_new_node) socks_changed = 1;
|
||||
|
||||
if (node_id != group->instance->node_id && (is_new_node || socks_changed)) {
|
||||
int v4a = topo_list_count((struct _topo_head*)nodeinfo1->node->v4_addrs);
|
||||
int v6a = topo_list_count((struct _topo_head*)nodeinfo1->node->v6_addrs);
|
||||
if (v4a > 0 || v6a > 0) route_connectivity_probe_node(group->instance, nodeinfo1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* sender, const uint8_t* data, size_t len) {
|
||||
if (!group || len < sizeof(struct TOPOMSG_WITHDRAW_PKT)) return -1;
|
||||
struct TOPOMSG_WITHDRAW_PKT* wp = (struct TOPOMSG_WITHDRAW_PKT*)data;
|
||||
uint64_t node_id = wp->node_id, wd_source = wp->wd_source;
|
||||
|
||||
struct TOPO_NODEQ* nq = topo_node_find_by_id(group, node_id);
|
||||
if (!nq) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "node not found"); return 0; }
|
||||
int ret = topo_group_remove_path_by_hop(nq, wd_source);
|
||||
if (ret == 1) {
|
||||
if (group->group_type != TOPO_GROUP_TYPE_CHAT && group->instance && group->instance->rt) route_delete(group->instance->rt, nq);
|
||||
if (group->instance->control_srv) control_server_notify_node_removed(group->instance->control_srv, node_id);
|
||||
nq->dirty = 1;
|
||||
route_connectivity_cancel_node(group->instance, nq);
|
||||
if (nq->paths) { queue_free(nq->paths); nq->paths = NULL; }
|
||||
topo_node_free_lists(group, nq);
|
||||
struct ll_entry* entry = queue_find_data_by_index(group->nodes, &node_id);
|
||||
if (entry) { queue_remove_data(group->nodes, entry); queue_entry_free(entry); }
|
||||
topo_group_broadcast_withdraw(group, node_id, wd_source, sender);
|
||||
#ifdef USE_SQLITE
|
||||
if (group->group_type == TOPO_GROUP_TYPE_CHAT && group->instance->topo_groups->topo_sqlite_db)
|
||||
topo_node_sqlite_member_del(group->instance->topo_groups->topo_sqlite_db, group->channel_id, node_id);
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_NODEQ* node, struct ETCP_CONN* conn) {
|
||||
if (!group || !node || !conn) return;
|
||||
|
||||
size_t max_sz = sizeof(struct TOPOMSG_NODEINFO_PKT) + 4096;
|
||||
uint8_t* p = u_malloc(max_sz);
|
||||
if (!p) return;
|
||||
|
||||
p[0] = ETCP_ID_TOPO_ENTRY; p[1] = TOPO_SUBCMD_NODEINFO;
|
||||
|
||||
int ser_len = topo_node_serialize(group, node, p + 2, max_sz - 2);
|
||||
if (ser_len < 0) { u_free(p); return; }
|
||||
|
||||
struct ll_entry* e = queue_entry_new(0);
|
||||
if (!e) { u_free(p); return; }
|
||||
e->dgram = p; e->len = (size_t)ser_len + 2;
|
||||
|
||||
if (etcp_send(conn, e) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send NODEINFO failed"); u_free(p); queue_entry_free(e); }
|
||||
}
|
||||
|
||||
static void topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
|
||||
if (!group || !conn || !group->senders_list) return;
|
||||
bool already = false;
|
||||
struct ll_entry* e = group->senders_list->head;
|
||||
while (e) { if (((struct TOPO_GROUP_CONN_ITEM*)e->data)->conn == conn) { already = true; break; } e = e->next; }
|
||||
if (!already) {
|
||||
struct ll_entry* item_entry = queue_entry_new(sizeof(struct TOPO_GROUP_CONN_ITEM));
|
||||
if (item_entry) { ((struct TOPO_GROUP_CONN_ITEM*)item_entry->data)->conn = conn; queue_data_put(group->senders_list, item_entry); }
|
||||
}
|
||||
}
|
||||
|
||||
static bool topo_group_should_send_to(const struct TOPO_NODEQ* nq, uint64_t target_id) {
|
||||
if (!nq || !nq->node || !nq->paths) return false;
|
||||
if (target_id == nq->node->node_id) return false;
|
||||
struct ll_entry* e = nq->paths->head;
|
||||
while (e) {
|
||||
struct TOPO_NODEPATH* path = (struct TOPO_NODEPATH*)e;
|
||||
uint64_t* hop = (uint64_t*)((uint8_t*)path + sizeof(struct TOPO_NODEPATH));
|
||||
bool has_id = false; for (uint8_t i = 0; i < path->hop_count; i++) if (hop[i] == target_id) { has_id = true; break; }
|
||||
if (!has_id) return true;
|
||||
e = e->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
|
||||
if (!group || !conn) return;
|
||||
uint64_t target = conn->peer_node_id;
|
||||
struct ll_entry* e = group->nodes ? group->nodes->head : NULL;
|
||||
while (e) {
|
||||
struct TOPO_NODEQ* nq = (struct TOPO_NODEQ*)e;
|
||||
if (nq->hop_count == 0) { e = e->next; continue; }
|
||||
if (!nq->paths) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "node has no paths"); e = e->next; continue; }
|
||||
if (topo_group_should_send_to(nq, target)) topo_group_send_nodeinfo(group, nq, conn);
|
||||
e = e->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void topo_group_handle_request_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
|
||||
if (!group || !conn) return;
|
||||
topo_group_send_nodeinfo(group, group->local_node, conn);
|
||||
topo_group_send_full_table(group, conn);
|
||||
topo_group_add_to_senders(group, conn);
|
||||
topo_group_send_table_complete(group, conn);
|
||||
}
|
||||
|
||||
static void topo_group_handle_nat_info(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) {
|
||||
if (!group || !from_conn || !data || len < sizeof(struct TOPOMSG_NAT_INFO) || !group->local_node || !group->local_node->node) return;
|
||||
|
||||
const struct TOPOMSG_NAT_INFO* info = (const struct TOPOMSG_NAT_INFO*)data;
|
||||
uint32_t nat_ip = (info->nat_ip[0] << 24) | (info->nat_ip[1] << 16) | (info->nat_ip[2] << 8) | info->nat_ip[3];
|
||||
uint16_t nat_port = ntohs(info->nat_port); uint8_t socket_id = info->socket_id;
|
||||
|
||||
uint8_t verified_type;
|
||||
if (info->nat_type == NAT_TYPE_EIM) verified_type = NAT_VERIFIED_EIM;
|
||||
else if (info->nat_type == NAT_TYPE_STRICT) verified_type = NAT_VERIFIED_STRICT;
|
||||
else if (info->nat_type == NAT_TYPE_DIRECT) verified_type = NAT_VERIFIED_DIRECT;
|
||||
else verified_type = NAT_VERIFIED_UNKNOWN;
|
||||
|
||||
int data_changed = 0;
|
||||
{ struct TOPO_SOCKMETA4* sm = group->local_node->node->v4_sock_meta; while (sm) { if (sm->id == socket_id) { if (sm->nat_type != verified_type) { sm->nat_type = verified_type; data_changed = 1; } break; } sm = sm->next; } }
|
||||
|
||||
struct ETCP_SOCKET* es = group->instance->etcp_sockets;
|
||||
while (es) { if (es->sock_id == socket_id) {
|
||||
if (verified_type == NAT_VERIFIED_STRICT) { struct sockaddr_in* nat_sin = (struct sockaddr_in*)&es->nat_addr; nat_sin->sin_family = AF_INET; nat_sin->sin_addr.s_addr = nat_ip; nat_sin->sin_port = htons(nat_port); es->nat_type = verified_type; }
|
||||
else { struct sockaddr_in* nat_sin = (struct sockaddr_in*)&es->nat_addr; uint32_t old_nat_ip = nat_sin->sin_addr.s_addr; uint16_t old_nat_port = ntohs(nat_sin->sin_port); nat_sin->sin_family = AF_INET; nat_sin->sin_addr.s_addr = nat_ip; nat_sin->sin_port = htons(nat_port); if (old_nat_ip != nat_ip || old_nat_port != nat_port) data_changed = 1; es->nat_type = verified_type; } break; }
|
||||
es = es->next; }
|
||||
|
||||
if (data_changed) {
|
||||
int prev_v4a = topo_list_count((struct _topo_head*)group->local_node->node->v4_addrs);
|
||||
topo_group_update_my_nodeinfo(group->instance, group);
|
||||
if (topo_list_count((struct _topo_head*)group->local_node->node->v4_addrs) != prev_v4a) {
|
||||
group->local_node->dirty = 1;
|
||||
group->local_node->node->ver = (group->local_node->node->ver % 255) + 1;
|
||||
group->local_node->last_ver = group->local_node->node->ver;
|
||||
}
|
||||
if (group->local_node->dirty && group->senders_list) {
|
||||
struct ll_entry* se = group->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(group, group->local_node, item->conn); se = se->next; }
|
||||
}
|
||||
}
|
||||
|
||||
struct ETCP_LINK* l = from_conn->links; while (l) { l->nat_type = info->nat_type; l = l->next; }
|
||||
}
|
||||
|
||||
static void topo_group_handle_nat_check_req(struct TOPO_GROUP* group, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) {
|
||||
if (!group || !from_conn || !data || len < sizeof(struct TOPOMSG_NAT_CHECK_REQ)) return;
|
||||
const struct TOPOMSG_NAT_CHECK_REQ* req = (const struct TOPOMSG_NAT_CHECK_REQ*)data;
|
||||
uint8_t socket_id = req->socket_id;
|
||||
struct ETCP_LINK* target_link = NULL;
|
||||
struct ETCP_LINK* l = from_conn->links; while (l) { if (l->remote_socket_id == socket_id) { target_link = l; break; } l = l->next; }
|
||||
if (!target_link) return;
|
||||
if (target_link->nat_check_status == NAT_CHECK_IN_PROGRESS) return;
|
||||
topo_group_start_link_nat_check(group, target_link);
|
||||
}
|
||||
|
||||
void topo_group_send_nat_info(struct ETCP_CONN* conn, uint8_t socket_id, uint32_t nat_ip, uint16_t nat_port, uint8_t nat_type) {
|
||||
if (!conn) return;
|
||||
struct TOPOMSG_NAT_INFO* pkt = u_calloc(1, sizeof(struct TOPOMSG_NAT_INFO)); if (!pkt) return;
|
||||
pkt->cmd = ETCP_ID_TOPO_ENTRY; pkt->subcmd = TOPO_SUBCMD_NAT_INFO;
|
||||
pkt->socket_id = socket_id;
|
||||
pkt->nat_ip[0] = (nat_ip >> 24) & 0xFF; pkt->nat_ip[1] = (nat_ip >> 16) & 0xFF;
|
||||
pkt->nat_ip[2] = (nat_ip >> 8) & 0xFF; pkt->nat_ip[3] = nat_ip & 0xFF;
|
||||
pkt->nat_port = htons(nat_port); pkt->nat_type = nat_type;
|
||||
struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(pkt); return; }
|
||||
e->dgram = (uint8_t*)pkt; e->len = sizeof(struct TOPOMSG_NAT_INFO);
|
||||
if (etcp_send(conn, e) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send NAT_INFO failed"); u_free(pkt); queue_entry_free(e); }
|
||||
}
|
||||
|
||||
void topo_group_send_nat_check_req(struct ETCP_CONN* conn, uint8_t socket_id) {
|
||||
if (!conn) return;
|
||||
struct TOPOMSG_NAT_CHECK_REQ* pkt = u_calloc(1, sizeof(struct TOPOMSG_NAT_CHECK_REQ)); if (!pkt) return;
|
||||
pkt->cmd = ETCP_ID_TOPO_ENTRY; pkt->subcmd = TOPO_SUBCMD_NAT_CHECK_REQ; pkt->socket_id = socket_id;
|
||||
struct ETCP_LINK* l = conn->links; while (l) { if (l->remote_socket_id == socket_id) { struct sockaddr_in* sin = (struct sockaddr_in*)&l->conn->interface_addr; if (sin->sin_family == AF_INET) { pkt->interface_ip = sin->sin_addr.s_addr; pkt->interface_port = sin->sin_port; } break; } l = l->next; }
|
||||
struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(pkt); return; }
|
||||
e->dgram = (uint8_t*)pkt; e->len = sizeof(struct TOPOMSG_NAT_CHECK_REQ);
|
||||
if (etcp_send(conn, e) != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send NAT_CHECK_REQ failed"); u_free(pkt); queue_entry_free(e); }
|
||||
}
|
||||
|
||||
void topo_group_request_nat_check_all(struct TOPO_GROUP* group) {
|
||||
if (!group || !group->instance) return;
|
||||
int count = 0;
|
||||
struct ETCP_CONN* conn = group->instance->connections; while (conn) { struct ETCP_LINK* link = conn->links; while (link) { if (link->nat_check_status < NAT_CHECK_IN_PROGRESS) topo_group_start_link_nat_check(group, link); count++; link = link->next; } conn = conn->next; }
|
||||
}
|
||||
|
||||
void topo_group_send_withdraw(struct TOPO_GROUP* group, uint64_t node_id) {
|
||||
if (!group) return;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "node=%016llx", (unsigned long long)node_id);
|
||||
topo_group_broadcast_withdraw(group, node_id, group->instance->node_id, NULL);
|
||||
}
|
||||
Loaded 100 of 200 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user