54 Commits
Author SHA1 Message Date
Evgeny 37417ba49d Fix: my_node_name insert inside [global] section, add button tooltips in channellist 2026-07-14 09:17:01 +03:00
Evgeny cca3511503 fix: replace deprecated SHA256 API with EVP, conditional dl linkage for Windows 2026-07-14 09:07:03 +03:00
Evgeny d70cdbb040 chatgui: preserve active channel selection on reload, members sync, profile settings, member list model 2026-07-14 08:52:10 +03:00
Evgeny c3f946fb63 chat_sync: fix join group stall — remove node_id match in cs_on_conn_up, add CS_MSG_ERROR protocol response 2026-07-13 18:11:18 +03:00
jeka 93de0aa591 agents 2026-07-13 15:46:29 +03:00
Evgeny 0c2eeb8820 chatgui: validate config options on startup, refuse to start on errors 2026-07-13 15:46:20 +03:00
Evgeny 637b21da9e debug: remove console_level/file_level — simplified output routing
Remove secondary level filtering in debug_output(). Now the only filter
is debug_should_output() (per-category + global fallback). Output goes
to file (if file_output set) or console (if console_enabled), with no
additional level check.

Removed:
- debug_set_console_level(), debug_set_file_level()
- console_level, file_level fields from debug_config_t
- debug_set_level() no longer sets console_level
2026-07-13 15:38:08 +03:00
Evgeny c0935aba6c docs: update AGENTS.md and etcp_arch.md 2026-07-13 15:21:30 +03:00
Evgeny 6bf267924d add DEBUG_CATEGORY_CONNECTIVITY for handshake/protocol lifecycle logs
New category (id=25) replaces DEBUG_CATEGORY_DEBUG in chat_sync and
chat_core. This allows enabling only protocol-level logs (SEND/RECV,
timeouts, handshake) without the noise from DEBUG category.

Changes:
- debug_config.h: add CONNECTIVITY=25, COUNT→26
- debug_config.c: add 'connectivity' to g_debug_categories
- chat_sync.c: DEBUG→CONNECTIVITY in all protocol logs
- chat_core.c: DEBUG→CONNECTIVITY in connection/create logs
- utun_node.cpp: remove hardcoded DEBUG=TRACE (now config-driven)
- chatgui.cfg: connectivity=info in debug_categories
2026-07-13 15:14:48 +03:00
Evgeny 5318476cdf chat_sync: add protocol request timeouts and SEND/RECV debug logs
- Add info_req_timer (5s) for CHANNEL_INFO_REQ — reports CONN_MGR_ERR_TIMEOUT
  to GUI on timeout, clears pending invite state
- Add join_timer (8s) for CHANNEL_JOIN → WELCOME — reports timeout on failure
- cs_on_conn_down: cancel all proto timers, reset pending invite state
- cs_send: log every outgoing message (SEND <type> to=<peer> ch=<id> len=<n>)
- Dispatcher: log every incoming message (RECV <type> from=<peer> ch=<id> len=<n>)
- Add cs_msg_name() helper for readable message type names
- pending_invite_node_id/ch_id no longer cleared immediately after send —
  kept until response or timeout, enabling proper error reporting
2026-07-13 15:06:06 +03:00
Evgeny 24294b22f6 fix: use got_initial_pkt instead of new_conn for send_reset decision
new_conn is only true for the first INIT after server restart.
Subsequent INITs from same peer (recovery loop) see new_conn=0
and revert to old logic, sending 0x05 (no reset) and overwriting
the first 0x03 response.

got_initial_pkt stays 0 until client actually sends seq=1, so
server keeps sending 0x03 across all INIT retries until client
resets and starts from seq=1.
2026-07-13 14:32:16 +03:00
Evgeny 43d95016ae fix: send INIT_RESPONSE with reset when server creates new connection after restart
When server restarts, new ETCP_CONN has session_id=0 (calloc).
If client sends INIT_REQUEST_NOINIT (0x04) with session_id=0,
the condition conn->session_id != session_id is false (both 0),
so no reset was sent. Client kept old seq numbers while server
expected seq=1, causing 'Waiting for initial packet' deadlock.

