Browse Source

keepalive handshake: device_type + keepalive negotiation in UDP/TCP INIT

- Добавлен device_type (1 байт, CLIENT_TYPE_*) в INIT_REQUEST и INIT_RESPONSE
- Добавлен keepalive_interval (2 байта) в INIT_RESPONSE
- STCP handshake расширен с 43 до 46 байт (device_type+keepalive)
- negotiate_keepalive(): оба desktop/server → min, иначе (есть mobile) → max
- keepalive_interval вынесен в UTUN_INSTANCE для использования в handshake
- stcp_client_connect() принимает device_type и keepalive_interval
- peer_device_type/peer_keepalive_interval передаются через stcp_link в ETCP_LINK
topo_upd
evgeny 2 months ago
parent
commit
0f1bd3d120
  1. 2
      AGENTS.md
  2. 3
      src/chat/chat_channel.c
  3. 4
      src/chat/chat_headless_control.c
  4. 5
      src/chat/chat_profile.c
  5. 31
      src/chat/chat_status.c
  6. 25
      src/routing_layer/conn_mgr_core.c
  7. 6
      src/routing_layer/conn_mgr_indirect.c
  8. 10
      src/routing_layer/conn_mgr_monitor.c
  9. 1
      src/routing_layer/conn_mgr_priv.h
  10. 4
      src/routing_layer/topo_group.c
  11. 47
      src/routing_layer/topo_node.c
  12. 4
      src/routing_layer/topo_node.h
  13. 13
      src/routing_layer/topo_recovery.c
  14. 14
      src/transport_layer/auto_socket.c
  15. 4
      src/transport_layer/etcp.c
  16. 31
      src/transport_layer/etcp_connect.c
  17. 220
      src/transport_layer/etcp_connections.c
  18. 13
      src/transport_layer/etcp_connections.h
  19. 16
      src/transport_layer/node_conn_direct.c
  20. 12
      src/transport_layer/stcp.h
  21. 27
      src/transport_layer/stcp_client.c
  22. 4
      src/transport_layer/stcp_client.h
  23. 103
      src/transport_layer/stcp_link.c
  24. 7
      src/transport_layer/stcp_link.h
  25. 18
      src/transport_layer/stcp_server.c
  26. 3
      src/utun_instance.c
  27. 6
      src/utun_instance.h
  28. 4
      tests/test_invite_group_create.c
  29. 16
      tests/test_stcp.c
  30. 6
      tools/chatgui-android/AGENTS.md
  31. 11
      tools/chatgui-android/build.sh
  32. 119
      tools/chatgui-android/jni_bridge/android_jni_bridge.c
  33. 13
      tools/chatgui-android/libutun_lite/instance_lite.c
  34. 14
      tools/chatgui/src/accountlist.cpp
  35. 2
      tools/chatgui/transport/gui_bridge.h
  36. 7
      tools/chatgui/transport/gui_bridge_impl.cpp
  37. 13
      tools/chatgui/transport/utun_node.cpp

2
AGENTS.md

@ -674,7 +674,7 @@ void lottie_animation_destroy(Lottie_Animation *anim);
Android-версия чатгуи — P2P чат на STCP (TCP), UI на Jetpack Compose (Kotlin).
C-ядро: `libutun_lite` (выборочная компиляция нужных .c из `lib/` и `src/`).
Сборка: CMake (headless, Linux) + Gradle/NDK (Android APK).
- 'fw' - собрать и обновить chatgui-andriod на телефоне
- 'fw' - собрать и обновить chatgui-android на телефоне (clean + сборка + install)
**Подробная инструкция:** `tools/chatgui-android/AGENTS.md`
**Chat-модули:** все файлы из `src/chat/` (описаны выше в секции «Chat») компилируются в `libutun_lite`.

3
src/chat/chat_channel.c

