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

- Добавлен device_type (1 байт, CLIENT_TYPE_*) в INIT_REQUEST и INIT_RESPONSE
- Добавлен keepalive_interval (2 байта) в INIT_RESPONSE
- STCP handshake расширен с 43 до 46 байт (device_type+keepalive)
- negotiate_keepalive(): оба desktop/server → min, иначе (есть mobile) → max
- keepalive_interval вынесен в UTUN_INSTANCE для использования в handshake
- stcp_client_connect() принимает device_type и keepalive_interval
- peer_device_type/peer_keepalive_interval передаются через stcp_link в ETCP_LINK
This commit is contained in:
evgeny committed 2026-08-11 11:16:27 +03:00
1 parent 43aca4a41b
commit 0f1bd3d120
37 files changed
+535 -309

No files matched your search

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