Fix: add || new_conn to the condition — when server created a
fresh connection (was restarted), always send reset to client.
2026-07-13 14:19:27 +03:00
Evgeny 41e0736b75 etcp_connections: force reinit when client sends ETCP_INIT_REQUEST (reset)
Server now checks both code==ETCP_INIT_REQUEST and session_id mismatch
to decide send_reset. Previously only session_id was checked, so if
session matched (both sides had 00000000), reinit was skipped even
when client explicitly requested reset (code 0x02).

Client side already correct: handle_init_response_client:1473 calls
etcp_conn_reinit if pkt_code==ETCP_INIT_RESPONSE (0x03).
2026-07-13 13:29:58 +03:00
Evgeny 1d29cab751 etcp: INFO-level TX DATA log + Initial packet received log
- 1133: DEBUG_DEBUG -> DEBUG_INFO for TX DATA (seq, len, retry, queue lengths)
- 1437: add DEBUG_INFO when initial packet seq=1 accepted

These two INFO lines show the exact order of first sends and receives
to diagnose INIT sequencer sync without guessing.
2026-07-13 13:19:08 +03:00
Evgeny d0f5a2970f etcp_connections: improve INIT diagnostics (demote spam, add type/source/fields)
- 1556: Normal decryption failed -> DEBUG_DEBUG (expected for INIT, no more spam)
- 1581: added 'INIT X25519 OK from %s' with source address
- 1604-1617: INFO log for decrypted packet type (PING/PONG/INIT) with peer_id+src
- 1662-1668: expanded INIT accepted log with all fields:
  peer_id, mtu, link_id, socket_id, type, only_local, src_ip:src_port,
  session_id, actual src (UDP) — shows NAT mismatch
2026-07-13 11:01:50 +03:00
Evgeny 03701f525f conn_mgr: add missing sc_init_ctx before sc_set_peer_public_key
Without sc_init_ctx, crypto_ctx.initialized==0, sc_set_peer_public_key returns
SC_ERR_NOT_INITIALIZED, and all subsequent sc_encrypt (INIT, etc) fail with
'encryption failed' error 2. Fixed in cm_start_phase_direct and
reverse-connect path (both places that create ETCP_CONN for conn_mgr).
2026-07-13 10:03:15 +03:00
Evgeny 89b1a01448 invite+clipboard+conn_mgr: sock_meta for alien nodes, clipboard fix, bg_ping fix
- chat_core_connect_from_invite: add TOPO_SOCKMETA4 (id=0, UNKNOWN, UNKNOWN)
  so cm_has_direct_ip/cm_start_phase_direct can match socket_id=0 addr
- conn_mgr: cm_bg_ping_timer_cb reads real addr from nq->node->v4_addrs
  instead of NULL; added cm_bg_ping_noop_cb per required etcp_send_ping_to_socket
- invitedialog: set both QClipboard::Clipboard and Selection on Linux/X11
2026-07-13 09:37:46 +03:00
Evgeny e3b32cf4d3 conn_mgr: fix all etcp_route_send calls (dgram was NULL) + improve error diagnostics
- conn_mgr.c: 5 call sites used queue_entry_new(data_size) with data[] instead
  of dgram — all etcp_route_send calls silently dropped. Fixed by switching to
  dgram-based allocation (queue_entry_new(0)+u_malloc) matching all other callers.