@ -86,8 +86,9 @@ static int collect_my_addrs(uint8_t* buf, int max_buf_sz) {
s = s->next;
}
{ struct ETCP_SOCKET* ts = g_cc.inst->tcp_sockets;
{ struct ETCP_SOCKET* ts = g_cc.inst->etcp_sockets;
while (ts) {
if (!ts->is_tcp) { ts = ts->next; continue; }
struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr;
if (addr && addr->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)addr;

4
src/chat/chat_headless_control.c

@ -344,6 +344,7 @@ static void hc_handle_invite(struct headless_client* cli, int id, const char* js
/* fallback: collect from sockets directly */
struct ETCP_SOCKET* s = g_hc.inst->etcp_sockets;
while (s && inv.addrCount < INVITE_ADDR_MAX) {
if (s->is_tcp) { s = s->next; continue; }
if (s->local_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&s->local_addr;
struct InviteAddr* a = &inv.addrs[inv.addrCount++];
@ -352,8 +353,9 @@ static void hc_handle_invite(struct headless_client* cli, int id, const char* js
}
s = s->next;
}
struct ETCP_SOCKET* ts = g_hc.inst->tcp_sockets;
struct ETCP_SOCKET* ts = g_hc.inst->etcp_sockets;
while (ts && inv.addrCount < INVITE_ADDR_MAX) {
if (!ts->is_tcp) { ts = ts->next; continue; }
if (ts->interface_addr.ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)&ts->interface_addr;
struct InviteAddr* a = &inv.addrs[inv.addrCount++];

5
src/chat/chat_profile.c

@ -161,9 +161,10 @@ void chat_core_sync_my_addresses(void) {
"INSERT OR REPLACE INTO node_addresses(node_id,family,protocol,address,port,addr_type,socket_id)"
" VALUES(?,?,2,?,?,?,?)", -1, &tcp_ins, NULL);
if (tcp_ins) {
struct ETCP_SOCKET* ts = g_cc.inst->tcp_sockets;
struct ETCP_SOCKET* ts = g_cc.inst->etcp_sockets;
int tcp_count = 0;
while (ts) { if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; }
while (ts) { if (!ts->is_tcp) { ts = ts->next; continue; }
if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; }
tcp_count++;
struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr;
if (!addr || !addr->ss_family) { ts = ts->next; continue; }

31
src/chat/chat_status.c

@ -102,8 +102,10 @@ static void chat_core_collect_status(void) {
while (link) {
char rtt_str[32]; rtt_str[0] = '\0';
if (link->rtt_last > 0) snprintf(rtt_str, sizeof(rtt_str), " / rtt=%ums", link->rtt_last / 10);
off += snprintf(buf + off, sizeof(buf) - off, " LINK#%d: %s /NAT=%s%s\n",
off += snprintf(buf + off, sizeof(buf) - off, " LINK#%d: %s %s %s NAT=%s%s\n",
link_idx,
link->is_tcp ? "TCP" : "UDP",
link->conn ? link->conn->name : "?",
link->link_status ? "UP" : "DOWN",
nat_type_str(link->nat_type),
rtt_str);
@ -261,7 +263,7 @@ static void collect_conn_metrics(uint64_t peer_node_id) {
while (link) {
const char* is_tcp_str = link->is_tcp ? "TCP" : "UDP";
char local_str[54], remote_str[54];
strncpy(local_str, link->conn ? sockaddr_storage_to_str(&link->conn->local_addr).str : "stcp", sizeof(local_str) - 1);
strncpy(local_str, link->conn ? (link->conn->interface_addr.ss_family ? sockaddr_storage_to_str(&link->conn->interface_addr).str : sockaddr_storage_to_str(&link->conn->local_addr).str) : "stcp", sizeof(local_str) - 1);
strncpy(remote_str, sockaddr_storage_to_str(&link->remote_addr).str, sizeof(remote_str) - 1);
off += snprintf(buf + off, sizeof(buf) - off,
"\n--- LINK#%d: %s %s NAT=%s ---\n"
@ -367,8 +369,6 @@ static void collect_member_detail(uint64_t node_id) {
uint8_t sock_count = 0;
for (struct ETCP_SOCKET* s = inst->etcp_sockets; s && sock_count < MEMBER_DETAIL_MAX_SOCKS; s = s->next)
sock_count++;
for (struct ETCP_SOCKET* ts = inst->tcp_sockets; ts && sock_count < MEMBER_DETAIL_MAX_SOCKS; ts = ts->next)
sock_count++;
size_t buf_sz = 12 + (size_t)link_count * MEMBER_DETAIL_LINK_SIZE
+ 1 + (size_t)sock_count * MEMBER_DETAIL_SOCK_SIZE;
@ -390,12 +390,19 @@ static void collect_member_detail(uint64_t node_id) {
{ uint8_t lf = 0; uint8_t la[16] = {0}; uint16_t lp = 0;
if (lk->conn) {
const struct sockaddr_storage* lsa = &lk->conn->local_addr;
const struct sockaddr_storage* lsa = lk->conn->interface_addr.ss_family ? &lk->conn->interface_addr : &lk->conn->local_addr;
if (lsa->ss_family == AF_INET) {
lf = 4; memcpy(la, &((const struct sockaddr_in*)lsa)->sin_addr, 4); lp = ntohs(((const struct sockaddr_in*)lsa)->sin_port);
} else if (lsa->ss_family == AF_INET6) {
lf = 6; memcpy(la, &((const struct sockaddr_in6*)lsa)->sin6_addr, 16); lp = ntohs(((const struct sockaddr_in6*)lsa)->sin6_port);
}
} else if (lk->is_tcp && lk->tcp_link) {
const struct sockaddr_storage* lsa = stcp_link_get_local_addr(lk->tcp_link);
if (lsa && lsa->ss_family == AF_INET) {
lf = 4; memcpy(la, &((const struct sockaddr_in*)lsa)->sin_addr, 4); lp = ntohs(((const struct sockaddr_in*)lsa)->sin_port);
} else if (lsa && lsa->ss_family == AF_INET6) {
lf = 6; memcpy(la, &((const struct sockaddr_in6*)lsa)->sin6_addr, 16); lp = ntohs(((const struct sockaddr_in6*)lsa)->sin6_port);
}
}
*p++ = lf; memcpy(p, la, 16); p += 16; memcpy(p, &lp, 2); p += 2;
}
@ -418,25 +425,13 @@ static void collect_member_detail(uint64_t node_id) {
const struct sockaddr_storage* sa = s->interface_addr.ss_family ? &s->interface_addr : &s->local_addr;
if (sa->ss_family == AF_INET) sp = ntohs(((const struct sockaddr_in*)sa)->sin_port);
else if (sa->ss_family == AF_INET6) sp = ntohs(((const struct sockaddr_in6*)sa)->sin6_port);
uint32_t lc = s->links_queue ? (uint32_t)queue_entry_count(s->links_queue) : 0;
uint32_t lc = s->is_tcp ? 0 : (s->links_queue ? (uint32_t)queue_entry_count(s->links_queue) : 0);
char ifname[38] = {0}; sock_ifname(s->name, ifname, sizeof(ifname));
memcpy(p, &sid, 4); p += 4;
memcpy(p, &sp, 2); p += 2;
memcpy(p, &lc, 4); p += 4;
memcpy(p, ifname, 38); p += 38;
}
for (struct ETCP_SOCKET* ts = inst->tcp_sockets; ts && sock_count; ts = ts->next, sock_count--) {
uint32_t sid = ts->sock_id;
uint16_t sp = 0;
const struct sockaddr_storage* sa = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr;
if (sa->ss_family == AF_INET) sp = ntohs(((const struct sockaddr_in*)sa)->sin_port);
else if (sa->ss_family == AF_INET6) sp = ntohs(((const struct sockaddr_in6*)sa)->sin6_port);
char ifname[38] = {0}; sock_ifname(ts->name, ifname, sizeof(ifname));
memcpy(p, &sid, 4); p += 4;
memcpy(p, &sp, 2); p += 2;
uint32_t lc = 0; memcpy(p, &lc, 4); p += 4;
memcpy(p, ifname, 38); p += 38;
}
chat_event_post(CHAT_EVT_MEMBER_DETAIL, buf, (int)(p - buf));
u_free(buf);

25
src/routing_layer/conn_mgr_core.c

@ -528,7 +528,7 @@ static void cm_ping_cb_impl(int success, uint16_t rtt, void* arg, uint64_t nonce
* Если ни одного линка не создалось — закрывает NCD и переходит к REVERSE/INDIRECT. */
static void cm_direct_add_links(struct ETCP_CONN* conn, struct CONN_MGR_ENTRY* entry,
struct TOPO_NODE* ni, int db_loaded) {
int any = 0;
int any = 0, v4_cnt = 0, v6_cnt = 0, v4_tcp = 0, v6_tcp = 0;
/* v4 */
if (db_loaded) {
for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) {
@ -536,8 +536,11 @@ static void cm_direct_add_links(struct ETCP_CONN* conn, struct CONN_MGR_ENTRY* e
memcpy(&sin.sin_addr.s_addr,a->addr,4); sin.sin_port=htons(a->port);
struct sockaddr_storage sa; memcpy(&sa,&sin,sizeof(sin));
if (a->protocol & TOPO_PROTO_TCP) {
struct ETCP_LINK *tlink = etcp_link_new(conn, NULL, NULL, 0);
if (tlink) { tlink->is_tcp = 1; etcp_tcp_link_start_connect(tlink, &sa, a->port); any = 1; }
struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets;
while (s) { if (s->is_tcp && s->local_addr.ss_family == AF_INET && s->type != CFG_SERVER_TYPE_PRIVATE) {
struct ETCP_LINK *tlink = etcp_link_new(conn, s, &sa, 0);
if (tlink) { tlink->is_tcp = 1; etcp_tcp_link_start_connect(tlink, &sa, a->port); any = 1; v4_tcp++; v4_cnt++; }
} s = s->next; }
}
if (a->protocol & TOPO_PROTO_UDP) {
struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets;
@ -571,17 +574,25 @@ static void cm_direct_add_links(struct ETCP_CONN* conn, struct CONN_MGR_ENTRY* e
{sin6.sin6_scope_id=sv->netif_index;break;} sv=sv->next;}
}
struct sockaddr_storage sa; memcpy(&sa,&sin6,sizeof(sin6));
struct ETCP_LINK *tlink = etcp_link_new(conn, NULL, NULL, 0);
if (tlink) { tlink->is_tcp = 1; etcp_tcp_link_start_connect(tlink, &sa, a6->port); any = 1; }
{ struct ETCP_SOCKET* s = entry->mgr->instance->etcp_sockets;
while (s) { if (s->is_tcp && s->local_addr.ss_family == AF_INET6 && s->type != CFG_SERVER_TYPE_PRIVATE) {
struct ETCP_LINK *tlink = etcp_link_new(conn, s, &sa, 0);
if (tlink) { tlink->is_tcp = 1; etcp_tcp_link_start_connect(tlink, &sa, a6->port); any = 1; v6_tcp++; v6_cnt++; }
} s = s->next; }
}
}
if (a6->protocol & TOPO_PROTO_UDP) {
uint8_t tc=cm_classify_v6_addr(a6->addr); if(tc==CM_V6_OTH) continue;
struct ETCP_SOCKET* s=entry->mgr->instance->etcp_sockets;
while(s){uint8_t sc=cm_sock_v6_classify(s);if(sc==CM_V6_ANY||sc==tc){cm_add_v6_link(conn,a6->addr,a6->port,s);any=1;} s=s->next;}
while(s){uint8_t sc=cm_sock_v6_classify(s);if(sc==CM_V6_ANY||sc==tc){cm_add_v6_link(conn,a6->addr,a6->port,s);any=1; v6_cnt++;} s=s->next;}
}
}
if (any) return;
if (any) {
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: direct_add_links node=0x%016llx db=%d v4=%d/tcp=%d v6=%d/tcp=%d conn=[%s]",
(unsigned long long)entry->node_id, db_loaded, v4_cnt, v4_tcp, v6_cnt, v6_tcp, conn->log_name);
return;
}
node_conn_direct_close(entry->ncd_handle); entry->ncd_handle=NULL;
if (db_loaded) {
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: db_node 0x%016llx direct failed (no compatible addr/socket)", (unsigned long long)entry->node_id);

6
src/routing_layer/conn_mgr_indirect.c

@ -55,7 +55,7 @@ void cm_compute_intermediaries(struct CONN_MGR_ENTRY* entry, struct CM_EXCHANGE_
}
for (uint8_t i=0;i<resp->my_count&&cnt<8;i++) {
struct TOPO_GROUP_NODE* nq=topo_node_find_by_id(entry->mgr->group,resp->my_candidates[i].node_id);
uint16_t our=nq?cm_get_node_min_rtt(nq):0; if(our==0xFFFF)our=0;
uint16_t our=nq?topo_get_chain_rtt(nq):0; if(our==0xFFFF)our=0;
all[cnt]=(typeof(all[0])){resp->my_candidates[i].node_id,(uint64_t)our+resp->my_candidates[i].rtt}; cnt++;
}
for (uint8_t i=0;i<cnt;i++) for(uint8_t j=i+1;j<cnt;j++) if(all[j].rtt<all[i].rtt){typeof(all[0])t=all[i];all[i]=all[j];all[j]=t;}
@ -65,7 +65,7 @@ void cm_compute_intermediaries(struct CONN_MGR_ENTRY* entry, struct CM_EXCHANGE_
struct CM_INTERM_SEL pkt; memset(&pkt,0,sizeof(pkt));
pkt.cmd=ETCP_RT_ID_CONN_MGR; pkt.subcmd=CM_SUBCMD_INTERM_SELECTED; pkt.count=sel;
for(uint8_t i=0;i<sel;i++){pkt.selected[i].node_id=all[i].nid;pkt.selected[i].rtt=0;
struct TOPO_GROUP_NODE* nq=topo_node_find_by_id(entry->mgr->group,all[i].nid); if(nq)pkt.selected[i].rtt=cm_get_node_min_rtt(nq);}
struct TOPO_GROUP_NODE* nq=topo_node_find_by_id(entry->mgr->group,all[i].nid); if(nq)pkt.selected[i].rtt=topo_get_chain_rtt(nq);}
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->mgr->group->group_id,entry->node_id,qe,1);}
entry->conn_type=CONN_TYPE_INDIRECT; entry->state=CONN_MGR_STATE_CONNECTED;
@ -136,7 +136,7 @@ void cm_handle_interm_exchange_req(struct ETCP_CONN* conn, struct CM_EXCHANGE_RE
for(uint8_t i=0;i<resp.your_count;i++){
resp.your_candidates[i].node_id=req->candidates[i].node_id;
struct TOPO_GROUP_NODE* nq2=topo_node_find_by_id(mgr->group,req->candidates[i].node_id);
resp.your_candidates[i].rtt=nq2?cm_get_node_min_rtt(nq2):0;
resp.your_candidates[i].rtt=nq2?topo_get_chain_rtt(nq2):0;
}
size_t sz=offsetof(struct CM_EXCHANGE_RESP,your_candidates)+(size_t)resp.your_count*sizeof(struct CONN_MGR_CANDIDATE);
struct ll_entry* qe=queue_entry_new(0);

10
src/routing_layer/conn_mgr_monitor.c

@ -14,15 +14,7 @@
#include "route_connectivity.h"
#include "route_ping.h"
/* ═══════ RTT утилиты ═══════ */
uint16_t cm_get_node_min_rtt(struct TOPO_GROUP_NODE* nq) {
uint16_t r = 0xFFFF;
if (nq->connectivity.interface_status == PROBE_RESULT_REACHABLE && nq->connectivity.interface_min_rtt < r) r = nq->connectivity.interface_min_rtt;
if (nq->connectivity.nat_status == PROBE_RESULT_REACHABLE && nq->connectivity.nat_min_rtt < r) r = nq->connectivity.nat_min_rtt;
if (nq->connectivity.real_status == PROBE_RESULT_REACHABLE && nq->connectivity.real_min_rtt < r) r = nq->connectivity.real_min_rtt;
return r;
}
/* ═══════ probe time утилиты ═══════ */
uint64_t cm_get_node_max_probe_time(struct TOPO_GROUP_NODE* nq) {
uint64_t t = nq->connectivity.interface_probe_time;

1
src/routing_layer/conn_mgr_priv.h

@ -224,7 +224,6 @@ void cm_handle_interm_selected(struct CONN_MGR* mgr, const uint8_t* data, size_t
void cm_idle_timer_cb(void* arg);
void cm_bg_ping_timer_cb(void* arg);
void cm_candidate_ping_timer_cb(void* arg);
uint16_t cm_get_node_min_rtt(struct TOPO_GROUP_NODE* nq);
uint64_t cm_get_node_max_probe_time(struct TOPO_GROUP_NODE* nq);
int cm_is_rtt_fresh(struct TOPO_GROUP_NODE* nq, uint64_t now_tb);
void cm_invite_overall_timeout(void* arg);

4
src/routing_layer/topo_group.c

@ -763,7 +763,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
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) {
uint16_t out_rtt = (uint16_t)((uint32_t)item->conn->rtt_last + incoming_cumulative_rtt);
uint16_t out_rtt = topo_get_chain_rtt(nodeinfo1);
topo_group_send_nodeinfo(group, nodeinfo1, item->conn, out_rtt);
}
}
@ -868,7 +868,7 @@ static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CON
struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e;
if (nq->node_id == group->instance->node_id) { 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)) { uint8_t _hc; uint16_t _rtt; topo_node_best_hop_list(nq, &_hc, &_rtt); topo_group_send_nodeinfo(group, nq, conn, (uint16_t)((uint32_t)conn->rtt_last + _rtt)); }
if (topo_group_should_send_to(nq, target)) topo_group_send_nodeinfo(group, nq, conn, topo_get_chain_rtt(nq));
e = e->next;
}
}

47
src/routing_layer/topo_node.c

@ -442,14 +442,27 @@ void topo_node_ping_update_rtt(struct TOPO_GROUPS* groups, uint64_t node_id, uin
}
}
uint16_t node_best_rtt(struct TOPO_GROUP_NODE* nq) {
if (!nq) return 0xFFFF;
struct TOPO_CONNECTIVITY* c = &nq->connectivity;
uint16_t best = 0x3FFF; uint8_t type = 0;
if (c->interface_status == PROBE_RESULT_REACHABLE && c->interface_min_rtt < best) { best = c->interface_min_rtt; type = 0; }
if (c->nat_status == PROBE_RESULT_REACHABLE && c->nat_min_rtt < best) { best = c->nat_min_rtt; type = 1; }
if (c->real_status == PROBE_RESULT_REACHABLE && c->real_min_rtt < best) { best = c->real_min_rtt; type = 2; }
return ((uint16_t)type << 14) | best;
uint16_t topo_get_chain_rtt(struct TOPO_GROUP_NODE* nq) {
if (!nq || !nq->paths || !nq->paths->head) {
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "chain_rtt: nid=%016llx NO PATHS", (unsigned long long)(nq ? nq->node_id : 0));
return 0xFFFF;
}
uint16_t best = 0xFFFF;
for (struct ll_entry* e = nq->paths->head; e; e = e->next) {
struct TOPO_NODEPATH* p = (struct TOPO_NODEPATH*)e;
if (!p->conn || !p->conn->links_up || !p->conn->rtt_last) {
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "chain_rtt: nid=%016llx SKIP conn=%s up=%d rtt=%u cum=%u",
(unsigned long long)nq->node_id, p->conn ? p->conn->log_name : "NULL",
p->conn ? p->conn->links_up : 0, p->conn ? p->conn->rtt_last : 0, p->cumulative_rtt);
continue;
}
uint16_t total = (uint16_t)((uint32_t)p->conn->rtt_last + p->cumulative_rtt);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "chain_rtt: nid=%016llx PATH %s rtt=%u+cum=%u=%u best=%u",
(unsigned long long)nq->node_id, p->conn->log_name, p->conn->rtt_last, p->cumulative_rtt, total, best);
if (total < best) best = total;
}
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "chain_rtt: nid=%016llx RESULT=%u(0x%04X)", (unsigned long long)nq->node_id, best, best);
return best;
}
// ===== dump / format =====
@ -656,8 +669,8 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR
if (!instance || !group) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return -1; }
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "TOPO_UPD_SELF: ENTER inst=%p nid=0x%016llx grp=%p grp_inst=%p grp_type=%d",
(void*)instance, (unsigned long long)instance->node_id, (void*)group, (void*)group->instance, group->group_type);
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "TOPO_UPD_SELF: topo_groups=%p etcp=%p tcp=%p reg=%p",
(void*)instance->topo_groups, (void*)instance->etcp_sockets, (void*)instance->tcp_sockets,
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "TOPO_UPD_SELF: topo_groups=%p etcp=%p reg=%p",
(void*)instance->topo_groups, (void*)instance->etcp_sockets,
instance->topo_groups ? (void*)instance->topo_groups->node_registry : NULL);
if (instance->topo_groups) {
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "TOPO_UPD_SELF: pools sm4=%p a4=%p sm6=%p a6=%p sub4=%p sub6=%p",
@ -683,8 +696,9 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR
else if (e_sock->local_addr.ss_family == AF_INET6) { sock6_count++; addr6_count++; }
e_sock = e_sock->next;
}
{ struct ETCP_SOCKET* ts = instance->tcp_sockets;
while (ts) { if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; }
{ struct ETCP_SOCKET* ts = instance->etcp_sockets;
while (ts) { if (!ts->is_tcp) { ts = ts->next; continue; }
if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; }
if (ts->interface_addr.ss_family == AF_INET || ts->local_addr.ss_family == AF_INET) { tcp4_count++; addr_count++; }
else if (ts->interface_addr.ss_family == AF_INET6 || ts->local_addr.ss_family == AF_INET6) { tcp6_count++; addr6_count++; }
ts = ts->next; }
@ -800,11 +814,11 @@ int topo_group_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct TOPO_GR
}
e_sock = e_sock->next;
}
{ struct ETCP_SOCKET* ts = instance->tcp_sockets;
{ struct ETCP_SOCKET* ts = instance->etcp_sockets;
int tcp_iter_cnt = 0;
while (ts) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "TOPO_UPD_SELF: tcp_iter[%d] ts=%p type=%d fam=%d next=%p",
tcp_iter_cnt, (void*)ts, ts->type, ts->local_addr.ss_family, (void*)ts->next);
tcp_iter_cnt++; if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; }
tcp_iter_cnt++; if (!ts->is_tcp) { ts = ts->next; continue; }
struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr;
if (!addr || !addr->ss_family) { ts = ts->next; continue; }
if (addr->ss_family == AF_INET) {
@ -907,8 +921,9 @@ void topo_node_update_my_addresses(struct UTUN_INSTANCE* instance) {
}
e_sock = e_sock->next;
}
{ struct ETCP_SOCKET* ts = instance->tcp_sockets;
while (ts) { if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; }
{ struct ETCP_SOCKET* ts = instance->etcp_sockets;
while (ts) { if (!ts->is_tcp) { ts = ts->next; continue; }
if (ts->type == CFG_SERVER_TYPE_PRIVATE || ts->type == CFG_SERVER_TYPE_LOCAL) { ts = ts->next; continue; }
struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr;
if (!addr || !addr->ss_family) { ts = ts->next; continue; }
if (addr->ss_family == AF_INET) {

4
src/routing_layer/topo_node.h

@ -202,9 +202,7 @@ void topo_node_dump_all(struct TOPO_GROUP* group);
int topo_node_format_all(struct TOPO_GROUP* group, char* buf, size_t buf_size);
int topo_node_ping_request_cbk(struct TOPO_GROUPS* groups, uint64_t node_id);
void topo_node_ping_update_rtt(struct TOPO_GROUPS* groups, uint64_t node_id, uint16_t rtt);
uint16_t node_best_rtt(struct TOPO_GROUP_NODE* nq);
#define NODE_RTT_TYPE(v) ((v) >> 14)
#define NODE_RTT_VALUE(v) ((v) & 0x3FFF)
uint16_t topo_get_chain_rtt(struct TOPO_GROUP_NODE* nq);
/** Build canonical message for Ed25519 signature: x25519_pubkey || name || client_type || client_activity || addresses */
#define TOPO_SIG_MSG_MAX_SIZE 2048

13
src/routing_layer/topo_recovery.c

@ -60,17 +60,6 @@ static int node_has_direct_ip(struct TOPO_GROUP* group, const struct TOPO_GROUP_
return 0;
}
/* Минимальный RTT из данных connectivity-проб (interface/nat/real). 0xFFFF если неизвестен */
static uint16_t node_get_min_rtt(const struct TOPO_GROUP_NODE* 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;
if (nq->connectivity.nat_status == PROBE_RESULT_REACHABLE && nq->connectivity.nat_min_rtt < rtt)
rtt = nq->connectivity.nat_min_rtt;
if (nq->connectivity.real_status == PROBE_RESULT_REACHABLE && nq->connectivity.real_min_rtt < rtt)
rtt = nq->connectivity.real_min_rtt;
return rtt;
}
/* Ищет незапущенный (started==0) контекст по next_hop_id */
static struct TOPO_RECOVERY_CTX* topo_recovery_find_by_next_hop(struct TOPO_GROUP* group, uint64_t next_hop_id) {
@ -188,7 +177,7 @@ void topo_recovery_add_node(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq
}
int list = node_has_direct_ip(group, nq) ? 0 : 1;
uint16_t rtt = node_get_min_rtt(nq);
uint16_t rtt = topo_get_chain_rtt(nq);
if (ctx->count[list] >= ctx->capacity[list]) {
size_t new_cap = ctx->capacity[list] * 2;

14
src/transport_layer/auto_socket.c

@ -410,8 +410,9 @@ static void remove_iface_sockets(struct AUTO_SOCKET* as, uint32_t ifindex) {
if (ifa->v4_tcp) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] removing v4 TCP socket ifidx=%u", ifindex);
{ uint16_t v4_port = load_port_from_db(as, ifname, AF_INET, AS_PROTO_TCP);
struct ETCP_SOCKET** tsp = &as->instance->tcp_sockets;
struct ETCP_SOCKET** tsp = &as->instance->etcp_sockets;
while (*tsp) {
if (!(*tsp)->is_tcp) { tsp = &(*tsp)->next; continue; }
uint16_t tsp_port = 0;
const struct sockaddr_storage* addr = (*tsp)->interface_addr.ss_family ? &(*tsp)->interface_addr : &(*tsp)->local_addr;
if (addr->ss_family == AF_INET) tsp_port = ntohs(((struct sockaddr_in*)addr)->sin_port);
@ -426,8 +427,9 @@ static void remove_iface_sockets(struct AUTO_SOCKET* as, uint32_t ifindex) {
if (ifa->v6_tcp) {
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] removing v6 TCP socket ifidx=%u", ifindex);
{ uint16_t v6_port = load_port_from_db(as, ifname, AF_INET6, AS_PROTO_TCP);
struct ETCP_SOCKET** tsp = &as->instance->tcp_sockets;
struct ETCP_SOCKET** tsp = &as->instance->etcp_sockets;
while (*tsp) {
if (!(*tsp)->is_tcp) { tsp = &(*tsp)->next; continue; }
uint16_t tsp_port = 0;
const struct sockaddr_storage* addr = (*tsp)->interface_addr.ss_family ? &(*tsp)->interface_addr : &(*tsp)->local_addr;
if (addr->ss_family == AF_INET6) tsp_port = ntohs(((struct sockaddr_in6*)addr)->sin6_port);
@ -708,8 +710,8 @@ static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] v4 TCP removed: %s ifidx=%u", ifname, ifindex);
{ char buf[IFNAMSIZ]; const char* nm = if_indextoname(ifindex, buf) ? buf : ifname;
uint16_t p = load_port_from_db(as, nm, AF_INET, AS_PROTO_TCP);
struct ETCP_SOCKET** tsp = &as->instance->tcp_sockets;
while (*tsp) { uint16_t tp = 0; const struct sockaddr_storage* a = (*tsp)->interface_addr.ss_family ? &(*tsp)->interface_addr : &(*tsp)->local_addr;
struct ETCP_SOCKET** tsp = &as->instance->etcp_sockets;
while (*tsp) { if (!(*tsp)->is_tcp) { tsp = &(*tsp)->next; continue; } uint16_t tp = 0; const struct sockaddr_storage* a = (*tsp)->interface_addr.ss_family ? &(*tsp)->interface_addr : &(*tsp)->local_addr;
if (a->ss_family == AF_INET) tp = ntohs(((struct sockaddr_in*)a)->sin_port);
if (tp == p && p > 0) { struct ETCP_SOCKET* rm = *tsp; *tsp = rm->next; u_free(rm); break; } tsp = &(*tsp)->next; }
}
@ -731,8 +733,8 @@ static void reconcile_iface(struct AUTO_SOCKET* as, uint32_t ifindex, const char
DEBUG_INFO(DEBUG_CATEGORY_AS, "[as] v6 TCP removed: %s ifidx=%u", ifname, ifindex);
{ char buf[IFNAMSIZ]; const char* nm = if_indextoname(ifindex, buf) ? buf : ifname;
uint16_t p = load_port_from_db(as, nm, AF_INET6, AS_PROTO_TCP);
struct ETCP_SOCKET** tsp = &as->instance->tcp_sockets;
while (*tsp) { uint16_t tp = 0; const struct sockaddr_storage* a = (*tsp)->interface_addr.ss_family ? &(*tsp)->interface_addr : &(*tsp)->local_addr;
struct ETCP_SOCKET** tsp = &as->instance->etcp_sockets;
while (*tsp) { if (!(*tsp)->is_tcp) { tsp = &(*tsp)->next; continue; } uint16_t tp = 0; const struct sockaddr_storage* a = (*tsp)->interface_addr.ss_family ? &(*tsp)->interface_addr : &(*tsp)->local_addr;
if (a->ss_family == AF_INET6) tp = ntohs(((struct sockaddr_in6*)a)->sin6_port);
if (tp == p && p > 0) { struct ETCP_SOCKET* rm = *tsp; *tsp = rm->next; u_free(rm); break; } tsp = &(*tsp)->next; }
}

4
src/transport_layer/etcp.c

@ -1247,6 +1247,8 @@ struct ETCP_DGRAM* etcp_request_pkt(struct ETCP_CONN* etcp) {
link->last_recv_updated=0;
if (dt<1000000) {
dgram->data[ptr++]=ETCP_SECTION_TIMESTAMP;
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "[%s] KA-TX: ts_section dt=%llu tcp=%d",
link->etcp->log_name, (unsigned long long)dt, dgram->link->is_tcp);
uint16_t t=link->last_recv_timestamp + dt;
dgram->data[ptr++]=t;
@ -1537,6 +1539,8 @@ 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;
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "[%s] KA-RTT: rtt=%u cur=%u ret=%u dlen=%u",
etcp->log_name, new_rtt, cur_ts, ret_ts, pkt->data_len);
etcp_metrics_add_rtt(etcp, new_rtt);
int recv_dt_tx1=data[3] | (data[4]<<8);// localtime удаленной стороны момента принятия пакета - timestamp этого пакета (на стороне отправителя, т.е. у нас)

31
src/transport_layer/etcp_connect.c

@ -78,16 +78,22 @@ static void connect_create_links_v4(struct ETCP_CONNECT* ctx, struct TOPO_GROUP_
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));
int addr_links = 0;
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)) link_count++; s = s->next; }
while (s) { if (s->local_addr.ss_family == AF_INET && etcp_link_new(ctx->conn, s, &sa, 0)) { link_count++; addr_links++; } s = s->next; }
}
if ((a->protocol & TOPO_PROTO_TCP)) {
struct ETCP_LINK *tlink = etcp_link_new(ctx->conn, NULL, NULL, 0);
if (tlink) { tlink->is_tcp = 1; etcp_tcp_link_start_connect(tlink, &sa, a->port); link_count++; }
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
while (s) { if (s->is_tcp && s->local_addr.ss_family == AF_INET) {
struct ETCP_LINK *tlink = etcp_link_new(ctx->conn, s, &sa, 0);
if (tlink) { tlink->is_tcp = 1; etcp_tcp_link_start_connect(tlink, &sa, a->port); link_count++; addr_links++; }
} s = s->next; }
}
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] link_v4: %d.%d.%d.%d:%d proto=%d links=%d conn=[%s]",
a->addr[0], a->addr[1], a->addr[2], a->addr[3], a->port, a->protocol, addr_links, ctx->conn->log_name);
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[etcp_connect] links_v4: addrs=%d socks=%d links=%d for node=%016llx",
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] links_v4: TOTAL addrs=%d socks=%d links=%d for node=%016llx",
addr_count, sock_count, link_count, (unsigned long long)node->node_id);
}
@ -115,8 +121,11 @@ static void connect_create_links_v6(struct ETCP_CONNECT* ctx, struct TOPO_GROUP_
{ sin6.sin6_scope_id = sv->netif_index; break; } sv = sv->next; }
}
struct sockaddr_storage sa; memcpy(&sa, &sin6, sizeof(sin6));
struct ETCP_LINK *tlink = etcp_link_new(ctx->conn, NULL, NULL, 0);
if (tlink) { tlink->is_tcp = 1; etcp_tcp_link_start_connect(tlink, &sa, a->port); }
struct ETCP_SOCKET* s = ctx->instance->etcp_sockets;
while (s) { if (s->is_tcp && s->local_addr.ss_family == AF_INET6) {
struct ETCP_LINK *tlink = etcp_link_new(ctx->conn, s, &sa, 0);
if (tlink) { tlink->is_tcp = 1; etcp_tcp_link_start_connect(tlink, &sa, a->port); }
} s = s->next; }
}
}
}
@ -152,8 +161,9 @@ static void connect_settle_timeout_cb(void* arg) {
while (link) {
struct ETCP_LINK* next = link->next;
if (!link->initialized) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] removing failed link=%p for node 0x%016llx",
(void*)link, (unsigned long long)ctx->node_id);
DEBUG_INFO(DEBUG_CATEGORY_ETCP_CONNECT, "[etcp_connect] settle: removing link=%d is_tcp=%d state=%d addr=%s for node 0x%016llx",
link->local_link_id, link->is_tcp, link->link_state,
sockaddr_storage_to_str(&link->remote_addr).str, (unsigned long long)ctx->node_id);
etcp_link_close(link);
}
link = next;
@ -173,7 +183,10 @@ static void connect_init_cb(struct ETCP_CONN* conn, int event, void* arg) { (voi
total_links++;
if (link->initialized && link->rtt_last && link->rtt_last < ctx->min_rtt)
ctx->min_rtt = link->rtt_last;
if (link->link_state == 3) up_links++;
if (link->link_state == LINK_STATE_CONNECTED) up_links++;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[etcp_connect] init_cb: link=%d state=%d init=%d is_tcp=%d rtt=%u addr=%s",
link->local_link_id, link->link_state, link->initialized, link->is_tcp, link->rtt_last,
sockaddr_storage_to_str(&link->remote_addr).str);
link = link->next;
}

220
src/transport_layer/etcp_connections.c

@ -86,7 +86,8 @@ void tcp_server_on_link(struct stcp_link *link, struct ETCP_SOCKET *tcp_sock) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "tcp_server_on_link: new ETCP_CONN peer=0x%016llx", (unsigned long long)node_id);
}
const struct sockaddr_storage* ra = stcp_link_get_remote_addr(link);
const struct sockaddr_storage* ra = stcp_link_get_peer_addr(link);
if (!ra) ra = stcp_link_get_remote_addr(link);
struct ETCP_LINK *tlink = etcp_link_new(conn, tcp_sock, ra, 1);
if (!tlink) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "tcp_server_on_link: etcp_link_new failed"); return; }
tlink->tcp_link = link;
@ -176,7 +177,7 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset, uint8_t c
*(uint64_t*)req->node_id = htobe64(link->etcp->instance->node_id);
*(uint32_t*)req->session_id = htobe32(link->etcp->session_id);
*(uint16_t*)req->mtu = htobe16(link->mtu_local);
*(uint16_t*)req->keepalive = htobe16(link->keepalive_interval);
*(uint16_t*)req->keepalive = htobe16(link->etcp->instance->keepalive_interval);
*(uint16_t*)req->recovery = htobe16(link->recovery_interval / 100);
req->link_id = link->local_link_id;
req->socket_id = link->conn ? link->conn->sock_id : 0;
@ -196,6 +197,7 @@ static void etcp_link_send_init(struct ETCP_LINK* link, uint8_t reset, uint8_t c
}
req->collision = collision;
memcpy(req->ed25519_pubkey, link->etcp->instance->my_ed25519_pubkey, SC_PUBKEY_SIZE);
req->device_type = link->etcp->instance->client_type;
size_t offset = ETCP_INIT_REQ_SIZE;
// padding
@ -269,7 +271,7 @@ static void etcp_link_init_timer_cbk(void* arg) {
}
link->init_timer = uasync_set_timeout(link->etcp->instance->ua, link->init_timeout, link, etcp_link_init_timer_cbk, "link_init");
if (link->link_state == 3 && link->initialized) { DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "[%s] init_timer: SUPPRESSED state=%d init=%d link_status=%d recv_ka=%d remote_ka=%d links_up=%d", link->etcp->log_name, link->link_state, link->initialized, link->link_status, link->recv_keepalive, link->remote_keepalive, link->etcp->links_up); return; }
if (link->link_state == LINK_STATE_CONNECTED && link->initialized) { DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "[%s] init_timer: SUPPRESSED state=%d init=%d link_status=%d recv_ka=%d remote_ka=%d links_up=%d", link->etcp->log_name, link->link_state, link->initialized, link->link_status, link->recv_keepalive, link->remote_keepalive, link->etcp->links_up); return; }
if (link->etcp->links_up > 0) { DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "[%s] init_timer: SUPPRESSED (links_up=%d > 0)", link->etcp->log_name, link->etcp->links_up); return; }
if (link->etcp->fin_wait) { DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "[%s] init_timer: SUPPRESSED (fin_wait)", link->etcp->log_name); return; }
if (link->is_tcp) { etcp_tcp_link_start_reconnect(link); return; } // TCP uses STCP handshake, not ETCP INIT
@ -288,7 +290,7 @@ void etcp_link_enter_init(struct ETCP_LINK* link) {//
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "");
if (!link) return;
int old_state = link->link_state;
link->link_state = 1; // handshake
link->link_state = LINK_STATE_HANDSHAKE; // handshake
etcp_fire_link_status_cbk(link, old_state, link->link_status);
if (link->is_server != 0) return;
etcp_link_send_init(link,1,0);// init with reset
@ -299,7 +301,7 @@ void etcp_link_enter_reinit(struct ETCP_LINK* link) {
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "");
if (!link) return;
int old_state = link->link_state;
link->link_state = 2; // reconnect
link->link_state = LINK_STATE_TRY_RECONNECT; // reconnect
etcp_fire_link_status_cbk(link, old_state, link->link_status);
etcp_on_link_down(link->etcp, link);
if (link->is_server != 0) return;
@ -315,6 +317,19 @@ void etcp_link_enter_reinit(struct ETCP_LINK* link) {
}
// Вычислить keepalive по типу устройств: оба десктоп/сервер → min, иначе (есть mobile) → max
static uint16_t negotiate_keepalive(uint8_t my_type, uint16_t my_ka,
uint8_t peer_type, uint16_t peer_ka) {
int my_lp = (my_type == CLIENT_TYPE_MOBILE);
int peer_lp = (peer_type == CLIENT_TYPE_MOBILE);
uint16_t result = (my_lp || peer_lp) ? (my_ka > peer_ka ? my_ka : peer_ka)
: (my_ka < peer_ka ? my_ka : peer_ka);
DEBUG_INFO(DEBUG_CATEGORY_KEEPALIVE, "negotiate: my=%d(type=%d) peer=%d(type=%d) → %d (rule=%s)",
my_ka, my_type, peer_ka, peer_type, result,
(my_lp || peer_lp) ? "max(low_power)" : "min(both_desktop)");
return result;
}
// Send empty keepalive packet (only timestamp, no sections)
static void etcp_link_send_keepalive(struct ETCP_LINK* link) {
DEBUG_TRACE(DEBUG_CATEGORY_KEEPALIVE, "");
@ -403,6 +418,8 @@ static void keepalive_timer_cb(void* arg) {
uint64_t now = get_time_tb();
uint64_t timeout_units = (uint64_t)link->keepalive_timeout * 10; // ms -> 0.1ms units
uint64_t elapsed = now - link->last_recv_local_time;
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[%s] ka_check: recv=%d elapsed=%llu timeout=%llu",
link->etcp->log_name, link->recv_keepalive, (unsigned long long)elapsed, (unsigned long long)timeout_units);
if (elapsed > timeout_units) {
if (link->recv_keepalive != 0) {
@ -835,13 +852,15 @@ void etcp_socket_remove(struct ETCP_SOCKET* conn) {
}
struct ll_entry* entry;
while ((entry = conn->links_queue->head) != NULL) {
struct link_queue_entry* lqe = (struct link_queue_entry*)entry->data;
etcp_link_close(lqe->link);
if (conn->links_queue) {
struct ll_entry* entry;
while ((entry = conn->links_queue->head) != NULL) {
struct link_queue_entry* lqe = (struct link_queue_entry*)entry->data;
etcp_link_close(lqe->link);
}
queue_free(conn->links_queue);
conn->links_queue = NULL;
}
queue_free(conn->links_queue);
conn->links_queue = NULL;
u_free(conn);
}
@ -925,8 +944,8 @@ struct ETCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SE
}
}
ts->next = instance->tcp_sockets;
instance->tcp_sockets = ts;
ts->next = instance->etcp_sockets;
instance->etcp_sockets = ts;
{ char loc_str[64] = "none", if_str[64] = "none";
if (ts->local_addr.ss_family) snprintf(loc_str, sizeof(loc_str), "%s", sockaddr_storage_to_str(&ts->local_addr).str);
if (ts->interface_addr.ss_family) snprintf(if_str, sizeof(if_str), "%s", sockaddr_storage_to_str(&ts->interface_addr).str);
@ -938,7 +957,7 @@ struct ETCP_SOCKET* tcp_socket_add(struct UTUN_INSTANCE* instance, struct CFG_SE
void tcp_socket_remove(struct ETCP_SOCKET* sock) {
if (!sock || !sock->instance) return;
struct UTUN_INSTANCE* inst = sock->instance;
struct ETCP_SOCKET** pp = &inst->tcp_sockets;
struct ETCP_SOCKET** pp = &inst->etcp_sockets;
while (*pp && *pp != sock) pp = &(*pp)->next;
if (*pp) *pp = sock->next;
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "tcp_socket_remove: %s sock_id=%u", sock->name, sock->sock_id);
@ -1116,6 +1135,7 @@ void etcp_link_close(struct ETCP_LINK* link) {
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[%s] Link %d closed: addr=%s rcvd=%zub ack=%llub infl=%ub/%upkt",
link->etcp->log_name, link->local_link_id, sockaddr_storage_to_str(&link->remote_addr).str, link->total_decrypted,
(unsigned long long)link->acked_bytes, link->inflight_bytes, link->inflight_packets);
etcp_fire_link_status_cbk(link, link->link_state, link->link_status);
u_free(link->bbr);
u_free(link);
}
@ -1141,10 +1161,17 @@ void etcp_link_enter_ready_tcp(struct ETCP_LINK *link) {
etcp_conn_reinit(etcp, "tcp session changed");
}
etcp->session_id = peer_sid;
link->peer_device_type = stcp_link_get_peer_device_type(link->tcp_link);
{ uint16_t peer_ka = stcp_link_get_peer_keepalive_interval(link->tcp_link);
link->keepalive_interval = negotiate_keepalive(etcp->instance->client_type,
etcp->instance->keepalive_interval, link->peer_device_type, peer_ka); }
}
link->initialized = 1; link->link_state = 3; link->link_status = 1;
int old_state = link->link_state;
link->initialized = 1; link->link_state = LINK_STATE_CONNECTED; link->link_status = 1;
link->recv_keepalive = 1;
link->last_recv_local_time = get_time_tb();
if (!link->mtu_remote) link->mtu_remote = link->mtu;
etcp->got_initial_pkt = 1;
etcp->reset_done = 1;
@ -1154,6 +1181,7 @@ void etcp_link_enter_ready_tcp(struct ETCP_LINK *link) {
etcp_link_send_keepalive(link);
start_keepalive_timer(link);
loadbalancer_link_ready(link);
etcp_fire_link_status_cbk(link, old_state, link->link_status);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TCP link %d UP (mtu=%d init=%d up=%d tcp_links=%d)",
etcp->log_name, link->local_link_id, link->mtu,
etcp->initialized, etcp->links_up, etcp->tcp_link_count);
@ -1168,8 +1196,15 @@ static void tcp_link_reconnect_cb(void *arg) {
if (link->tcp_reconnect_delay_ms == 0) link->tcp_reconnect_delay_ms = 1000;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TCP link %d reconnect attempt (delay=%ums)", link->etcp->log_name, link->local_link_id, link->tcp_reconnect_delay_ms);
uint16_t port = ntohs(((struct sockaddr_in *)&link->remote_addr)->sin_port);
struct sockaddr_storage bind_addr; const struct sockaddr_storage *laddr = NULL;
if (link->conn && link->conn->interface_addr.ss_family) {
memcpy(&bind_addr, &link->conn->interface_addr, sizeof(bind_addr));
if (bind_addr.ss_family == AF_INET6) ((struct sockaddr_in6*)&bind_addr)->sin6_port = 0;
else ((struct sockaddr_in*)&bind_addr)->sin_port = 0;
laddr = &bind_addr;
}
struct stcp_link_config tcp_cfg = {.ua = link->etcp->instance->ua, .my_keys = &link->etcp->instance->my_keys, .inst = link->etcp->instance, .peer_pubkey = link->etcp->crypto_ctx.peer_public_key,
.peer_pubkey_mode = 0, .remote_addr = &link->remote_addr, .remote_port = port, .got_initial_pkt = link->etcp->got_initial_pkt, .session_id = link->etcp->session_id};
.peer_pubkey_mode = 0, .remote_addr = &link->remote_addr, .remote_port = port, .local_addr = laddr, .got_initial_pkt = link->etcp->got_initial_pkt, .session_id = link->etcp->session_id};
struct stcp_link *sl = stcp_link_connect(&tcp_cfg);
if (!sl) { link->tcp_reconnect_delay_ms *= 2; if (link->tcp_reconnect_delay_ms > 30000) link->tcp_reconnect_delay_ms = 30000;
link->tcp_reconnect_timer = uasync_set_timeout(link->etcp->instance->ua, (int)(link->tcp_reconnect_delay_ms * 10), link, tcp_link_reconnect_cb, "tcp_rct"); return; }
@ -1179,13 +1214,23 @@ static void tcp_link_reconnect_cb(void *arg) {
void etcp_tcp_link_start_connect(struct ETCP_LINK *link, struct sockaddr_storage *addr, uint16_t port) {
if (!link || !link->etcp || !addr) return;
if (!link->conn) { for (struct ETCP_SOCKET *s = link->etcp->instance->etcp_sockets; s; s = s->next) { if (s->is_tcp) { link->conn = s; break; } }
if (!link->conn) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] no TCP socket in instance, cannot start TCP link %d", link->etcp->log_name, link->local_link_id); return; } }
memcpy(&link->remote_addr, addr, sizeof(*addr));
struct sockaddr_storage bind_addr; const struct sockaddr_storage *laddr = NULL;
if (link->conn->interface_addr.ss_family) {
memcpy(&bind_addr, &link->conn->interface_addr, sizeof(bind_addr));
if (bind_addr.ss_family == AF_INET6) ((struct sockaddr_in6*)&bind_addr)->sin6_port = 0;
else ((struct sockaddr_in*)&bind_addr)->sin_port = 0;
laddr = &bind_addr;
}
struct stcp_link_config tcp_cfg = {
.ua = link->etcp->instance->ua,
.my_keys = &link->etcp->instance->my_keys,
.inst = link->etcp->instance,
.peer_pubkey = link->etcp->crypto_ctx.peer_public_key,
.peer_pubkey_mode = 0, .remote_addr = &link->remote_addr, .remote_port = port,
.local_addr = laddr,
.got_initial_pkt = link->etcp->got_initial_pkt,
.session_id = link->etcp->session_id};
struct stcp_link *sl = stcp_link_connect(&tcp_cfg);
@ -1201,7 +1246,7 @@ void etcp_tcp_link_start_reconnect(struct ETCP_LINK *link) {
if (link->etcp->state == 2) return;
if (link->tcp_reconnect_timer) return;
if (link->tcp_link) { stcp_link_close(link->tcp_link); link->tcp_link = NULL; }
link->link_state = 1;
link->link_state = LINK_STATE_HANDSHAKE;
if (link->etcp->tcp_link_count > 0) link->etcp->tcp_link_count--;
link->tcp_reconnect_timer = uasync_set_timeout(link->etcp->instance->ua, (int)(link->tcp_reconnect_delay_ms * 10), link, tcp_link_reconnect_cb, "tcp_rct");
}
@ -1210,9 +1255,12 @@ static void tcp_link_close_cb(struct stcp_link *sl, int err, void *arg) {
struct ETCP_LINK *link = (struct ETCP_LINK *)arg;
if (!link || !link->etcp) return;
if (link->etcp->state == 2) return;
int old_state = link->link_state;
stcp_link_close(sl);
link->tcp_link = NULL;
etcp_fire_link_status_cbk(link, old_state, 0);
if (link->is_server) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TCP server link %d down err=%d, closing", link->etcp->log_name, link->local_link_id, err);
etcp_on_link_down(link->etcp, link);
@ -1229,13 +1277,21 @@ static int etcp_tcp_send(struct ETCP_DGRAM* dgram) {
if (!link->tcp_link || !stcp_link_is_ready(link->tcp_link)) return -1;
link->pkt_sent_since_keepalive = 1;
dgram->flag_up = 1;
dgram->timestamp = get_current_timestamp();
size_t total_len = 3 + dgram->data_len;
uint8_t *buf = u_malloc(total_len);
if (!buf) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "[%s] TCP send: malloc %zu failed", link->etcp->log_name, total_len); return -1; }
memcpy(buf, &dgram->timestamp, 2);
buf[2] = dgram->flag_up;
if (dgram->data_len) memcpy(buf + 3, dgram->data, dgram->data_len);
int rc;
if (link->send_hook) rc = (int)link->send_hook(0, dgram->data, dgram->data_len, NULL, 0, link, link->send_hook_ctx);
else if (link->tcp_link) rc = stcp_link_send(link->tcp_link, dgram->data, dgram->data_len) == 0 ? (int)dgram->data_len : -1;
if (link->send_hook) rc = (int)link->send_hook(0, buf, total_len, NULL, 0, link, link->send_hook_ctx);
else if (link->tcp_link) rc = stcp_link_send(link->tcp_link, buf, total_len) == 0 ? (int)dgram->data_len : -1;
else rc = -1;
u_free(buf);
if (rc > 0) link->total_encrypted += (size_t)rc;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] TCP send: link=%d dlen=%d rc=%d via=%s",
link->etcp->log_name, link->local_link_id, dgram->data_len, rc,
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] TCP send: link=%d total=%zu dlen=%d rc=%d via=%s",
link->etcp->log_name, link->local_link_id, total_len, dgram->data_len, rc,
link->send_hook ? "hook" : (link->tcp_link ? "stcp" : "NONE"));
return rc;
}
@ -1686,6 +1742,8 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk
}
}
memcpy(resp->ed25519_pubkey, e_sock->instance->my_ed25519_pubkey, SC_PUBKEY_SIZE);
resp->device_type = e_sock->instance->client_type;
*(uint16_t*)resp->keepalive = htobe16(e_sock->instance->keepalive_interval);
pkt->noencrypt_len=0;
pkt->link=link;
link->recv_keepalive = 1;
@ -1718,7 +1776,7 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk
memory_pool_free(e_sock->instance->pkt_pool, pkt);
link->initialized = 1;
{ int old_state = link->link_state; link->link_state = 3; etcp_fire_link_status_cbk(link, old_state, link->link_status); }
{ int old_state = link->link_state; link->link_state = LINK_STATE_CONNECTED; etcp_fire_link_status_cbk(link, old_state, link->link_status); }
if (link->init_timer) {
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer);
link->init_timer = NULL;
@ -1735,6 +1793,7 @@ static void send_init_response(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pk
}
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 (!e_sock) return -1;
if (pkt_len < ETCP_INIT_RESP_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "INIT_RESPONSE too short: pkt_len=%zu", pkt_len);
return 46;
@ -1817,12 +1876,16 @@ static int handle_init_response_client(struct ETCP_SOCKET* e_sock, struct ETCP_D
memcpy(link->etcp->peer_ed25519_pubkey, resp->ed25519_pubkey, SC_PUBKEY_SIZE);
DEBUG_DEBUG(DEBUG_CATEGORY_CRYPTO, "[%s] Received Ed25519 pubkey from peer", link->etcp->log_name);
link->peer_device_type = resp->device_type;
{ uint16_t server_ka = (resp->keepalive[0]<<8) | resp->keepalive[1];
link->keepalive_interval = negotiate_keepalive(link->etcp->instance->client_type,
link->etcp->instance->keepalive_interval, resp->device_type, server_ka); }
link->etcp->peer_node_id = server_node_id;
etcp_update_log_name(link->etcp);
link->initialized = 1;// получен init response (client)
{ int old_state = link->link_state; link->link_state = 3; etcp_fire_link_status_cbk(link, old_state, link->link_status); }
{ int old_state = link->link_state; link->link_state = LINK_STATE_CONNECTED; etcp_fire_link_status_cbk(link, old_state, link->link_status); }
if (link->init_timer) {
uasync_cancel_timeout(link->etcp->instance->ua, link->init_timer);
link->init_timer = NULL;
@ -1911,7 +1974,9 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
recv_len, (unsigned long long)link->etcp->crypto_ctx.rx_counter);
sc_status_t dec_rc = sc_decrypt(&link->etcp->crypto_ctx, data, recv_len, (uint8_t*)&pkt->timestamp, &pkt_len);
if (!dec_rc) {
goto process_decrypted;
int ec = etcp_packet_decrypted(e_sock, pkt, link, pkt_len);
if (ec) { errorcode = ec; goto ec_fr; }
return;
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp: DECRYPT FAIL on existing link. rc=%d link=%p log=%s from=%s sess=%d link_state=%d keepalive=%d enc_errs=%zu my_pub=%016llx peer_pub=%016llx seskey=%02x%02x%02x%02x",
dec_rc, link, link->etcp->log_name, sockaddr_storage_to_str(&addr).str, link->etcp->crypto_ctx.session_ready,
@ -2122,32 +2187,32 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
struct ETCP_LINK* existing_link = etcp_link_find_by_remote_id(conn, req->link_id);
if (!existing_link) {
existing_link = etcp_link_find_by_addr(e_sock, &addr);
if (existing_link && existing_link->etcp == conn) {
if (memcmp(conn->crypto_ctx.peer_public_key, sc.peer_public_key, SC_PUBKEY_SIZE)) {
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] link address match but pubkey mismatch, firing node_changed", conn->log_name);
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "pubkey mismatch on reused link for node %016llx", (unsigned long long)peer_id);
conn->callbacks_running = 1;
etcp_cbk_fire(conn, ETCP_CBK_EVENT_NODE_CHANGED);
conn->callbacks_running = 0;
if (existing_link && existing_link->etcp == conn) {
if (memcmp(conn->crypto_ctx.peer_public_key, sc.peer_public_key, SC_PUBKEY_SIZE)) {
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] link address match but pubkey mismatch, firing node_changed", conn->log_name);
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "pubkey mismatch on reused link for node %016llx", (unsigned long long)peer_id);
conn->callbacks_running = 1;
etcp_cbk_fire(conn, ETCP_CBK_EVENT_NODE_CHANGED);
conn->callbacks_running = 0;
errorcode = 67;
goto ec_fr;
}
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] found existing outbound link by addr for incoming INIT, reusing link=%p",
conn->log_name, existing_link);
} else if (existing_link) {
struct ETCP_CONN* old_conn = existing_link->etcp;
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] conflicting link at %s belongs to %s, firing node_changed",
conn->log_name, sockaddr_storage_to_str(&addr).str, old_conn->log_name);
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "link address conflict for node %016llx, firing node_changed callbacks on %s",
(unsigned long long)peer_id, old_conn->log_name);
old_conn->callbacks_running = 1;
etcp_cbk_fire(old_conn, ETCP_CBK_EVENT_NODE_CHANGED);
old_conn->callbacks_running = 0;
errorcode = 67;
goto ec_fr;
} else {
existing_link = NULL;
}
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] found existing outbound link by addr for incoming INIT, reusing link=%p",
conn->log_name, existing_link);
} else if (existing_link) {
struct ETCP_CONN* old_conn = existing_link->etcp;
DEBUG_WARN(DEBUG_CATEGORY_CONNECTION, "[%s] conflicting link at %s belongs to %s, firing node_changed",
conn->log_name, sockaddr_storage_to_str(&addr).str, old_conn->log_name);
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "link address conflict for node %016llx, firing node_changed callbacks on %s",
(unsigned long long)peer_id, old_conn->log_name);
old_conn->callbacks_running = 1;
etcp_cbk_fire(old_conn, ETCP_CBK_EVENT_NODE_CHANGED);
old_conn->callbacks_running = 0;
errorcode = 67;
goto ec_fr;
} else {
existing_link = NULL;
}
}
uint8_t send_reset = 0;
@ -2256,7 +2321,9 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
etcp_conn_reinit(conn, "session changed");
} }
}
link->keepalive_interval=(req->keepalive[0]<<8) | req->keepalive[1];
uint16_t client_ka = (req->keepalive[0]<<8) | req->keepalive[1];
link->peer_device_type = req->device_type;
link->keepalive_interval = negotiate_keepalive(e_sock->instance->client_type, e_sock->instance->keepalive_interval, req->device_type, client_ka);
link->recovery_interval=((req->recovery[0]<<8) | req->recovery[1])*100;// timebase в link, timebase/100 в кодограмме
if (link->keepalive_interval < 10) link->keepalive_interval = 10;
DEBUG_DEBUG(DEBUG_CATEGORY_KEEPALIVE, "set keepalive for link=%d", link->keepalive_interval);
@ -2276,16 +2343,29 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) {
return;
process_decrypted:
ec_fr:
e_sock->pkt_format_errors++;
if (e_sock->pkt_format_errors < 3 || (e_sock->pkt_format_errors % 500 == 0))
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "error %d, from %s (count=%zu)", 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);
return;
}
int etcp_packet_decrypted(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
struct ETCP_LINK* link, size_t pkt_len) {
DEBUG_TRACE(DEBUG_CATEGORY_ETCP, "Decrypt ok - normal pkt");
if (pkt_len<3) { errorcode=46; DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "decrypted packet too small, size=%zu", pkt_len); goto ec_fr; }
if (pkt_len<3) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "decrypted packet too small, size=%zu", pkt_len); return 46; }
pkt->data_len=pkt_len-3;
pkt->noencrypt_len=0;
pkt->link=link;
if (pkt->data_len && pkt->data[0] != ETCP_KEEPALIVE) {
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[%s] decrypt: code=%02x dlen=%u plen=%zu recv=%d sock=%p",
link->etcp->log_name, pkt->data[0], pkt->data_len, pkt_len,
link->recv_keepalive, (void*)e_sock);
}
link->remote_keepalive = pkt->flag_up;
/* restore recv_keepalive BEFORE computing link_status = remote && local */
if (link->recv_keepalive != 1) {
link->recv_keepalive = 1;
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[%s] Link %d recv_keepalive restored to 1 (was 0) link_status=%d remote_ka=%d state=%d init=%d links_up=%d",
@ -2297,19 +2377,16 @@ process_decrypted:
if (link->link_status != was_up)
etcp_fire_link_status_cbk(link, link->link_state, was_up);
if (link->link_status && !was_up && link->initialized) {
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[%s] Link %d status popped UP: recv_ka=%d remote_ka=%d state=%d init=%d → calling loadbalancer_link_ready", link->etcp->log_name, link->local_link_id, link->recv_keepalive, link->remote_keepalive, link->link_state, link->initialized);
DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "[%s] Link %d status popped UP: recv_ka=%d remote_ka=%d state=%d init=%d", link->etcp->log_name, link->local_link_id, link->recv_keepalive, link->remote_keepalive, link->link_state, link->initialized);
loadbalancer_link_ready(link);
}
link->last_recv_local_time=get_time_tb();
link->last_recv_timestamp=pkt->timestamp;
link->last_recv_updated=1;
// Count decrypted bytes
link->total_decrypted += pkt->data_len;
size_t offset = 0;
uint8_t pkt_code = pkt->data[offset++];
uint8_t pkt_code = pkt->data[0];
if (pkt_code == ETCP_KEEPALIVE) {
if (pkt->data_len >= 3) {
@ -2318,40 +2395,29 @@ process_decrypted:
}
link->keepalive_recv_count++;
memory_pool_free(e_sock->instance->pkt_pool, pkt);
return; // KA handled, nothing more to process
return 0;
}
if (pkt_code == ETCP_INIT_RESPONSE || pkt_code == ETCP_INIT_RESPONSE_NOINIT) {
int ret = handle_init_response_client(e_sock, pkt, link, pkt_code, pkt_len);
if (ret) { errorcode = ret; goto ec_fr; }
return;
if (ret) return ret;
return 0;
}
if (link->link_state == 2) {// из recovery получен нормальный пакет - восстанавливаем линк в нормальный режим
if (link->link_state == LINK_STATE_TRY_RECONNECT) {// 0 - just init, 1 - handshake, 2 - try reconnect, 3 - connected
start_keepalive_timer(link);
etcp_link_send_keepalive(link); // Start keepalive timer
{ int old_state = link->link_state; link->link_state = 3; etcp_fire_link_status_cbk(link, old_state, link->link_status); }
etcp_link_send_keepalive(link);
int old_state = link->link_state; link->link_state = LINK_STATE_CONNECTED;
etcp_fire_link_status_cbk(link, old_state, link->link_status);
}
// log_dump("RECV decrypted:", pkt->data, pkt->data_len, link);
if (link->link_state == 3) {
if (link->link_state == LINK_STATE_CONNECTED) {
if (memory_pool_is_freed(e_sock->instance->pkt_pool, 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);
} else memory_pool_free(e_sock->instance->pkt_pool, pkt);
return;
ec_fr:
e_sock->pkt_format_errors++;
if (e_sock->pkt_format_errors < 3 || (e_sock->pkt_format_errors % 500 == 0))
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "error %d, from %s (count=%zu)", 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);
return;
return 0;
}
// Initialize only sockets (servers for incoming connections)

13
src/transport_layer/etcp_connections.h

@ -85,6 +85,8 @@ struct ETCP_INIT_REQUEST_PKT {
uint8_t src_port[2]; // 27: client interface_addr port (big-endian)
uint8_t collision; // 29: 1 = cross-connect, remote claims master
uint8_t ed25519_pubkey[SC_PUBKEY_SIZE]; // 30: client Ed25519 pubkey (32 bytes)
// V4 fields:
uint8_t device_type; // 62: CLIENT_TYPE_SERVER/DESKTOP/MOBILE
} __attribute__((packed));
#define ETCP_INIT_REQ_SIZE sizeof(struct ETCP_INIT_REQUEST_PKT)
@ -103,6 +105,9 @@ struct ETCP_INIT_RESPONSE_PKT {
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)
// V4 fields:
uint8_t device_type; // 57: CLIENT_TYPE_SERVER/DESKTOP/MOBILE
uint8_t keepalive[2]; // 58: server keepalive interval (big-endian)
} __attribute__((packed));
#define ETCP_INIT_RESP_SIZE sizeof(struct ETCP_INIT_RESPONSE_PKT)
@ -178,6 +183,10 @@ struct ETCP_SOCKET {
#define NAT_VERIFIED_DIRECT 7 // real public IP, no NAT
#define LINK_STATE_HANDSHAKE 1
#define LINK_STATE_TRY_RECONNECT 2
#define LINK_STATE_CONNECTED 3
// Тип функции-перехватчика отправки UDP.
// Если link->send_hook != NULL, etcp_udp_send вызывает её вместо socket_sendto.
struct ETCP_LINK;
@ -270,6 +279,7 @@ struct ETCP_LINK {
uint8_t pkt_sent_since_keepalive; // Флаг: был ли отправлен пакет с последнего keepalive тика
uint32_t keepalive_sent_count; // Счётчик отправленных keepalive
uint32_t keepalive_recv_count; // Счётчик полученных keepalive
uint8_t peer_device_type; // CLIENT_TYPE_* удалённой стороны (из handshake)
uint16_t handshake_minsize; // минимальный размер udp при handshake
uint16_t handshake_maxsize; // мax размер udp при handshake (выбирает рандом)
@ -361,6 +371,9 @@ void etcp_link_enter_ready_tcp(struct ETCP_LINK *link);
void etcp_tcp_link_start_connect(struct ETCP_LINK *link, struct sockaddr_storage *addr, uint16_t port);
void etcp_tcp_link_start_reconnect(struct ETCP_LINK *link);
int etcp_packet_decrypted(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt,
struct ETCP_LINK* link, size_t pkt_len);
int etcp_send_ping(struct UTUN_INSTANCE* instance, const uint8_t* peer_pubkey_bin,
const struct sockaddr_storage* addr, int timeout_ms,
etcp_ping_callback_t cb, void* user_arg,

16
src/transport_layer/node_conn_direct.c

@ -129,7 +129,7 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
struct ETCP_SOCKET* specific_sock) {
struct ETCP_CONN* conn = entry->conn;
struct UTUN_INSTANCE* inst = conn->instance;
int link_count = 0;
int link_count = 0, v4_skip = 0, v6_skip = 0, v4_addrs = 0, v6_addrs = 0;
uint64_t nid = entry->node_id;
/* ---- IPv4 ---- */
@ -148,7 +148,8 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
if (sock_count > 0) {
int rr = 0;
for (const struct TOPO_ADDR4* a = ni->v4_addrs; a; a = a->next) {
if (a->port == 0 || !(a->protocol & TOPO_PROTO_UDP)) { DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[ncd] v4 skip proto=%02x port=%d node=0x%016llx", a->protocol, (int)a->port, (unsigned long long)nid); continue; }
v4_addrs++;
if (a->port == 0 || !(a->protocol & TOPO_PROTO_UDP)) { DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[ncd] v4 skip proto=%02x port=%d node=0x%016llx", a->protocol, (int)a->port, (unsigned long long)nid); v4_skip++; continue; }
int zero = 1; for (int j = 0; j < 4; j++) if (a->addr[j] != 0) { zero = 0; break; }
if (zero) { DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[ncd] v4 skip zero-addr node=0x%016llx", (unsigned long long)nid); continue; }
struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET;
@ -198,7 +199,8 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
if (sock_count > 0) {
int rr = 0;
for (const struct TOPO_ADDR6* a = ni->v6_addrs; a; a = a->next) {
if (a->port == 0 || !(a->protocol & TOPO_PROTO_UDP)) { DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[ncd] v6 skip proto=%02x port=%d node=0x%016llx", a->protocol, (int)a->port, (unsigned long long)nid); continue; }
v6_addrs++;
if (a->port == 0 || !(a->protocol & TOPO_PROTO_UDP)) { DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[ncd] v6 skip proto=%02x port=%d node=0x%016llx", a->protocol, (int)a->port, (unsigned long long)nid); v6_skip++; continue; }
int zero = 1; for (int j = 0; j < 16; j++) if (a->addr[j] != 0) { zero = 0; break; }
if (zero) { DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "[ncd] v6 skip zero-addr node=0x%016llx", (unsigned long long)nid); continue; }
struct sockaddr_in6 sin6; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6;
@ -230,6 +232,10 @@ static int ncd_create_links(struct ncd_entry* entry, struct TOPO_NODE* ni,
}
}
if (link_count == 0) {
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] create_links: node=0x%016llx v4=%d/skp=%d v6=%d/skp=%d → %d UDP links (TCP handled by cm_direct_add_links)",
(unsigned long long)nid, v4_addrs, v4_skip, v6_addrs, v6_skip, link_count);
}
return link_count;
}
@ -636,7 +642,7 @@ int node_conn_direct_open(struct UTUN_INSTANCE* inst, uint64_t node_id,
int link_count = ncd_create_links(entry, ni, specific_sock);
if (link_count == 0)
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] no links created for node=0x%016llx", (unsigned long long)node_id);
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] no links created (UDP only, TCP added by conn_mgr) node=0x%016llx", (unsigned long long)node_id);
DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "[ncd] connect timer SET: node=0x%016llx value_tb=%u now_tb=%llu path=NEW_node_conn_direct_open",
(unsigned long long)node_id, inst->etcp_connect_timeout_tb, (unsigned long long)get_time_tb());
@ -778,7 +784,7 @@ int node_conn_direct_open_node(struct UTUN_INSTANCE* inst, uint64_t node_id,
int link_count = ncd_create_links(entry, ni, specific_sock);
if (link_count == 0)
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] open_node no links created for node=0x%016llx", (unsigned long long)node_id);
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] open_node (UDP only, TCP added by conn_mgr) node=0x%016llx links=%d", (unsigned long long)node_id, link_count);
DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "[ncd] connect timer SET: node=0x%016llx value_tb=%u now_tb=%llu path=NEW_node_conn_direct_open_node",
(unsigned long long)node_id, inst->etcp_connect_timeout_tb, (unsigned long long)get_time_tb());

12
src/transport_layer/stcp.h

@ -23,10 +23,10 @@ struct UTUN_INSTANCE;
#define STCP_HS_TIMEOUT 50000 // 5s in 0.1ms timebase units
#define STCP_CONNECT_TIMEOUT 100000 // 10s in 0.1ms timebase units
#define STCP_HS_CLIENT_MIN (SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT) // 72+43=115
#define STCP_HS_SERVER_MIN (SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER) // 72+43=115
#define STCP_HS_ENC_CLIENT 43 // ed25519_pubkey(32) + got_initial_pkt(1) + session_id(4) + padding_size(2) + CRC32(4)
#define STCP_HS_ENC_SERVER 43 // ed25519_pubkey(32) + got_initial_pkt(1) + session_id(4) + padding_size(2) + CRC32(4)
#define STCP_HS_CLIENT_MIN (SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT) // 72+46=118
#define STCP_HS_SERVER_MIN (SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER) // 72+46=118
#define STCP_HS_ENC_CLIENT 46 // ed25519_pubkey(32) + got_initial_pkt(1) + session_id(4) + padding_size(2) + device_type(1) + keepalive(2) + CRC32(4)
#define STCP_HS_ENC_SERVER 46 // ed25519_pubkey(32) + got_initial_pkt(1) + session_id(4) + padding_size(2) + device_type(1) + keepalive(2) + CRC32(4)
#define STCP_STREAM_CLIENT_SEND 0
#define STCP_STREAM_SERVER_SEND 1
@ -76,6 +76,10 @@ struct stcp_conn {
uint8_t peer_got_initial_pkt; // remote, received from peer during handshake
uint32_t session_id; // local, sent to peer during handshake
uint32_t peer_session_id; // remote, received from peer during handshake
uint8_t device_type; // CLIENT_TYPE_* sent to peer during handshake
uint16_t keepalive_interval; // keepalive interval sent to peer during handshake
uint8_t peer_device_type; // CLIENT_TYPE_* from peer handshake
uint16_t peer_keepalive_interval; // keepalive interval from peer handshake
struct ll_queue *rx_queue;
struct ll_queue *tx_queue;

27
src/transport_layer/stcp_client.c

@ -54,14 +54,16 @@ static void client_send_handshake(struct stcp_conn *c, const uint8_t *server_pub
memcpy(hs, salt, SC_PUBKEY_ENC_SALT_SIZE);
sc_obfuscate_pubkey(salt, server_pubkey, c->my_keys.public_key, hs + SC_PUBKEY_ENC_SALT_SIZE);
uint8_t plain[39]; memcpy(plain, my_ed25519, 32);
uint8_t plain[42]; memcpy(plain, my_ed25519, 32);
plain[32] = c->got_initial_pkt;
memcpy(plain + 33, &c->session_id, 4);
plain[37] = (uint8_t)padding; plain[38] = (uint8_t)(padding >> 8);
uint32_t crc = crc32_calc(plain, 39);
plain[39] = c->device_type;
*(uint16_t*)(plain + 40) = htobe16(c->keepalive_interval);
uint32_t crc = crc32_calc(plain, 42);
uint8_t *enc_dst = hs + SC_PUBKEY_ENC_SIZE;
memcpy(enc_dst, plain, 39);
enc_dst[39] = (uint8_t)(crc >> 0); enc_dst[40] = (uint8_t)(crc >> 8); enc_dst[41] = (uint8_t)(crc >> 16); enc_dst[42] = (uint8_t)(crc >> 24);
memcpy(enc_dst, plain, 42);
enc_dst[42] = (uint8_t)(crc >> 0); enc_dst[43] = (uint8_t)(crc >> 8); enc_dst[44] = (uint8_t)(crc >> 16); enc_dst[45] = (uint8_t)(crc >> 24);
if (sc_stream_xor(&c->stream_send, enc_dst, STCP_HS_ENC_CLIENT) != SC_OK) { u_free(hs); stcp_conn_do_close(c, 2); return; }
for (int i = 0; i < padding; i++) hs[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT + i] = (uint8_t)(salt[0] ^ i);
@ -94,8 +96,10 @@ static void client_hs_cb(struct stcp_conn *c, uint8_t *data, size_t len) {
c->peer_got_initial_pkt = enc_hs[32];
memcpy(&c->peer_session_id, enc_hs + 33, 4);
uint16_t padding_size = (uint16_t)enc_hs[37] | ((uint16_t)enc_hs[38] << 8);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_client: server response OK gop=%d sid=%08x padding=%u",
c->peer_got_initial_pkt, c->peer_session_id, padding_size);
c->peer_device_type = enc_hs[39];
c->peer_keepalive_interval = ((uint16_t)enc_hs[40] << 8) | enc_hs[41];
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_client: server response OK gop=%d sid=%08x padding=%u dev=%d ka=%u",
c->peer_got_initial_pkt, c->peer_session_id, padding_size, c->peer_device_type, c->peer_keepalive_interval);
stcp_recv_set(c, padding_size, 0, client_hs_padding_cb);
}
@ -145,8 +149,10 @@ struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uin
struct SC_MYKEYS *keys, const uint8_t *peer_pubkey,
const uint8_t *my_ed25519_pubkey,
uint8_t got_initial_pkt, uint32_t session_id,
uint8_t device_type, uint16_t keepalive_interval,
stcp_ready_cb ready_cb, void *arg,
stcp_close_cb close_cb, void *close_arg) {
stcp_close_cb close_cb, void *close_arg,
const struct sockaddr_storage *local_addr) {
if (!ua || !addr || !keys || !peer_pubkey || !ready_cb) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "invalid args"); return NULL; }
struct stcp_client *cli = u_calloc(1, sizeof(struct stcp_client));
if (!cli) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "calloc failed"); return NULL; }
@ -161,6 +167,8 @@ struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uin
c->tx_cb = stcp_tx_queue_cb;
c->got_initial_pkt = got_initial_pkt;
c->session_id = session_id;
c->device_type = device_type;
c->keepalive_interval = keepalive_interval;
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
@ -174,6 +182,11 @@ struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uin
c->sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (c->sock == SOCKET_INVALID) { freeaddrinfo(res); u_free(cli); return NULL; }
socket_set_nonblocking(c->sock);
if (local_addr && local_addr->ss_family) {
if (bind(c->sock, (const struct sockaddr*)local_addr, local_addr->ss_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0)
DEBUG_WARN(DEBUG_CATEGORY_ETCP, "stcp_client bind to %s port=0 failed err=%d(%s), continuing to %s:%u",
sockaddr_storage_to_str(local_addr).str, socket_get_error(), socket_strerror(socket_get_error()), addr, port);
}
int conn_ret = connect(c->sock, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
if (conn_ret < 0) {

4
src/transport_layer/stcp_client.h

@ -16,8 +16,10 @@ struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uin
struct SC_MYKEYS *keys, const uint8_t *peer_pubkey,
const uint8_t *my_ed25519_pubkey,
uint8_t got_initial_pkt, uint32_t session_id,
uint8_t device_type, uint16_t keepalive_interval,
stcp_ready_cb ready_cb, void *arg,
stcp_close_cb close_cb, void *close_arg);
stcp_close_cb close_cb, void *close_arg,
const struct sockaddr_storage *local_addr);
void stcp_client_destroy(struct stcp_client *cli);
struct stcp_conn *stcp_client_get_conn(struct stcp_client *cli);

103
src/transport_layer/stcp_link.c

@ -46,6 +46,8 @@ struct stcp_link {
uint8_t peer_got_initial_pkt; // received from peer during handshake
uint32_t peer_session_id; // received from peer during handshake
uint8_t peer_device_type; // CLIENT_TYPE_* from peer handshake
uint16_t peer_keepalive_interval; // keepalive interval from peer handshake
};
// ====== rx dispatch ======
@ -56,31 +58,31 @@ static void link_rx_cb(struct ll_queue *q, void *arg) {
if (!e) { queue_resume_callback(q); return; }
if (link->etcp_conn && link->etcp_link && link->cfg.inst) {
if (e->len >= 1 && e->dgram[0] == ETCP_KEEPALIVE) {
struct ETCP_LINK *l = link->etcp_link;
if (e->len >= 3) { uint16_t pp = e->dgram[1] | ((uint16_t)e->dgram[2] << 8); l->keepalive_timeout = (uint32_t)pp * KA_TIMEOUT_MULT; }
l->recv_keepalive = 1; l->remote_keepalive = 1; l->link_status = 1; l->keepalive_recv_count++;
l->total_decrypted += e->len;
l->last_recv_local_time = get_time_tb();
queue_dgram_free(e); queue_entry_free(e);
} else {
if (e->len > PACKET_DATA_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_link rx oversized pkt: %u > %u, closing link=%p", e->len, (unsigned)PACKET_DATA_SIZE, (void*)link);
queue_dgram_free(e); queue_entry_free(e);
queue_resume_callback(q);
if (link->conn) stcp_conn_do_close(link->conn, EMSGSIZE);
return;
}
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_link rx → etcp_conn_input len=%zu link=%p", e->len, (void*)link->etcp_link);
struct ETCP_DGRAM *pkt = memory_pool_alloc(link->cfg.inst->pkt_pool);
if (pkt) {
pkt->link = link->etcp_link; pkt->data_len = (uint16_t)e->len; pkt->noencrypt_len = 0;
if (e->len > 0) memcpy(pkt->data, e->dgram, e->len);
link->etcp_link->total_decrypted += e->len;
etcp_conn_input(pkt);
}
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "[%s] stcp_rx: len=%u hdr=%02x%02x%02x sock=%p",
link->etcp_conn->log_name, e->len,
e->dgram && e->len>=3 ? e->dgram[0] : 0,
e->dgram && e->len>=3 ? e->dgram[1] : 0,
e->dgram && e->len>=3 ? e->dgram[2] : 0,
(void*)link->etcp_link->conn);
if (e->len > PACKET_DATA_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_link rx oversized pkt: %u > %u, closing link=%p", e->len, (unsigned)PACKET_DATA_SIZE, (void*)link);
queue_dgram_free(e); queue_entry_free(e);
queue_resume_callback(q);
if (link->conn) stcp_conn_do_close(link->conn, EMSGSIZE);
return;
}
if (e->len < 3) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_link rx too short: %u bytes", e->len); queue_dgram_free(e); queue_entry_free(e); queue_resume_callback(q); return; }
struct ETCP_DGRAM *pkt = memory_pool_alloc(link->cfg.inst->pkt_pool);
if (!pkt) { queue_dgram_free(e); queue_entry_free(e); queue_resume_callback(q); return; }
pkt->timestamp = ((uint16_t*)e->dgram)[0];
pkt->flag_up = e->dgram[2] & 1;
if (e->len > 3) memcpy(pkt->data, e->dgram + 3, e->len - 3);
else memset(pkt->data, 0, 1);
pkt->link = link->etcp_link;
link->etcp_link->total_decrypted += e->len;
struct ETCP_SOCKET *sock = link->etcp_link->conn;
etcp_packet_decrypted(sock, pkt, link->etcp_link, e->len);
queue_dgram_free(e); queue_entry_free(e);
} else {
struct UTUN_INSTANCE *inst = link->cfg.inst;
uint8_t id = (e->dgram && e->len > 0) ? e->dgram[0] : 0;
@ -115,6 +117,8 @@ static void server_accept_cb(struct stcp_conn *conn, void *arg) {
link->conn = conn;
link->peer_got_initial_pkt = conn->peer_got_initial_pkt;
link->peer_session_id = conn->peer_session_id;
link->peer_device_type = conn->peer_device_type;
link->peer_keepalive_interval = conn->peer_keepalive_interval;
stcp_conn_set_on_close(conn, stcp_link_on_stcp_close, link);
@ -140,6 +144,8 @@ static void client_ready_cb(struct stcp_conn *conn, void *arg) {
link->conn = conn;
link->peer_got_initial_pkt = conn->peer_got_initial_pkt;
link->peer_session_id = conn->peer_session_id;
link->peer_device_type = conn->peer_device_type;
link->peer_keepalive_interval = conn->peer_keepalive_interval;
stcp_conn_set_on_close(conn, stcp_link_on_stcp_close, link);
@ -152,8 +158,18 @@ static void client_ready_cb(struct stcp_conn *conn, void *arg) {
queue_set_waiter_defer(link->tx_queue, 1);
stcp_conn_set_tx_queue(conn, link->tx_queue);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_link: client handshake OK, link=%p etcp_link=%p",
(void*)link, (void*)link->etcp_link);
if (link->cfg.remote_addr) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_link: client handshake OK [%s] link=%d addr=%s gop=%d sid=%08x",
link->etcp_conn ? link->etcp_conn->log_name : "?",
link->etcp_link ? link->etcp_link->local_link_id : -1,
sockaddr_storage_to_str(link->cfg.remote_addr).str,
link->peer_got_initial_pkt, link->peer_session_id);
} else {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_link: client handshake OK [%s] link=%d addr=? gop=%d sid=%08x",
link->etcp_conn ? link->etcp_conn->log_name : "?",
link->etcp_link ? link->etcp_link->local_link_id : -1,
link->peer_got_initial_pkt, link->peer_session_id);
}
if (link->etcp_link) etcp_link_enter_ready_tcp(link->etcp_link);
if (link->on_ready_cb) link->on_ready_cb(link, link->ready_arg);
}
@ -220,11 +236,14 @@ struct stcp_link *stcp_link_connect(struct stcp_link_config *cfg) {
link->cli = stcp_client_connect(cfg->ua, addr_str, port, cfg->my_keys, pubkey,
cfg->inst ? cfg->inst->my_ed25519_pubkey : NULL,
cfg->got_initial_pkt, cfg->session_id,
client_ready_cb, link, NULL, NULL);
cfg->inst ? cfg->inst->client_type : 0,
cfg->inst ? cfg->inst->keepalive_interval : 200,
client_ready_cb, link, NULL, NULL, cfg->local_addr);
if (!link->cli) { u_free(link); return NULL; }
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_link: connecting to %s:%u pubkey=%016llx",
addr_str, port, (unsigned long long)*(const uint64_t*)pubkey);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_link: connecting to %s:%u pubkey=%016llx bind=%s",
addr_str, port, (unsigned long long)*(const uint64_t*)pubkey,
cfg->local_addr ? sockaddr_storage_to_str(cfg->local_addr).str : "auto");
return link;
}
@ -305,6 +324,26 @@ const struct sockaddr_storage *stcp_link_get_remote_addr(struct stcp_link *link)
return link && link->cfg.remote_addr ? link->cfg.remote_addr : NULL;
}
static struct sockaddr_storage g_peer_addr;
const struct sockaddr_storage *stcp_link_get_peer_addr(struct stcp_link *link) {
if (!link || !link->conn || link->conn->sock == SOCKET_INVALID) return NULL;
memset(&g_peer_addr, 0, sizeof(g_peer_addr));
socklen_t slen = sizeof(g_peer_addr);
if (getpeername(link->conn->sock, (struct sockaddr*)&g_peer_addr, &slen) == 0)
return &g_peer_addr;
return NULL;
}
static struct sockaddr_storage g_local_addr;
const struct sockaddr_storage *stcp_link_get_local_addr(struct stcp_link *link) {
if (!link || !link->conn || link->conn->sock == SOCKET_INVALID) return NULL;
memset(&g_local_addr, 0, sizeof(g_local_addr));
socklen_t slen = sizeof(g_local_addr);
if (getsockname(link->conn->sock, (struct sockaddr*)&g_local_addr, &slen) == 0)
return &g_local_addr;
return NULL;
}
void stcp_link_set_on_ready(struct stcp_link *link, stcp_link_cb cb, void *arg) {
if (!link) return;
link->on_ready_cb = cb;
@ -333,6 +372,14 @@ uint32_t stcp_link_get_peer_session_id(struct stcp_link *link) {
return link ? link->peer_session_id : 0;
}
uint8_t stcp_link_get_peer_device_type(struct stcp_link *link) {
return link ? link->peer_device_type : 0;
}
uint16_t stcp_link_get_peer_keepalive_interval(struct stcp_link *link) {
return link ? link->peer_keepalive_interval : 0;
}
void stcp_server_list_add(struct UTUN_INSTANCE *inst, struct stcp_server *srv) {
if (!inst || !srv) return;
srv->next = inst->stcp_servers;

7
src/transport_layer/stcp_link.h

@ -27,7 +27,8 @@ struct stcp_link_config {
struct UTUN_INSTANCE *inst; // для диспатча через api_bindings
const uint8_t *peer_pubkey; // pubkey пира (клиент)
int peer_pubkey_mode; // 0=binary, 1=hex
const struct sockaddr_storage *remote_addr; // адрес пира (клиент)
const struct sockaddr_storage *local_addr; // локальный адрес/интерфейс для bind (клиент, NULL=авто)
const struct sockaddr_storage *remote_addr; // адрес пира (клиент)
uint16_t remote_port; // порт пира (клиент)
int listen_family; // AF_INET или AF_INET6 для сервера (0 = v4)
uint8_t got_initial_pkt; // моё значение, отправляется пиру при handshake
@ -59,6 +60,8 @@ struct ETCP_CONN *stcp_link_get_etcp_conn(struct stcp_link *link);
void stcp_link_set_etcp_conn(struct stcp_link *link, struct ETCP_CONN *conn);
void stcp_link_set_etcp_link(struct stcp_link *link, struct ETCP_LINK *elink);
const struct sockaddr_storage *stcp_link_get_remote_addr(struct stcp_link *link);
const struct sockaddr_storage *stcp_link_get_peer_addr(struct stcp_link *link); /* getpeername(conn->sock) */
const struct sockaddr_storage *stcp_link_get_local_addr(struct stcp_link *link); /* getsockname(conn->sock) */
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);
@ -67,6 +70,8 @@ const uint8_t *stcp_link_get_peer_pubkey(struct stcp_link *link);
const uint8_t *stcp_link_get_peer_ed25519_pubkey(struct stcp_link *link);
uint8_t stcp_link_get_peer_got_initial_pkt(struct stcp_link *link);
uint32_t stcp_link_get_peer_session_id(struct stcp_link *link);
uint8_t stcp_link_get_peer_device_type(struct stcp_link *link);
uint16_t stcp_link_get_peer_keepalive_interval(struct stcp_link *link);
#ifdef __cplusplus

18
src/transport_layer/stcp_server.c

@ -78,8 +78,10 @@ static void server_hs_phase1_cb(struct stcp_conn *c, uint8_t *data, size_t len)
c->peer_got_initial_pkt = enc_hs[32];
memcpy(&c->peer_session_id, enc_hs + 33, 4);
uint16_t padding_size = (uint16_t)enc_hs[37] | ((uint16_t)enc_hs[38] << 8);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: client handshake OK gop=%d sid=%08x padding=%u",
c->peer_got_initial_pkt, c->peer_session_id, padding_size);
c->peer_device_type = enc_hs[39];
c->peer_keepalive_interval = ((uint16_t)enc_hs[40] << 8) | enc_hs[41];
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: client handshake OK gop=%d sid=%08x padding=%u dev=%d ka=%u",
c->peer_got_initial_pkt, c->peer_session_id, padding_size, c->peer_device_type, c->peer_keepalive_interval);
stcp_recv_set(c, padding_size, 0, server_hs_phase2_cb);
}
@ -112,14 +114,16 @@ static void server_hs_phase2_cb(struct stcp_conn *c, uint8_t *data, size_t len)
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);
uint8_t plain_hs[39]; memcpy(plain_hs, c->my_ed25519_pubkey, SC_PUBKEY_SIZE);
uint8_t plain_hs[42]; memcpy(plain_hs, c->my_ed25519_pubkey, SC_PUBKEY_SIZE);
plain_hs[32] = server_gop;
memcpy(plain_hs + 33, &server_sid, 4);
plain_hs[37] = (uint8_t)padding; plain_hs[38] = (uint8_t)(padding >> 8);
uint32_t crc = crc32_calc(plain_hs, 39);
plain_hs[39] = c->device_type;
*(uint16_t*)(plain_hs + 40) = htobe16(c->keepalive_interval);
uint32_t crc = crc32_calc(plain_hs, 42);
uint8_t *enc_dst = resp + SC_PUBKEY_ENC_SIZE;
memcpy(enc_dst, plain_hs, 39);
enc_dst[39] = (uint8_t)(crc >> 0); enc_dst[40] = (uint8_t)(crc >> 8); enc_dst[41] = (uint8_t)(crc >> 16); enc_dst[42] = (uint8_t)(crc >> 24);
memcpy(enc_dst, plain_hs, 42);
enc_dst[42] = (uint8_t)(crc >> 0); enc_dst[43] = (uint8_t)(crc >> 8); enc_dst[44] = (uint8_t)(crc >> 16); enc_dst[45] = (uint8_t)(crc >> 24);
log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_server hs_resp BEFORE xor", enc_dst, STCP_HS_ENC_SERVER);
if (sc_stream_xor(&c->stream_send, enc_dst, STCP_HS_ENC_SERVER) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "encrypt failed");
@ -179,6 +183,8 @@ static void server_accept_cb(socket_t listen_sock, void *arg) {
c->close_arg = srv->close_arg;
c->socket_id = uasync_add_socket_t(srv->ua, cli_sock, server_conn_read_cb, stcp_write_cb, NULL, c);
c->inst = srv->inst;
c->device_type = srv->inst ? srv->inst->client_type : 0;
c->keepalive_interval = srv->inst ? srv->inst->keepalive_interval : 200;
stcp_recv_set(c, SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT, 0, server_hs_phase1_cb);
c->hs_timer = uasync_set_timeout(c->ua, STCP_HS_TIMEOUT, c, hs_timeout_cb, "stcp_hs");
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: accepted connection fd=%d", (int)cli_sock);

3
src/utun_instance.c

@ -101,6 +101,7 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
// Set client type
instance->client_type = config->global.client_type;
instance->keepalive_interval = (uint16_t)(config->global.keepalive_interval > 0 ? config->global.keepalive_interval : 200);
instance->client_activity = (instance->client_type == CLIENT_TYPE_SERVER) ? CLIENT_ACTIVITY_ACTIVE : CLIENT_ACTIVITY_STANDBY;
instance->client_activity_timer = NULL;
@ -512,7 +513,7 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
if (tc) DEBUG_INFO(DEBUG_CATEGORY_SYS, "[DESTROY] J: closed %d TCP links", tc);
}
queue_free(instance->tcp_connections); instance->tcp_connections = NULL;
while (instance->tcp_sockets) tcp_socket_remove(instance->tcp_sockets);
/* TCP sockets already cleaned in Phase G via etcp_sockets */
uasync_drain_immediate(instance->ua);
DEBUG_INFO(DEBUG_CATEGORY_SYS, "[DESTROY] J done — TCP");

