diff --git a/AGENTS.md b/AGENTS.md index 62839536..088d4731 100644 --- a/AGENTS.md +++ b/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`. diff --git a/src/chat/chat_channel.c b/src/chat/chat_channel.c index 1e9999dc..5770c00b 100644 --- a/src/chat/chat_channel.c +++ b/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; diff --git a/src/chat/chat_headless_control.c b/src/chat/chat_headless_control.c index be915d5c..9da41b1f 100644 --- a/src/chat/chat_headless_control.c +++ b/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++]; diff --git a/src/chat/chat_profile.c b/src/chat/chat_profile.c index 3fba0058..a03b8e30 100644 --- a/src/chat/chat_profile.c +++ b/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; } diff --git a/src/chat/chat_status.c b/src/chat/chat_status.c index 4181e424..ca3c7ceb 100644 --- a/src/chat/chat_status.c +++ b/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); diff --git a/src/routing_layer/conn_mgr_core.c b/src/routing_layer/conn_mgr_core.c index a2cb6111..ee1bf947 100644 --- a/src/routing_layer/conn_mgr_core.c +++ b/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); diff --git a/src/routing_layer/conn_mgr_indirect.c b/src/routing_layer/conn_mgr_indirect.c index c43b08f1..5798fb07 100644 --- a/src/routing_layer/conn_mgr_indirect.c +++ b/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;imy_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;imgr->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;icandidates[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); diff --git a/src/routing_layer/conn_mgr_monitor.c b/src/routing_layer/conn_mgr_monitor.c index 81597eb4..6818a264 100644 --- a/src/routing_layer/conn_mgr_monitor.c +++ b/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; diff --git a/src/routing_layer/conn_mgr_priv.h b/src/routing_layer/conn_mgr_priv.h index 82b1f7db..4e5ea4e1 100644 --- a/src/routing_layer/conn_mgr_priv.h +++ b/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); diff --git a/src/routing_layer/topo_group.c b/src/routing_layer/topo_group.c index daf724a2..6a7a2624 100644 --- a/src/routing_layer/topo_group.c +++ b/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; } } diff --git a/src/routing_layer/topo_node.c b/src/routing_layer/topo_node.c index 563f447c..6a51d878 100644 --- a/src/routing_layer/topo_node.c +++ b/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) { diff --git a/src/routing_layer/topo_node.h b/src/routing_layer/topo_node.h index 22d20a2b..da5cffd8 100644 --- a/src/routing_layer/topo_node.h +++ b/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 diff --git a/src/routing_layer/topo_recovery.c b/src/routing_layer/topo_recovery.c index a44d0e8b..fe176d0b 100644 --- a/src/routing_layer/topo_recovery.c +++ b/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; diff --git a/src/transport_layer/auto_socket.c b/src/transport_layer/auto_socket.c index 64934973..7044c61c 100644 --- a/src/transport_layer/auto_socket.c +++ b/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; } } diff --git a/src/transport_layer/etcp.c b/src/transport_layer/etcp.c index 64d741e9..bcc79ebf 100644 --- a/src/transport_layer/etcp.c +++ b/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 этого пакета (на стороне отправителя, т.е. у нас) diff --git a/src/transport_layer/etcp_connect.c b/src/transport_layer/etcp_connect.c index 1f783679..ffc99ded 100644 --- a/src/transport_layer/etcp_connect.c +++ b/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; } diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c index e723bea8..6fa82285 100644 --- a/src/transport_layer/etcp_connections.c +++ b/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; @@ -1876,7 +1939,7 @@ void etcp_connections_read_callback_socket(socket_t sock, void* arg) { // 66 - не удалось создать линк struct ETCP_SOCKET* e_sock = (struct ETCP_SOCKET*)arg; if (!e_sock) return; - + struct sockaddr_storage addr; uint8_t data[PACKET_DATA_SIZE]; socklen_t addr_len=sizeof(addr); @@ -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) @@ -2424,7 +2490,7 @@ int init_sockets(struct UTUN_INSTANCE* instance) { } } } - + // TCP transport: create stcp_server instead of UDP socket if (server->transport) { uint16_t port = 0; @@ -2469,7 +2535,7 @@ int init_sockets(struct UTUN_INSTANCE* instance) { continue; } success_count++; - + DEBUG_INFO(DEBUG_CATEGORY_CONNECTION, "Initialized server %s on %s (links: %d)", server->name, sockaddr_storage_to_str(&server->ip).str, queue_entry_count(e_sock->links_queue)); server = server->next; diff --git a/src/transport_layer/etcp_connections.h b/src/transport_layer/etcp_connections.h index 9cf4480d..80f46456 100644 --- a/src/transport_layer/etcp_connections.h +++ b/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, diff --git a/src/transport_layer/node_conn_direct.c b/src/transport_layer/node_conn_direct.c index 6dd80f79..adaf7048 100644 --- a/src/transport_layer/node_conn_direct.c +++ b/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()); diff --git a/src/transport_layer/stcp.h b/src/transport_layer/stcp.h index 54c770c2..4dd51386 100644 --- a/src/transport_layer/stcp.h +++ b/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; diff --git a/src/transport_layer/stcp_client.c b/src/transport_layer/stcp_client.c index d0c6ff90..173b2281 100644 --- a/src/transport_layer/stcp_client.c +++ b/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) { diff --git a/src/transport_layer/stcp_client.h b/src/transport_layer/stcp_client.h index dea9d797..82726641 100644 --- a/src/transport_layer/stcp_client.h +++ b/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); diff --git a/src/transport_layer/stcp_link.c b/src/transport_layer/stcp_link.c index 834eb13a..21606a4c 100644 --- a/src/transport_layer/stcp_link.c +++ b/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; diff --git a/src/transport_layer/stcp_link.h b/src/transport_layer/stcp_link.h index bcf9a561..f358b0c5 100644 --- a/src/transport_layer/stcp_link.h +++ b/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 diff --git a/src/transport_layer/stcp_server.c b/src/transport_layer/stcp_server.c index f6bfc52a..ba5cc749 100644 --- a/src/transport_layer/stcp_server.c +++ b/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); diff --git a/src/utun_instance.c b/src/utun_instance.c index 05d3afe6..428b3437 100644 --- a/src/utun_instance.c +++ b/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"); diff --git a/src/utun_instance.h b/src/utun_instance.h index eeebc667..8f412e44 100644 --- a/src/utun_instance.h +++ b/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 diff --git a/tests/test_invite_group_create.c b/tests/test_invite_group_create.c index 41dbdfd9..76e69108 100644 --- a/tests/test_invite_group_create.c +++ b/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); diff --git a/tests/test_stcp.c b/tests/test_stcp.c index 911ebeea..d977c60f 100644 --- a/tests/test_stcp.c +++ b/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++; } diff --git a/tools/chatgui-android/AGENTS.md b/tools/chatgui-android/AGENTS.md index 506199e2..08b15ee2 100644 --- a/tools/chatgui-android/AGENTS.md +++ b/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 # на конкретный девайс +./build.sh # clean + сборка + прошивка на подключённый телефон +./build.sh noinstall # только сборка, без прошивки +./build.sh # clean + сборка + прошивка на конкретный девайс ``` ### APK (Android) diff --git a/tools/chatgui-android/build.sh b/tools/chatgui-android/build.sh index bb7d45aa..632cf1b5 100755 --- a/tools/chatgui-android/build.sh +++ b/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 diff --git a/tools/chatgui-android/jni_bridge/android_jni_bridge.c b/tools/chatgui-android/jni_bridge/android_jni_bridge.c index 248b21d4..0b99aebc 100644 --- a/tools/chatgui-android/jni_bridge/android_jni_bridge.c +++ b/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; } diff --git a/tools/chatgui-android/libutun_lite/instance_lite.c b/tools/chatgui-android/libutun_lite/instance_lite.c index 0622de99..f2123ec3 100644 --- a/tools/chatgui-android/libutun_lite/instance_lite.c +++ b/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)); diff --git a/tools/chatgui/src/accountlist.cpp b/tools/chatgui/src/accountlist.cpp index 26c8165e..aed5bb98 100644 --- a/tools/chatgui/src/accountlist.cpp +++ b/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("● %2 %3 → %4") - .arg(lc, proto, left, right)); + const char* arrow = lk.isServer + ? "◀" + : "▶"; + if (lk.localFamily == 6 || lk.family == 6) { + lines.append(QString("● %2 %3:%4").arg(lc, proto, addrStr(lk.localAddr.constData(), lk.localFamily)).arg(lk.localPort)); + lines.append(QString(" %1 %2:%3").arg(arrow, addrStr(lk.addr.constData(), lk.family)).arg(lk.port)); + } else { + lines.append(QString("● %2 %3 %4 %5") + .arg(lc, proto, left, arrow, right)); + } } /* DB addresses without live link */ for (const auto& a : m_detailAddrs) { diff --git a/tools/chatgui/transport/gui_bridge.h b/tools/chatgui/transport/gui_bridge.h index b18cb4d7..3b81b110 100644 --- a/tools/chatgui/transport/gui_bridge.h +++ b/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 */ diff --git a/tools/chatgui/transport/gui_bridge_impl.cpp b/tools/chatgui/transport/gui_bridge_impl.cpp index 7be47d2a..1a5ab71b 100644 --- a/tools/chatgui/transport/gui_bridge_impl.cpp +++ b/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)); } diff --git a/tools/chatgui/transport/utun_node.cpp b/tools/chatgui/transport/utun_node.cpp index fc1735ee..d294617a 100644 --- a/tools/chatgui/transport/utun_node.cpp +++ b/tools/chatgui/transport/utun_node.cpp @@ -217,19 +217,20 @@ QList 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; } }