- DIRCECT_REQ reuses already-allocated pkt as qe->dgram (no extra memcpy).
- etcp_router.c:1078 — 'empty entry' now shows dgram=%p len=%u dst=0x... force=%d
- etcp_connections.c:1139 — 'bad args' now shows all arg pointers to identify NULL
- etcp_connections.c:1145 — 'no socket' now shows addr_family=%d
- conn_mgr.c:633 — 'no candidates' now notes direct+reverse failed
2026-07-13 08:49:20 +03:00
Evgeny 2d93dd66c8 chatgui: fix Windows build - replace netinet/in.h with socket_compat.h 2026-07-13 08:08:27 +03:00
Evgeny 85a91a1320 chatgui: invite link connect flow + GUI_ERROR always-on diagnostics
- debug_ui.h: remove NDEBUG guard so GUI_ERROR works in Release builds
- invite_link.h/cpp: add error field to InviteData with specific failure reason
- joindialog: show exact decode error in QLabel instead of generic message
- chat_sync: implement invite connect flow (CHANNEL_INFO_REQ/RESP, JOIN, WELCOME)
- topo_node_sqlite: node/address table init and lookup
- utun_node: getInviteAddresses fallback from ETCP sockets
- minor fixes in mainwindow, chat_core, gui_bridge
2026-07-13 08:06:19 +03:00
Evgeny 102816d0dc node_config: load() returns true on parse success; main: specific diagnostics per missing field 2026-07-13 07:52:49 +03:00
Evgeny c65940f499 Copy Qt platforms/qwindows.dll plugin alongside executable 2026-07-13 07:44:06 +03:00
Evgeny 79bdafc9a7 Optimize recursive DLL resolution: scan only newly added DLLs 2026-07-13 07:43:25 +03:00
Evgeny cb1021f231 Recursive DLL dependency resolution via objdump for build.sh windeployqt fallback 2026-07-13 07:36:51 +03:00
Evgeny 58503907b5 Use objdump -p for smart DLL dependency discovery as windeployqt fallback 2026-07-13 07:33:49 +03:00
Evgeny 666507d3fe Move Qt DLL deployment from CMake to build.sh (windeployqt + manual fallback) 2026-07-13 07:30:56 +03:00
Evgeny f0f20c158b Simplify windeployqt: use PATH, add diagnostics, --no-compiler-runtime 2026-07-13 07:23:35 +03:00
Evgeny ed5ccd1ffb Add windeployqt post-build step for automatic Qt DLL deployment on Windows 2026-07-13 07:19:37 +03:00
Evgeny 65611790fa Fix enterEvent signature for Qt5/Qt6 compatibility 2026-07-13 07:06:58 +03:00
Evgeny e74240f0dd Add my_ed25519_privkey field to UTUN_INSTANCE struct 2026-07-13 07:01:42 +03:00
Evgeny e438e19f9e Add cross-platform utun_mkdir() wrapper for Windows compatibility 2026-07-13 06:44:18 +03:00
Evgeny de7841f33a chatgui: create group, unified logging, fix uasync_post epoll bug
- Add CreateGroupDialog with group name input
- Generate X25519+Ed25519 keypairs, random uint64 channel_id,
  Ed25519 signature, write to DB via uasync thread
- chat_core_create_channel(): create msg table, topo_node_sqlite_channel_put()
- GUI_EVT_CHANNEL_UPDATED notification to reload channel list
- Per-category debug config: debug_level= + debug_categories= in [gui]
- Unified logging via debug_config: all threads write to same file/console
- debug_enable_console(1) to mirror file output to stderr
- DEBUG_LEVEL_DISABLED=-1: truly disable category, NONE=0 fallback to global
- Fix uasync_post under epoll: process_posted_tasks was never called
- Fix MainWindow constructor parameter ordering (dbPath vs debugFile)
- debug_ui.h: append mode ('a' instead of 'w')
2026-07-12 17:39:20 +03:00
Evgeny a9a6b6bb10 TOPO_GROUP: add group_type support (UTUN/CHAT), mismatch detection, topo_groups_create_group()
- CHAT groups don't send subnets and skip route insert/delete
- Group type mismatch sends ERR_GROUP_MISMATCH to peer
- New topo_groups_create_group() public API for creating typed groups
2026-07-11 22:16:39 +03:00
Evgeny 3f874a80ab TOPO_GROUP: embed struct ll_entry as first member (direct queue embedding, hash index) 2026-07-09 19:50:17 +03:00
Evgeny 1f4749a1b9 Убрал имена функций из DEBUG-сообщений (__FUNCTION__ уже добавляет)
Оставлены метки-подсказки: NODEINFO, ACK_SEND, ETCP_SEND, DUMP,
router, conn_mgr, db_sync, probe, ctrl_out, socks_proxy и т.д.
2026-07-09 19:23:49 +03:00
Evgeny 28e5950d29 TOPO_BGP -> TOPO_GROUP: multi-group support
- Renamed TOPO_BGP to TOPO_GROUP with topo_group_id (64bit)
- Added TOPO_GROUPS container (ll_queue group_list) in UTUN_INSTANCE
- Memory pools moved from TOPO_GROUP to TOPO_GROUPS (instance-level)
- Default group: TOPO_GROUP_UTUN = 0x8000000000000000
- topo_groups_get_default() searches by group_id, not just first entry
- All topo_bgp_* renamed to topo_group_*; function signatures updated
- Files: topo_bgp.h/c -> topo_group.h/c
2026-07-09 18:08:49 +03:00
Evgeny 3c3d9a65d9 refactor: переименование route_node/route_bgp в topo_node/topo_bgp
Файлы: route_node.* route_node_lmdb.* route_bgp.* -> topo_node.* topo_node_lmdb.* topo_bgp.*