6
src/utun_instance.h

@ -134,9 +134,8 @@ struct UTUN_INSTANCE {
struct memory_pool* pkt_pool;
struct memory_pool* ack_pool;
// Active sockets (UDP)
struct ETCP_SOCKET* etcp_sockets;// linked-list (UDP + TCP is_tcp=1)
struct ETCP_SOCKET* tcp_sockets; // linked-list (TCP, filtered by is_tcp=1)
// Active sockets (UDP + TCP, is_tcp=1 flag)
struct ETCP_SOCKET* etcp_sockets;// linked-list (UDP + TCP)
struct stcp_server *stcp_servers; // TCP servers linked list (via stcp_link.c)
void* socket_monitor; // SOCKET_MONITOR* (opaque, transport_layer/socket_monitor.c)
void* auto_socket_state; // AUTO_SOCKET* (opaque, transport_layer/auto_socket.c)
@ -187,6 +186,7 @@ struct UTUN_INSTANCE {
struct broadcast_ctx* broadcast; // Broadcast protocol instance handler (ETCP_ID_BROADCAST dispatcher)
uint8_t client_type; // CLIENT_TYPE_SERVER/DESKTOP/MOBILE (from config)
uint16_t keepalive_interval; // желаемый keepalive (ms), из конфига. для handshake
uint8_t client_activity; // CLIENT_ACTIVITY_STANDBY/ACTIVE
void* client_activity_timer; // uasync timer handle for inactivity timeout

4
tests/test_invite_group_create.c

@ -97,10 +97,10 @@ int main(void) {
sin.sin_port = htons((uint16_t)55555);
memcpy(&ts->local_addr, &sin, sizeof(sin));
ts->interface_addr = ts->local_addr;
ts->next = a->tcp_sockets; a->tcp_sockets = ts;
ts->next = a->etcp_sockets; a->etcp_sockets = ts;
fprintf(stderr, "Added TCP socket ts=%p next=%p\n", (void*)ts, (void*)ts->next); fflush(stderr);
}
fprintf(stderr, "tcp_sockets=%p etcp_sockets=%p\n", (void*)a->tcp_sockets, (void*)a->etcp_sockets); fflush(stderr);
fprintf(stderr, "etcp_sockets=%p\n", (void*)a->etcp_sockets); fflush(stderr);
/* Get B's node_id and pubkey from its instance */
struct UTUN_INSTANCE* b = utun_instance_create(ua, cb);

16
tests/test_stcp.c

@ -104,7 +104,7 @@ static int test1_sizes(void) {
uint16_t port = BASE_PORT + 1;
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, NULL, NULL, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, 0, 200, client_ready_cb, &cli, peer_close_cb, &cli, NULL); TASSERT(sc);
size_t sizes[] = {0, 1, 16, 17, 255, 256, 1000, 65535};
int n_sizes = 8;
@ -144,7 +144,7 @@ static int test2_many(void) {
uint16_t port = BASE_PORT + 2;
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, NULL, NULL, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, 0, 200, client_ready_cb, &cli, peer_close_cb, &cli, NULL); TASSERT(sc);
int sent = 0, ticks = 0;
while (srv.msg_count < 200 && ticks < 200) {
@ -185,7 +185,7 @@ static int test3_wrong_key(void) {
struct SC_MYKEYS rogue;
TASSERT(sc_generate_keypair(&rogue) == SC_OK);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, rogue.public_key, NULL, 0, 0, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, rogue.public_key, NULL, 0, 0, 0, 200, client_ready_cb, &cli, peer_close_cb, &cli, NULL); TASSERT(sc);
int ticks = 0;
while (ticks < 200) {
@ -210,7 +210,7 @@ static int test4_close(void) {
uint16_t port = BASE_PORT + 4;
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, NULL, NULL, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, 0, 200, client_ready_cb, &cli, peer_close_cb, &cli, NULL); TASSERT(sc);
int closed = 0, ticks = 0;
while (!srv.closed && ticks < 200) {
@ -260,7 +260,7 @@ static int test5_multi(void) {
struct stcp_client *clients[NCLI] = {0};
for (int i = 0; i < NCLI; i++) {
clients[i] = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, client_ready_cb, &clip[i], peer_close_cb, &clip[i]);
clients[i] = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, 0, 200, client_ready_cb, &clip[i], peer_close_cb, &clip[i], NULL);
TASSERT(clients[i]);
}
@ -309,7 +309,7 @@ static int test6_interleaved(void) {
uint16_t port = BASE_PORT + 6;
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, NULL, NULL, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, 0, 200, client_ready_cb, &cli, peer_close_cb, &cli, NULL); TASSERT(sc);
int round = 0, ticks = 0;
while (srv.msg_count < 50 || cli.msg_count < 50) {
@ -343,7 +343,7 @@ static int test7_bulk_4mb(void) {
uint16_t port = BASE_PORT + 7;
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, NULL, NULL, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, 0, 200, client_ready_cb, &cli, peer_close_cb, &cli, NULL); TASSERT(sc);
#define N_BULK 64
#define SZ_BULK 65535
@ -382,7 +382,7 @@ static int test8_srv_recv_close(void) {
uint16_t port = BASE_PORT + 8;
struct stcp_server *ss = stcp_server_create(ua, port, &s_keys, NULL, NULL, server_connect_cb, &srv, peer_close_cb, &srv, AF_INET); TASSERT(ss);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, client_ready_cb, &cli, peer_close_cb, &cli); TASSERT(sc);
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", port, &c_keys, s_keys.public_key, NULL, 0, 0, 0, 200, client_ready_cb, &cli, peer_close_cb, &cli, NULL); TASSERT(sc);
int ticks = 0;
while ((!srv.ready || !cli.ready) && ticks < 200) { uasync_poll(ua, 10); ticks++; }

6
tools/chatgui-android/AGENTS.md

@ -138,9 +138,9 @@ sdkmanager "platforms;android-36" "build-tools;36.0.0" "ndk;29.0.14206865"
## Сборка
```
./build.sh # APK
./build.sh install # сборка + прошивка на подключённый телефон
./build.sh install <SN> # на конкретный девайс
./build.sh # clean + сборка + прошивка на подключённый телефон
./build.sh noinstall # только сборка, без прошивки
./build.sh <SN> # clean + сборка + прошивка на конкретный девайс
```
### APK (Android)

11
tools/chatgui-android/build.sh

@ -4,6 +4,7 @@ cd "$(dirname "$0")"
export ANDROID_HOME="${ANDROID_HOME:-/home/user/android}"
echo "Building..."
TMP=$(mktemp)
./gradlew clean > /dev/null 2>&1
if ! ./gradlew assembleDebug > "$TMP" 2>&1; then
cat "$TMP"
rm -f "$TMP"
@ -11,9 +12,9 @@ if ! ./gradlew assembleDebug > "$TMP" 2>&1; then
fi
rm -f "$TMP"
echo "Build OK"
if [ "$1" = "install" ]; then
DEV=${2:-$(adb devices 2>/dev/null | awk 'NR==2{print $1}')}
ADB=adb; [ -n "$DEV" ] && ADB="adb -s $DEV"
$ADB install -r app/build/outputs/apk/debug/app-debug.apk
echo "Install OK"
if [ "$1" != "noinstall" ]; then
DEV=${1:-$(adb devices 2>/dev/null | awk 'NR==2{print $1}')}
ADB=adb; [ -n "$DEV" ] && ADB="adb -s $DEV"
$ADB install -r app/build/outputs/apk/debug/app-debug.apk
echo "Install OK"
fi

119
tools/chatgui-android/jni_bridge/android_jni_bridge.c

@ -233,6 +233,7 @@ static int bridge_collect_invite_addrs(struct InviteAddrC* out, int max_cnt) {
int cnt = 0;
struct ETCP_SOCKET* s = g_cc.inst->etcp_sockets;
while (s) {
if (s->is_tcp) { s = s->next; continue; }
struct sockaddr_storage* sa = s->interface_addr.ss_family ? &s->interface_addr : &s->local_addr;
if (sa && sa->ss_family == AF_INET && cnt < max_cnt) {
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
@ -247,21 +248,22 @@ static int bridge_collect_invite_addrs(struct InviteAddrC* out, int max_cnt) {
}
s = s->next;
}
{ struct ETCP_SOCKET* ts = g_cc.inst->tcp_sockets;
while (ts) {
struct sockaddr_storage* sa = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr;
{ struct ETCP_SOCKET* s2 = g_cc.inst->etcp_sockets;
while (s2) {
if (!s2->is_tcp) { s2 = s2->next; continue; }
struct sockaddr_storage* sa = s2->interface_addr.ss_family ? &s2->interface_addr : &s2->local_addr;
if (sa && sa->ss_family == AF_INET && cnt < max_cnt) {
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
out[cnt].family = 4; out[cnt].socketId = ts->sock_id; out[cnt].proto = INVITE_PROTO_TCP;
out[cnt].family = 4; out[cnt].socketId = s2->sock_id; out[cnt].proto = INVITE_PROTO_TCP;
memcpy(out[cnt].address, &sin->sin_addr, 4);
out[cnt].port = ntohs(sin->sin_port); cnt++;
} else if (sa && sa->ss_family == AF_INET6 && cnt < max_cnt) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa;
out[cnt].family = 6; out[cnt].socketId = ts->sock_id; out[cnt].proto = INVITE_PROTO_TCP;
out[cnt].family = 6; out[cnt].socketId = s2->sock_id; out[cnt].proto = INVITE_PROTO_TCP;
memcpy(out[cnt].address, &sin6->sin6_addr, 16);
out[cnt].port = ntohs(sin6->sin6_port); cnt++;
}
ts = ts->next; }
s2 = s2->next; }
}
return cnt;
}
@ -1015,12 +1017,19 @@ char* utun_bridge_get_member_links_json(uint64_t node_id) {
char la[INET6_ADDRSTRLEN] = ""; int lf = 0; uint16_t lp = 0;
if (lk->conn) {
const struct sockaddr_storage* lsa = &lk->conn->local_addr;
const struct sockaddr_storage* lsa = lk->conn->interface_addr.ss_family ? &lk->conn->interface_addr : &lk->conn->local_addr;
if (lsa->ss_family == AF_INET) {
inet_ntop(AF_INET, &((struct sockaddr_in*)lsa)->sin_addr, la, sizeof(la)); lf = 4; lp = ntohs(((struct sockaddr_in*)lsa)->sin_port);
} else if (lsa->ss_family == AF_INET6) {
inet_ntop(AF_INET6, &((struct sockaddr_in6*)lsa)->sin6_addr, la, sizeof(la)); lf = 6; lp = ntohs(((struct sockaddr_in6*)lsa)->sin6_port);
}
} else if (lk->is_tcp && lk->tcp_link) {
const struct sockaddr_storage* lsa = stcp_link_get_local_addr(lk->tcp_link);
if (lsa && lsa->ss_family == AF_INET) {
inet_ntop(AF_INET, &((struct sockaddr_in*)lsa)->sin_addr, la, sizeof(la)); lf = 4; lp = ntohs(((struct sockaddr_in*)lsa)->sin_port);
} else if (lsa && lsa->ss_family == AF_INET6) {
inet_ntop(AF_INET6, &((struct sockaddr_in6*)lsa)->sin6_addr, la, sizeof(la)); lf = 6; lp = ntohs(((struct sockaddr_in6*)lsa)->sin6_port);
}
}
char ra[INET6_ADDRSTRLEN] = ""; int rf = 0; uint16_t rp = 0;
@ -1035,9 +1044,10 @@ char* utun_bridge_get_member_links_json(uint64_t node_id) {
}
const char* sep = first ? "" : ","; first = 0;
const char* sn = lk->conn ? lk->conn->name : "";
pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos,
"%s{\"lf\":%d,\"la\":\"%s\",\"lp\":%d,\"rf\":%d,\"ra\":\"%s\",\"rp\":%d,\"st\":%d,\"ls\":%d,\"is\":%d}",
sep, lf, la, (int)lp, rf, ra, (int)rp, st, ls, lk->is_server);
"%s{\"id\":%d,\"lf\":%d,\"la\":\"%s\",\"lp\":%d,\"rf\":%d,\"ra\":\"%s\",\"rp\":%d,\"st\":%d,\"ls\":%d,\"is\":%d,\"sn\":\"%s\"}",
sep, lk->local_link_id, lf, la, (int)lp, rf, ra, (int)rp, st, ls, lk->is_server, sn);
}
if (pos < (int)sizeof(buf))
pos += snprintf(buf + pos, sizeof(buf) - (size_t)pos, "]}");
@ -1055,45 +1065,46 @@ char* utun_bridge_get_local_sockets_json(void) {
if (!chat_core_is_initialized() || !g_cc.inst) return u_strdup("[]");
struct UTUN_INSTANCE* inst = g_cc.inst;
/* count active TCP connections */
int active_conns = 0, active_links = 0;
if (inst->connections) {
for (struct ll_entry* entry = inst->connections->head; entry; entry = entry->next) {
struct conn_queue_entry* ce = (struct conn_queue_entry*)entry->data;
if (ce && ce->conn && ce->conn->initialized) {
int has_tcp = 0;
for (struct ETCP_LINK* lk = ce->conn->links; lk; lk = lk->next) {
if (lk->is_tcp) { has_tcp = 1; break; }
}
if (!has_tcp) continue;
active_conns++;
active_links += ce->conn->links_up;
}
}
}
if (inst->tcp_connections) {
for (struct ll_entry* entry = inst->tcp_connections->head; entry; entry = entry->next) {
struct tcp_conn_entry* te = (struct tcp_conn_entry*)entry->data;
if (te && te->etcp_conn && te->etcp_conn->initialized) {
active_conns++;
active_links += te->etcp_conn->links_up;
}
}
}
size_t cap = 4096, pos = 1;
char* json = u_malloc(cap);
if (!json) return u_strdup("[]");
json[0] = '[';
int first = 1;
bridge_log(BLEV_INFO, "getLocalSockets: tcp_sockets=%p etcp_sockets=%p conns=%d links=%d",
(void*)inst->tcp_sockets, (void*)inst->etcp_sockets, active_conns, active_links);
bridge_log(BLEV_INFO, "getLocalSockets: etcp_sockets=%p", (void*)inst->etcp_sockets);
/* Helper: count per-socket links_up / links_down */
#define SOCK_COUNT_LINKS(_s, _lu, _ld) do { \
int *_p_lu = &(_lu), *_p_ld = &(_ld); \
*_p_lu = 0; *_p_ld = 0; \
if (inst->connections) { \
for (struct ll_entry* _e = inst->connections->head; _e; _e = _e->next) { \
struct conn_queue_entry* _ce = (struct conn_queue_entry*)_e->data; \
if (!_ce || !_ce->conn) continue; \
for (struct ETCP_LINK* _lk = _ce->conn->links; _lk; _lk = _lk->next) { \
if (_lk->conn != (_s)) continue; \
if (_lk->link_state == 3) (*_p_lu)++; else (*_p_ld)++; \
} \
} \
} \
if (inst->tcp_connections) { \
for (struct ll_entry* _e = inst->tcp_connections->head; _e; _e = _e->next) { \
struct tcp_conn_entry* _te = (struct tcp_conn_entry*)_e->data; \
if (!_te || !_te->etcp_conn) continue; \
for (struct ETCP_LINK* _lk = _te->etcp_conn->links; _lk; _lk = _lk->next) { \
if (_lk->conn != (_s)) continue; \
if (_lk->link_state == 3) (*_p_lu)++; else (*_p_ld)++; \
} \
} \
} \
} while(0)
/* TCP sockets */
{ struct ETCP_SOCKET* ts = inst->tcp_sockets;
while (ts) {
{ struct ETCP_SOCKET* s = inst->etcp_sockets;
while (s) {
if (!s->is_tcp) { s = s->next; continue; }
char ip_str[INET6_ADDRSTRLEN] = ""; int port = 0;
const struct sockaddr_storage* addr = ts->interface_addr.ss_family ? &ts->interface_addr : &ts->local_addr;
const struct sockaddr_storage* addr = s->interface_addr.ss_family ? &s->interface_addr : &s->local_addr;
if (addr->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)addr;
port = ntohs(sin->sin_port);
@ -1103,21 +1114,25 @@ char* utun_bridge_get_local_sockets_json(void) {
port = ntohs(sin6->sin6_port);
inet_ntop(AF_INET6, &sin6->sin6_addr, ip_str, sizeof(ip_str));
}
int lu, ld; SOCK_COUNT_LINKS(s, lu, ld);
int state = 2; /* bound and listening */
const char* sep = first ? "" : ","; first = 0;
size_t needed = snprintf(NULL, 0, "%s{\"si\":%u,\"nm\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"pt\":\"TCP\",\"st\":%d,\"lk\":%d}",
sep, ts->sock_id, ts->name, ip_str, port, state, active_links);
size_t needed = snprintf(NULL, 0,
"%s{\"si\":%u,\"nm\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"pt\":\"TCP\",\"st\":%d,\"lu\":%d,\"ld\":%d}",
sep, s->sock_id, s->name, ip_str, port, state, lu, ld);
while (pos + needed + 2 > cap) { char* tmp = u_realloc(json, cap * 2); if (!tmp) break; json = tmp; cap *= 2; }
if (pos + needed + 2 <= cap)
pos += (size_t)snprintf(json + pos, cap - pos, "%s{\"si\":%u,\"nm\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"pt\":\"TCP\",\"st\":%d,\"lk\":%d}",
sep, ts->sock_id, ts->name, ip_str, port, state, active_links);
ts = ts->next;
pos += (size_t)snprintf(json + pos, cap - pos,
"%s{\"si\":%u,\"nm\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"pt\":\"TCP\",\"st\":%d,\"lu\":%d,\"ld\":%d}",
sep, s->sock_id, s->name, ip_str, port, state, lu, ld);
s = s->next;
}
}
/* UDP sockets */
{ struct ETCP_SOCKET* es = inst->etcp_sockets;
while (es) {
if (es->is_tcp) { es = es->next; continue; }
char ip_str[INET6_ADDRSTRLEN] = ""; int port = 0;
const struct sockaddr_storage* addr = es->interface_addr.ss_family ? &es->interface_addr : &es->local_addr;
if (addr->ss_family == AF_INET) {
@ -1129,19 +1144,23 @@ char* utun_bridge_get_local_sockets_json(void) {
port = ntohs(sin6->sin6_port);
inet_ntop(AF_INET6, &sin6->sin6_addr, ip_str, sizeof(ip_str));
}
int lk = es->links_queue ? queue_entry_count(es->links_queue) : 0;
int lu, ld; SOCK_COUNT_LINKS(es, lu, ld);
int state = 2; /* bound and listening */
const char* sep = first ? "" : ","; first = 0;
size_t needed = snprintf(NULL, 0, "%s{\"si\":%u,\"nm\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"pt\":\"UDP\",\"st\":%d,\"lk\":%d}",
sep, es->sock_id, es->name, ip_str, port, state, lk);
size_t needed = snprintf(NULL, 0,
"%s{\"si\":%u,\"nm\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"pt\":\"UDP\",\"st\":%d,\"lu\":%d,\"ld\":%d}",
sep, es->sock_id, es->name, ip_str, port, state, lu, ld);
while (pos + needed + 2 > cap) { char* tmp = u_realloc(json, cap * 2); if (!tmp) break; json = tmp; cap *= 2; }
if (pos + needed + 2 <= cap)
pos += (size_t)snprintf(json + pos, cap - pos, "%s{\"si\":%u,\"nm\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"pt\":\"UDP\",\"st\":%d,\"lk\":%d}",
sep, es->sock_id, es->name, ip_str, port, state, lk);
pos += (size_t)snprintf(json + pos, cap - pos,
"%s{\"si\":%u,\"nm\":\"%s\",\"ad\":\"%s\",\"po\":%d,\"pt\":\"UDP\",\"st\":%d,\"lu\":%d,\"ld\":%d}",
sep, es->sock_id, es->name, ip_str, port, state, lu, ld);
es = es->next;
}
}
#undef SOCK_COUNT_LINKS
json[pos++] = ']'; json[pos] = '\0';
return json;
}

13
tools/chatgui-android/libutun_lite/instance_lite.c

@ -161,7 +161,10 @@ static void chat_event_forward(int type, const uint8_t* data, int len) {
static void nodeinfo_event_cb(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq, void* arg) {
(void)group; (void)arg;
if (!nq || !g_event_handler) return;
uint16_t best_rtt = node_best_rtt(nq);
uint16_t best_rtt = topo_get_chain_rtt(nq);
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "nodeinfo_cb: nid=%016llx pres=%02x up=%02x rtt=%u(0x%04X) paths=%zu",
(unsigned long long)nq->node_id, nq->conn_presence, nq->conn_up,
best_rtt, best_rtt, nq->paths ? queue_entry_count(nq->paths) : 0);
uint8_t data[12];
memcpy(data, &nq->node_id, 8);
data[8] = nq->conn_presence;
@ -299,6 +302,7 @@ static void* instance_thread(void* arg) {
etcp_router_bind(g_inst, ETCP_RT_ID_CHAT_SYNC, NULL);
utun_add_nodeinfo_cbk(g_inst, nodeinfo_event_cb, NULL);
etcp_add_link_status_cbk(g_inst, on_link_status_changed, NULL);
/* Set my_name from config */
if (g_inst->config->global.name[0]) {
@ -433,8 +437,11 @@ int instance_lite_start(const char* config_text) {
debug_config_init();
debug_set_level(DEBUG_LEVEL_INFO);
debug_set_category_level(DEBUG_CATEGORY_SYS, DEBUG_LEVEL_INFO);
debug_set_category_level(DEBUG_CATEGORY_CHAT_SYNC, DEBUG_LEVEL_TRACE);
debug_set_category_level(DEBUG_CATEGORY_MEMBER_SYNC, DEBUG_LEVEL_TRACE);
debug_set_category_level(DEBUG_CATEGORY_CHAT_SYNC, DEBUG_LEVEL_INFO);
debug_set_category_level(DEBUG_CATEGORY_MEMBER_SYNC, DEBUG_LEVEL_INFO);
debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO);
debug_set_category_level(DEBUG_CATEGORY_ETCP, DEBUG_LEVEL_INFO);
debug_set_category_level(DEBUG_CATEGORY_DUMP, DEBUG_LEVEL_WARN);
debug_enable_function_name(0);
cfg_get_val(config_text, "db_path", g_db_path, sizeof(g_db_path));

14
tools/chatgui/src/accountlist.cpp

@ -38,6 +38,8 @@ QString AccountList::addrStr(const void* addr, int family) {
}
if (family == 6) {
const uint16_t* a = (const uint16_t*)addr;
int zero = 1; for (int i = 0; i < 8; i++) if (a[i]) { zero = 0; break; }
if (zero) return "[::]";
return QString("%1:%2:%3:%4:%5:%6:%7:%8")
.arg(ntohs(a[0]), 0, 16).arg(ntohs(a[1]), 0, 16)
.arg(ntohs(a[2]), 0, 16).arg(ntohs(a[3]), 0, 16)
@ -336,8 +338,16 @@ void AccountList::updateDetailDisplay() {
QString lc = (lk.linkState == 3 && lk.linkStatus == 1) ? "#4CAF50" : "#E53935";
QString left = QString("%1:%2").arg(addrStr(lk.localAddr.constData(), lk.localFamily)).arg(lk.localPort);
QString right = QString("%1:%2").arg(addrStr(lk.addr.constData(), lk.family)).arg(lk.port);
lines.append(QString("<span style='color:%1;'>●</span> %2 %3 → %4")
.arg(lc, proto, left, right));
const char* arrow = lk.isServer
? "<b style='color:#FF9800;font-size:13pt;'>◀</b>"
: "<b style='color:#2196F3;font-size:13pt;'>▶</b>";
if (lk.localFamily == 6 || lk.family == 6) {
lines.append(QString("<span style='color:%1;'>●</span> %2 %3:%4").arg(lc, proto, addrStr(lk.localAddr.constData(), lk.localFamily)).arg(lk.localPort));
lines.append(QString("&nbsp;%1 %2:%3").arg(arrow, addrStr(lk.addr.constData(), lk.family)).arg(lk.port));
} else {
lines.append(QString("<span style='color:%1;'>●</span> %2 %3 %4 %5")
.arg(lc, proto, left, arrow, right));
}
}
/* DB addresses without live link */
for (const auto& a : m_detailAddrs) {

2
tools/chatgui/transport/gui_bridge.h

@ -27,7 +27,7 @@ struct TOPO_GROUP_NODE;
#define GUI_EVT_NODE_CHANGED 11 /* data: [node_id:8] */
#define GUI_EVT_ATTACHMENT_DOWNLOADED 14 /* data: [ch_id_len:1][ch_id:var][msg_id:8] */
#define GUI_EVT_DOWNLOAD_PROGRESS 15 /* data: [ch_id_len:1][ch_id:var][msg_id:8][blocks_done:4][num_blocks:4] */
#define GUI_EVT_NODEINFO_UPDATE 16 /* data: node_id:8 conn_presence:1 conn_up:1 best_rtt_packed:2 */
#define GUI_EVT_NODEINFO_UPDATE 16 /* data: node_id:8 conn_presence:1 conn_up:1 best_rtt:2(0.1ms,0xFFFF=nodata) */
#define GUI_EVT_CONN_LIST 20 /* data: [count:2][entry:46B]* — binary conn list */
#define GUI_EVT_CONN_METRICS 21 /* data: text dump of selected connection metrics */
#define GUI_EVT_MEMBER_DETAIL 22 /* data: [node_id:8][flags:1][own_tcp_active:2][links...][socks...] — live snapshot for member detail panel */

7
tools/chatgui/transport/gui_bridge_impl.cpp

@ -318,12 +318,15 @@ void gui_bridge_set_member_removed_cb(gui_member_removed_fn cb) {
extern "C" void gui_nodeinfo_cb_impl(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* nq, void* arg) {
(void)arg;
if (!nq) return;
uint16_t best_rtt = node_best_rtt(nq);
uint8_t data[12];
uint16_t best_rtt = topo_get_chain_rtt(nq);
uint8_t data[16];
memcpy(data, &nq->node_id, 8);
data[8] = nq->conn_presence;
data[9] = nq->conn_up;
memcpy(data + 10, &best_rtt, 2);
uint16_t bgp_nodes = group ? (uint16_t)queue_entry_count(group->nodes) : 0;
memcpy(data + 12, &bgp_nodes, 2);
memcpy(data + 14, &bgp_nodes, 2);
gui_bridge_post(GUI_EVT_NODEINFO_UPDATE, data, sizeof(data));
}

13
tools/chatgui/transport/utun_node.cpp

@ -217,19 +217,20 @@ QList<NodeAddr> UtunNode::getInviteAddresses(DbManager* db) {
/* 3) TCP from local TCP sockets */
{
struct ETCP_SOCKET* ts = m_instance->tcp_sockets;
while (ts) {
struct sockaddr_storage* sa = &ts->interface_addr;
struct ETCP_SOCKET* s = m_instance->etcp_sockets;
while (s) {
if (!s->is_tcp) { s = s->next; continue; }
struct sockaddr_storage* sa = &s->interface_addr;
if (sa->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
QByteArray addr((const char*)&sin->sin_addr, 4);
add_addr(addr, ntohs(sin->sin_port), 4, (int)ts->sock_id, INVITE_PROTO_TCP);
add_addr(addr, ntohs(sin->sin_port), 4, (int)s->sock_id, INVITE_PROTO_TCP);
} else if (sa->ss_family == AF_INET6) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)sa;
QByteArray addr((const char*)&sin6->sin6_addr, 16);
add_addr(addr, ntohs(sin6->sin6_port), 6, (int)ts->sock_id, INVITE_PROTO_TCP);
add_addr(addr, ntohs(sin6->sin6_port), 6, (int)s->sock_id, INVITE_PROTO_TCP);
}
ts = ts->next;
s = s->next;
}
}

Loading…
Cancel
Save