Структуры:
- ROUTE_BGP -> TOPO_BGP, ROUTE_BGP_CONN_ITEM -> TOPO_BGP_CONN_ITEM
- NODEINFO -> TOPO_NODE, NODEINFO_Q -> TOPO_NODEQ, NODEINFO_PATH -> TOPO_NODEPATH
- NODEINFO_ROUTES -> TOPO_NODESUBNETS, NODE_CONNECTIVITY -> TOPO_CONNECTIVITY
- wire: NODEINFO_MSG -> TOPOMSG_NODE, NODEINFO_IPV4_* -> TOPOMSG_*4 и т.д.
- memory: NI_IPV4_* -> TOPO_*4, NI_IPV6_* -> TOPO_*6
- ROUTE_SUBCMD_* -> TOPO_SUBCMD_*, ETCP_ID_ROUTE_ENTRY -> ETCP_ID_TOPO_ENTRY

Функции:
- route_bgp_* -> topo_bgp_*
- nodeinfo_* -> topo_node_*
- route_node_* -> topo_node_*
- route_node_lmdb_* -> topo_node_lmdb_*

Смысл: отделение механизма формирования топологии узлов от IP-роутинга.
2026-07-09 17:01:56 +03:00
Evgeny c7ac42eb5f route_node: refactor NODEINFO — separate protocol/memory structs, linked lists with memory_pool
- NODEINFO_MSG (protocol, packed): added flags byte + group_id field
- NODEINFO (memory): ref_count, linked list heads instead of inline arrays
- NODEINFO_ROUTES: separate struct with linked list subnet heads
- NODEINFO_Q: node*, routes*, tranzit_data*, hop_list* as separate mallocs
- 6 memory_pools in ROUTE_BGP for NI_* linked list items
- ni_list_count() universal counter via _ni_head cast
- nodeinfo_serialize/deserialize for protocol <-> memory conversion
- NODEINFO_FLAG_SEND_SUBNETS controls subnet data in protocol
- group_id defaults: utun=NODEINFO_GROUP_UTUN(1) with subnets

Fixes:
- deserialization: data+2 instead of data+sizeof(BGP_NODEINFO_PACKET)
- memset: start after ll_entry to preserve ll.size
- hop_src: subtract hop_count*8 to point at correct dynamic offset
- same-ver branch: remove_path(conn) instead of remove_path_by_hop(peer)
- double-free in route_bgp_remove_conn/process_withdraw
- conn_mgr_add_alien_node: rewritten for new structures
- all test files updated for new API
2026-07-09 16:20:48 +03:00
Evgeny 62954ca0a9 chatgui: replace MsgClient/ChatPropagator/crypto with chat_core, update DB/messaging/layout 2026-07-08 21:15:45 +03:00
Evgeny 1889d23240 metrics: RTT/loss histograms + Ed25519 signing + peer exchange via INIT handshake + file logging to stats/<peer>.dat
- etcp_metrics struct: 12-bucket RTT histogram, 11-bucket loss histogram, 10-min snapshots (>=200 pkts)
- Total counters: packets/bytes sent/rcvd, losses (retrans + probe gap detection)
- CSV append to <stats_dir>/<peer_id>.dat every 10 min
- Ed25519 pubkey exchange in INIT handshake (V3), always present
- Signed metrics exchange via ETCP_SECTION_METRICS (0x0A)
- Received metrics saved as from_<peer_id>.dat with signature hex
- on_metrics_rcvd callback in ETCP_BINDINGS
- stats_dir derived from config file path, mkdir'd at instance init
- Simplified versioning: ETCP_INIT_REQ_SIZE / ETCP_INIT_RESP_SIZE via sizeof
2026-07-07 17:16:45 +03:00
Evgeny f5c93266dc security: hardening flags + control_server fixes (F-001, F-002)
- Add --hardened flag to build.sh with FORTIFY, stack protector, PIE, RELRO
- Add --enable-hardening to configure.ac
- F-001: fix %s -> %.32s to prevent stack leak from non-null-terminated network string
- F-002: add pre-check recv_len >= MAX before buf_space computation to prevent size_t underflow
2026-07-07 07:47:37 +03:00
jeka 861d81815f gui update db and connection 2026-07-06 21:57:52 +03:00
jeka 9dafb4c31e gui update db and connection 2026-07-06 21:52:46 +03:00
Evgeny 19a4505a03 chat_sync: новый протокол синхронизации чата вместо ChatPropagator
- gui_bridge.h/cpp — кросс-поточные асинхронные коллбэки uasync↔GUI
- chat_sync.h/c — протокол синхронизации (INIT_SYNC/RESP, SEND_DATA, PUSH/ACK, SYNC_DONE)
- DbManager полностью переписан: per-channel таблицы msg_<ch> + peers_<ch>,
  4 ключа в channels, signature, protocol/rtt в node_addresses, sync-методы
- ChatPropagator удалён, заменён на chat_sync
- db_sync_stub.c — заглушки для сборки chatgui без LMDB-версии db_sync
- db_schema.md актуализирован под новую схему
2026-07-06 16:25:52 +03:00
Evgeny 46f5ff7eca test_etcp_router: fix flaky keepalive timeout under dummynet congestion
Root cause: 80kbps congestion causes 9+ second backpressure during push
phase. No packets reach server → keepalive timeout (2s default) fires
→ ETCP link down → BGP cleanup → etcp_router_conn_restart()
  - frees send_q (up to 64 = ROUTER_MAX_SEND_Q_PACKETS lost)
  - sends 9-byte restart notification to service handler (conn=NULL)
  - resets seq to 0

Fixes:
1. Set keepalive_timeout=60000, keepalive_adaptive=0 in test configs
   to prevent spurious timeouts during intentional congestion testing
2. Handle 9-byte restart notification in srv_handler:
   when conn==NULL && len==9 → reset g_expected_seq=0 gracefully
2026-07-06 12:28:54 +03:00
Evgeny 7628f77b93 chatgui: integrate UtunNode + NodeConfig, replace MsgClient
- Add NodeConfig: simplified INI config manager for embedded uTun node
  - X25519 key generation via OpenSSL EVP
  - Random node_id generation
  - addServer/removeServer, addClient/removeClient API
  - Auto-creates config with default server on first run
- Integrate UtunNode into MainWindow:
  - Creates NodeConfig in ~/.config/chatgui/node.conf
  - Starts embedded uTun node (no TUN, no msg_transport)
  - In separate thread with uasync_poll event loop
- Replace MsgClient (TCP IPC) with UtunNode (direct calls):
  - ChatPropagator now uses UtunNode for send/recv
  - send(uint64_t, QByteArray) replaces sendTo
  - messageReceived signal replaces received signal
- MainWindow cleanup: remove MsgClient, add UtunNode+NodeConfig
2026-07-06 11:59:01 +03:00
Evgeny 118ab04788 chatgui: add UtunNode C++ wrapper for embedded uTun node
- UtunNode runs uTun instance in a dedicated std::thread
- no TUN, no msg_transport — direct etcp_route_send/recv calls
- Thread-safe cross-thread communication via Qt signals
- C callback (etcp_recv_fn) stores UtunNode* via thread_local
- etcp_router_bind on service ID 0x11 for chat messages
- uasync_poll(100ms) loop runs until stop() called
2026-07-06 11:51:41 +03:00
Evgeny bfb83f7459 chatgui: add CMake build for libutun (C library linked into GUI)
- Add tools/chatgui/libutun/CMakeLists.txt — builds libuasync (from lib/*.c)
  and libutun (from src/*.c except utun.c) as static libraries
- All C code compiled as C99, linked into chatgui C++ binary
- Include paths and defs match autotools build
- chatgui CMakeLists.txt: add_subdirectory(libutun), link utun library
- Fix zxing-cpp docs/ directory issue
2026-07-06 11:48:56 +03:00
Evgeny 797c0736a7 headers: add extern "C" guards for C++ interop (chatgui)
Added #ifdef __cplusplus / extern "C" { ... } / #endif to ~65 headers
in src/ and lib/, enabling the C library to be linked into C++ code (chatgui).
Fixed packet_dump.h (added missing header guard) and etcp_bbr.h
(#pragma once guard).
2026-07-06 11:44:29 +03:00
Evgeny 8756f561be build: extract libutun.a from src/ + add db_sync module + etcp API, BBR improvements
- Add libutun.a (noinst) containing all src/*.c except utun.c
- utun binary now links libutun.a + libuasync.a
- tests/Makefile.am simplified: all tests link libutun.a instead of
  manual .o file lists (removed ~70 lines of boilerplate)
- Add db_sync module: distributed content-addressed DB with LMDB
  backend, peer-to-peer sync via etcp_router (svc 0x20)
- etcp API: etcp_send_in_order(), etcp_clear_inflight_queues(),
  verify_packet_signature(), BBR getters
- BBR: adaptive threshold (1/4 of pipe-min instead of hardcoded)
- route_bgp/etcp_connect: STCP transport support in NODEINFO
- conn_mgr: NODEINFO_ADDRS doc for nat_type/transport/proto fields
- Cleanup: removed AUDIT_FINDINGS.txt, filelist.txt, nat.txt
2026-07-06 11:42:13 +03:00
jeka 7139969acd fix: Windows/MSYS2 build compatibility
- proxy/icmp_proxy.c: define struct icmp/ip + ICMP constants for _WIN32
- lwip_tcp_priv.h: use #include <winsock2.h> instead of #define htonl/htons
- lwip_tcp.h/c: rename poll field to pollfn (avoid WSAPoll macro conflict)
- tests: guard Unix-only headers with #ifndef _WIN32, skip fork/socketpair tests
2026-07-05 20:57:44 +03:00
Evgeny 59d307f98b conn_mgr.h: document in ll_queue style; config_parser/utun_instance updates 2026-07-05 19:08:13 +03:00
Evgeny 90ad64a106 stcp: add protocol field to NODEINFO addr structs, TCP support in etcp_connect, stcp traffic test
- route_node.h: add NODE_PROTO_UDP/NODE_PROTO_TCP constants, protocol field in NODEINFO_IPV4_ADDR/NODEINFO_IPV6_ADDR
- route_node.c: fill protocol for UDP sockets + TCP servers from config, update dumps
- etcp_connect.c: create stcp_link for TCP-capable addresses, ready/close callbacks, cleanup
- etcp_connections.c: store stcp_server_listen result in instance->stcp_server, fix socket init checks for TCP-only configs
- utun_instance.c: use stcp_link_server_destroy (high-level API) instead of stcp_server_destroy
- tests/test_stcp_traffic.c: 2 nodes via etcp_connect+TCP, 1MB bidirectional traffic (10-1800 byte pkts)
2026-07-05 14:49:35 +03:00
Evgeny 4914348557 etcp_router: транзитные очереди per (src,dst) с backpressure + унификация etcp_send
- TRANSIT_QUEUE: отдельная очередь на каждую пару src+dst узлов
- ll_queue с хеш-индексом для быстрого поиска transit-очередей
- backpressure через waiter на send_input_q (threshold=0, один пакет за вызов, round-robin)
- Очередь создаётся при первом пакете который не получается отправить напрямую, удаляется при опустошении
- Унифицирован etcp_send: единый путь queue_data_put(send_input_q) для UDP (normalizer->input) и STCP (tx_queue)
- Убран STCP-ветвления из router_forward_transit/drain_cb
- Исправлено перекрытие ll.data и tq->q в TRANSIT_QUEUE (добавлены явные поля src_node_id/dst_node_id)
- Фикс лика dgram в tx_queue_cb/client_tx_queue_cb (добавлен queue_dgram_free)
- transit_queues живут внутри ETCP_CONN, инициализируются лениво, очищаются в etcp_connection_close (до pn_deinit) и stcp_link_close
2026-07-05 12:57:41 +03:00
200 changed files with 17261 additions and 6909 deletions

No files matched your search

+87 -17
View File
@@ -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.
-253
View File
@@ -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 (дубликат секции)
+1 -1
View File
@@ -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"
+18
View File
@@ -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
View File
@@ -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
View File
@@ -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
+14
View File
@@ -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
View File
@@ -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
+35
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+9
View File
@@ -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
+9
View File
@@ -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
+18
View File
@@ -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
+9
View File
@@ -32,6 +32,15 @@
#ifndef _RADIX_H_
#define _RADIX_H_
#ifdef __cplusplus
extern "C" {
#ifdef __cplusplus
}
#endif
#endif
#include <stdint.h>
#include "mem.h"
+9
View File
@@ -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
View File
@@ -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
+9
View File
@@ -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
+9
View File
@@ -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
+9
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
-219
View File
@@ -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]
+239
View File
@@ -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
View File
@@ -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 \
+22
View File
@@ -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) то узел отправляет сразу содержимое записей.
(надо додумать алгоритм, чтобы оптимизировать количество итераций - лучше передать больше данных за раз чем много итераций с ожиданием ответной стороны)
+9
View File
@@ -0,0 +1,9 @@
Задача сделать механизм маршрутизации между узлами для групповых чатов.
Узлы для всех чатов находятся в одной таблице - это nodeinfo (информация о подключении)
В другой таблице есть список узлов конкретного чата.
надо сделать возможность хранить одновременно несколько групп узлов. в группу узлов добавить тип - utun или чатовая.
если utun - в группе используется роутинг (ipv4/ipv6).
если чатовая - то только связность между узлами.
+73 -7
View File
@@ -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");
}
+11
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+2 -2
View File
@@ -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
+9
View File
@@ -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
View File
@@ -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, &timestamp, 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, &timestamp, 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);
}
+50
View File
@@ -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.
+9
View File
@@ -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
+9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+9
View File
@@ -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
+9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,
+9
View File
@@ -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
+4 -4
View File
@@ -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
View File
@@ -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
+9 -9
View File
@@ -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;
+9
View File
@@ -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
View File
@@ -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;
+11 -5
View File
@@ -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
View File
@@ -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;
+9
View File
@@ -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
+13
View File
@@ -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
View File
@@ -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) {
+9
View File
@@ -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
+23
View File
@@ -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
+9
View File
@@ -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
+9
View File
@@ -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
+7 -7
View File
@@ -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++) {
+9
View File
@@ -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
+9
View File
@@ -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
+9
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
-227
View File
@@ -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
View File
@@ -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*)&target;
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*)&target; 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*)&target;
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*)&target; 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;
+12 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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
-632
View File
@@ -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;
}
-196
View File
@@ -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
-150
View File
@@ -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;
}
-14
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+9
View File
@@ -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
View File
@@ -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);
+9
View File
@@ -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
+9
View File
@@ -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
View File
@@ -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;
+9
View File
@@ -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
View File
@@ -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);
+9
View File
@@ -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
View File
@@ -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);
+9
View File
@@ -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
+846
View File
@@ -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