Browse Source

etcp: TCP-ping через STCP-хендшейк с флагом PING

- STCP-хендшейк: добавлен байт флагов (STCP_HANDSHAKE_FLAG_PING), HS_ENC 46→47
- stcp_client: stcp_ping_send() — connect+handshake(PING)+rtt+close; ошибка/таймаут → success=0
- stcp_server: детект флага, ответ с эхо-флагом, мягкое закрытие (close_after_send)
- etcp_connections: etcp_send_tcp_ping(), guard is_tcp/fd и проверка возврата send_ping_raw
- route_connectivity: probe TCP-адресов через TCP-ping (1 попытка), UDP-кандидаты без TCP-сокетов
- tests: test_stcp_ping (success+failure path), обновлена сигнатура stcp_client_connect
topo_upd
evgeny 2 months ago
parent
commit
d04e9d58b6
  1. 83
      src/routing_layer/route_connectivity.c
  2. 59
      src/transport_layer/etcp_connections.c
  3. 6
      src/transport_layer/etcp_connections.h
  4. 1
      src/transport_layer/stcp.c
  5. 16
      src/transport_layer/stcp.h
  6. 67
      src/transport_layer/stcp_client.c
  7. 13
      src/transport_layer/stcp_client.h
  8. 3
      src/transport_layer/stcp_link.c
  9. 24
      src/transport_layer/stcp_server.c
  10. 5
      tests/Makefile.am
  11. 16
      tests/test_stcp.c
  12. 4
      tests/test_stcp_link.c
  13. 97
      tests/test_stcp_ping.c

83
src/routing_layer/route_connectivity.c

@ -29,6 +29,7 @@ struct conn_probe_ctx {
struct TOPO_GROUP* group; struct TOPO_GROUP* group;
struct TOPO_GROUP_NODE* nq; struct TOPO_GROUP_NODE* nq;
uint8_t addr_type; // ADDR_TYPE_* uint8_t addr_type; // ADDR_TYPE_*
uint8_t is_tcp; // 1 = TCP-ping (stcp_ping_send), 0 = UDP-ping
struct sockaddr_storage target_addr; struct sockaddr_storage target_addr;
uint8_t peer_pubkey[SC_PUBKEY_SIZE]; uint8_t peer_pubkey[SC_PUBKEY_SIZE];
@ -37,7 +38,7 @@ struct conn_probe_ctx {
uint8_t candidate_index; uint8_t candidate_index;
uint16_t best_across_sockets; // min RTT по всем сокетам uint16_t best_across_sockets; // min RTT по всем сокетам
uint8_t count_total; // 3 на серию uint8_t count_total; // 3 на серию (1 для TCP)
uint8_t count_sent; uint8_t count_sent;
uint8_t count_ok; uint8_t count_ok;
uint16_t min_rtt; // min RTT в текущей серии uint16_t min_rtt; // min RTT в текущей серии
@ -95,6 +96,7 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance,
// IPv6: prefer sockets in same /64 subnet // IPv6: prefer sockets in same /64 subnet
e_sock = instance->etcp_sockets; e_sock = instance->etcp_sockets;
while (e_sock && found < (int)max_count) { while (e_sock && found < (int)max_count) {
if (e_sock->is_tcp) { e_sock = e_sock->next; continue; }
if (e_sock->local_addr.ss_family == AF_INET6) { if (e_sock->local_addr.ss_family == AF_INET6) {
struct sockaddr_in6* if_sin6 = (struct sockaddr_in6*)&e_sock->interface_addr; struct sockaddr_in6* if_sin6 = (struct sockaddr_in6*)&e_sock->interface_addr;
if (memcmp(&if_sin6->sin6_addr, &target_sin6->sin6_addr, 8) == 0) { if (memcmp(&if_sin6->sin6_addr, &target_sin6->sin6_addr, 8) == 0) {
@ -108,6 +110,7 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance,
// Pass 2: all other IPv6 sockets // Pass 2: all other IPv6 sockets
e_sock = instance->etcp_sockets; e_sock = instance->etcp_sockets;
while (e_sock && found < (int)max_count) { while (e_sock && found < (int)max_count) {
if (e_sock->is_tcp) { e_sock = e_sock->next; continue; }
if (e_sock->local_addr.ss_family != AF_INET6) { e_sock = e_sock->next; continue; } if (e_sock->local_addr.ss_family != AF_INET6) { e_sock = e_sock->next; continue; }
int dup = 0; int dup = 0;
for (int i = 0; i < found; i++) if (out_sockets[i] == e_sock) { dup = 1; break; } for (int i = 0; i < found; i++) if (out_sockets[i] == e_sock) { dup = 1; break; }
@ -123,6 +126,7 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance,
// Проход 1: точное совпадение подсети (для INTERFACE) или PUBLIC/NAT_VERIFIED (для NAT) // Проход 1: точное совпадение подсети (для INTERFACE) или PUBLIC/NAT_VERIFIED (для NAT)
while (e_sock && found < (int)max_count) { while (e_sock && found < (int)max_count) {
if (e_sock->is_tcp) { e_sock = e_sock->next; continue; }
if (e_sock->local_addr.ss_family != AF_INET) { e_sock = e_sock->next; continue; } if (e_sock->local_addr.ss_family != AF_INET) { e_sock = e_sock->next; continue; }
int match = 0; int match = 0;
struct sockaddr_in* if_sin = (struct sockaddr_in*)&e_sock->interface_addr; struct sockaddr_in* if_sin = (struct sockaddr_in*)&e_sock->interface_addr;
@ -152,6 +156,7 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance,
// Проход 2: все остальные подходящие // Проход 2: все остальные подходящие
e_sock = instance->etcp_sockets; e_sock = instance->etcp_sockets;
while (e_sock && found < (int)max_count) { while (e_sock && found < (int)max_count) {
if (e_sock->is_tcp) { e_sock = e_sock->next; continue; }
if (e_sock->local_addr.ss_family != AF_INET) { e_sock = e_sock->next; continue; } if (e_sock->local_addr.ss_family != AF_INET) { e_sock = e_sock->next; continue; }
int dup = 0; int dup = 0;
for (int i = 0; i < found; i++) if (out_sockets[i] == e_sock) { dup = 1; break; } for (int i = 0; i < found; i++) if (out_sockets[i] == e_sock) { dup = 1; break; }
@ -172,6 +177,7 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance,
// Проход 3: остальные IPv4 (fallback) // Проход 3: остальные IPv4 (fallback)
e_sock = instance->etcp_sockets; e_sock = instance->etcp_sockets;
while (e_sock && found < (int)max_count) { while (e_sock && found < (int)max_count) {
if (e_sock->is_tcp) { e_sock = e_sock->next; continue; }
if (e_sock->local_addr.ss_family != AF_INET) { e_sock = e_sock->next; continue; } if (e_sock->local_addr.ss_family != AF_INET) { e_sock = e_sock->next; continue; }
int dup = 0; int dup = 0;
for (int i = 0; i < found; i++) if (out_sockets[i] == e_sock) { dup = 1; break; } for (int i = 0; i < found; i++) if (out_sockets[i] == e_sock) { dup = 1; break; }
@ -184,6 +190,17 @@ static int conn_match_candidate_sockets(struct UTUN_INSTANCE* instance,
// ---- probe lifecycle ---- // ---- probe lifecycle ----
static int conn_probe_send(struct conn_probe_ctx* ctx) {
if (ctx->is_tcp) {
return etcp_send_tcp_ping(ctx->instance, ctx->peer_pubkey, &ctx->target_addr,
ctx->timeout_ms, conn_probe_single_cb, ctx);
}
struct ETCP_SOCKET* sock = ctx->candidate_sockets[ctx->candidate_index];
return etcp_send_ping_to_socket(ctx->instance, sock, ctx->peer_pubkey,
&ctx->target_addr, ctx->timeout_ms,
conn_probe_single_cb, ctx, NULL, 0, 0);
}
static void conn_probe_start_series(struct conn_probe_ctx* ctx) { static void conn_probe_start_series(struct conn_probe_ctx* ctx) {
if (ctx->candidate_index >= ctx->candidate_count) { if (ctx->candidate_index >= ctx->candidate_count) {
// все сокеты перебраны // все сокеты перебраны
@ -192,25 +209,14 @@ static void conn_probe_start_series(struct conn_probe_ctx* ctx) {
return; return;
} }
struct ETCP_SOCKET* sock = ctx->candidate_sockets[ctx->candidate_index];
ctx->count_sent = 0; ctx->count_sent = 0;
ctx->count_ok = 0; ctx->count_ok = 0;
ctx->min_rtt = 65535; ctx->min_rtt = 65535;
static const char* atype_names[] = {"INTERFACE", "NAT", "REAL"}; int ret = conn_probe_send(ctx);
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "probe series start: socket=%s target=%s:%u type=%s",
sock->name,
ip_to_str(&((struct sockaddr_in*)&ctx->target_addr)->sin_addr, AF_INET).str,
(unsigned)ntohs(((struct sockaddr_in*)&ctx->target_addr)->sin_port),
atype_names[ctx->addr_type]);
int ret = etcp_send_ping_to_socket(ctx->instance, sock, ctx->peer_pubkey,
&ctx->target_addr, ctx->timeout_ms,
conn_probe_single_cb, ctx, NULL, 0, 0);
if (ret != 0) { if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "probe: cannot start ping from socket %s", sock->name); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "probe: cannot start ping (tcp=%d)", ctx->is_tcp);
ctx->count_sent = 3; // simulate full failure ctx->count_sent = ctx->count_total; // simulate full failure
ctx->candidate_index++; ctx->candidate_index++;
conn_probe_start_series(ctx); conn_probe_start_series(ctx);
} }
@ -228,24 +234,19 @@ static void conn_probe_single_cb(int success, uint16_t rtt, void* arg,
if (rtt < ctx->min_rtt) ctx->min_rtt = rtt; if (rtt < ctx->min_rtt) ctx->min_rtt = rtt;
} }
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "probe ping: ok=%d rtt=%u sent=%d/%d socket=%s", DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "probe ping: ok=%d rtt=%u sent=%d/%d tcp=%d",
success, rtt, ctx->count_sent, ctx->count_total, success, rtt, ctx->count_sent, ctx->count_total, ctx->is_tcp);
ctx->candidate_sockets[ctx->candidate_index]->name);
if (ctx->count_sent < ctx->count_total) { if (ctx->count_sent < ctx->count_total) {
// продолжаем с тем же сокетом int ret = conn_probe_send(ctx);
struct ETCP_SOCKET* sock = ctx->candidate_sockets[ctx->candidate_index];
int ret = etcp_send_ping_to_socket(ctx->instance, sock, ctx->peer_pubkey,
&ctx->target_addr, ctx->timeout_ms,
conn_probe_single_cb, ctx, NULL, 0, 0);
if (ret != 0) { if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "probe: cannot continue ping from socket %s", sock->name); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "probe: cannot continue ping (tcp=%d)", ctx->is_tcp);
ctx->count_sent = ctx->count_total; // force finish series ctx->count_sent = ctx->count_total; // force finish series
ctx->count_ok = 0; ctx->count_ok = 0;
} else return; // следующий пинг отправлен, ждём callback } else return; // следующий пинг отправлен, ждём callback
} }
// серия из 3 пингов завершена // серия пингов завершена
if (ctx->count_ok > 0) { if (ctx->count_ok > 0) {
if (ctx->min_rtt < ctx->best_across_sockets) ctx->best_across_sockets = ctx->min_rtt; if (ctx->min_rtt < ctx->best_across_sockets) ctx->best_across_sockets = ctx->min_rtt;
conn_probe_finish(ctx, 1); conn_probe_finish(ctx, 1);
@ -367,15 +368,21 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_G
struct sockaddr_storage target; memset(&target, 0, sizeof(target)); struct sockaddr_storage target; memset(&target, 0, sizeof(target));
struct sockaddr_in* sin = (struct sockaddr_in*)&target; sin->sin_family = AF_INET; sin->sin_addr.s_addr = ip; sin->sin_port = htons(port); struct sockaddr_in* sin = (struct sockaddr_in*)&target; sin->sin_family = AF_INET; sin->sin_addr.s_addr = ip; sin->sin_port = htons(port);
struct ETCP_SOCKET* candidates[CONN_MAX_SOCKET_CANDIDATES];
int cand_count = conn_match_candidate_sockets(instance, a->type, &target, candidates, CONN_MAX_SOCKET_CANDIDATES);
if (cand_count == 0) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for target %s:%u type=%d", ip_to_str(&ip, AF_INET).str, port, a->type); continue; }
struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx)); if (!ctx) continue; struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx)); if (!ctx) continue;
ctx->instance = instance; ctx->group = group; ctx->nq = nq; ctx->addr_type = a->type; ctx->target_addr = target; ctx->instance = instance; ctx->group = group; ctx->nq = nq; ctx->addr_type = a->type; ctx->target_addr = target;
memcpy(ctx->peer_pubkey, ni->public_key, SC_PUBKEY_SIZE); memcpy(ctx->peer_pubkey, ni->public_key, SC_PUBKEY_SIZE);
ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535;
if (a->protocol & TOPO_PROTO_TCP) {
ctx->is_tcp = 1;
ctx->candidate_count = 1; ctx->candidate_index = 0; ctx->count_total = 1;
} else {
struct ETCP_SOCKET* candidates[CONN_MAX_SOCKET_CANDIDATES];
int cand_count = conn_match_candidate_sockets(instance, a->type, &target, candidates, CONN_MAX_SOCKET_CANDIDATES);
if (cand_count == 0) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for target %s:%u type=%d", ip_to_str(&ip, AF_INET).str, port, a->type); u_free(ctx); continue; }
memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*)); memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*));
ctx->candidate_count = cand_count; ctx->candidate_index = 0; ctx->count_total = CONN_PROBE_COUNT; ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535; ctx->candidate_count = cand_count; ctx->candidate_index = 0; ctx->count_total = CONN_PROBE_COUNT;
}
ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list; nq->connectivity.probe_list = ctx; pend++; ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list; nq->connectivity.probe_list = ctx; pend++;
conn_probe_start_series(ctx); conn_probe_start_series(ctx);
} }
@ -396,15 +403,21 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct TOPO_G
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&target; sin6->sin6_family = AF_INET6; struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&target; sin6->sin6_family = AF_INET6;
memcpy(&sin6->sin6_addr, a6->addr, 16); sin6->sin6_port = htons(a6->port); memcpy(&sin6->sin6_addr, a6->addr, 16); sin6->sin6_port = htons(a6->port);
struct ETCP_SOCKET* candidates[CONN_MAX_SOCKET_CANDIDATES];
int cand_count = conn_match_candidate_sockets(instance, a6->type, &target, candidates, CONN_MAX_SOCKET_CANDIDATES);
if (cand_count == 0) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for IPv6 target type=%d", a6->type); continue; }
struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx)); if (!ctx) continue; struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx)); if (!ctx) continue;
ctx->instance = instance; ctx->nq = nq; ctx->addr_type = a6->type; ctx->target_addr = target; ctx->instance = instance; ctx->nq = nq; ctx->addr_type = a6->type; ctx->target_addr = target;
memcpy(ctx->peer_pubkey, ni->public_key, SC_PUBKEY_SIZE); memcpy(ctx->peer_pubkey, ni->public_key, SC_PUBKEY_SIZE);
ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535;
if (a6->protocol & TOPO_PROTO_TCP) {
ctx->is_tcp = 1;
ctx->candidate_count = 1; ctx->candidate_index = 0; ctx->count_total = 1;
} else {
struct ETCP_SOCKET* candidates[CONN_MAX_SOCKET_CANDIDATES];
int cand_count = conn_match_candidate_sockets(instance, a6->type, &target, candidates, CONN_MAX_SOCKET_CANDIDATES);
if (cand_count == 0) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for IPv6 target type=%d", a6->type); u_free(ctx); continue; }
memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*)); memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*));
ctx->candidate_count = cand_count; ctx->candidate_index = 0; ctx->count_total = CONN_PROBE_COUNT; ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535; ctx->candidate_count = cand_count; ctx->candidate_index = 0; ctx->count_total = CONN_PROBE_COUNT;
}
ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list; nq->connectivity.probe_list = ctx; pend++; ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list; nq->connectivity.probe_list = ctx; pend++;
conn_probe_start_series(ctx); conn_probe_start_series(ctx);
} }

59
src/transport_layer/etcp_connections.c

@ -17,6 +17,7 @@
#include "etcp.h" #include "etcp.h"
#include "stcp_link.h" #include "stcp_link.h"
#include "stcp.h" #include "stcp.h"
#include "stcp_client.h"
#include "topo_node.h" #include "topo_node.h"
#include "topo_group.h" #include "topo_group.h"
#include "node_conn_direct.h" #include "node_conn_direct.h"
@ -1272,6 +1273,11 @@ int etcp_send_ping_to_socket(struct UTUN_INSTANCE* instance, struct ETCP_SOCKET*
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "bad args"); DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "bad args");
return -1; return -1;
} }
if (e_sock->is_tcp || e_sock->fd == SOCKET_INVALID) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "ping on non-UDP socket name=%s is_tcp=%d fd=%d",
e_sock->name, e_sock->is_tcp, (int)e_sock->fd);
return -2;
}
if (user_data_len > PACKET_DATA_SIZE - 24 - 2 - SC_PUBKEY_ENC_SIZE) { if (user_data_len > PACKET_DATA_SIZE - 24 - 2 - SC_PUBKEY_ENC_SIZE) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "user_data too long"); DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "user_data too long");
return -2; return -2;
@ -1340,10 +1346,15 @@ int etcp_send_ping_to_socket(struct UTUN_INSTANCE* instance, struct ETCP_SOCKET*
DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "set key failed"); DEBUG_ERROR(DEBUG_CATEGORY_CRYPTO, "set key failed");
return -5; return -5;
} }
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "ping sent nonce=%016llx timeout=%d ulen=%zu", DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "ping send nonce=%016llx timeout=%d ulen=%zu",
(unsigned long long)ctx->nonce, timeout_ms, user_data_len); (unsigned long long)ctx->nonce, timeout_ms, user_data_len);
etcp_send_ping_raw(dgram, e_sock->fd, &sc, addr); int send_rc = etcp_send_ping_raw(dgram, e_sock->fd, &sc, addr);
u_free(dgram); u_free(dgram);
if (send_rc < 0) {
if (ctx->user_data) u_free(ctx->user_data);
u_free(ctx);
return -6;
}
if (instance->pending_pings == NULL) { if (instance->pending_pings == NULL) {
instance->pending_pings = ctx; instance->pending_pings = ctx;
} else { } else {
@ -1376,6 +1387,50 @@ int etcp_send_ping(struct UTUN_INSTANCE* instance, const uint8_t* peer_pubkey_bi
cb, user_arg, user_data, user_data_len, 0); cb, user_arg, user_data, user_data_len, 0);
} }
struct tcp_ping_adapter {
etcp_ping_callback_t cb;
void* arg;
};
static void tcp_ping_cb_adapter(int success, uint16_t rtt, void* arg) {
struct tcp_ping_adapter* a = (struct tcp_ping_adapter*)arg;
a->cb(success, rtt, a->arg, 0, NULL, 0);
u_free(a);
}
int etcp_send_tcp_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) {
if (!instance || !peer_pubkey_bin || !addr || timeout_ms <= 0 || !cb) {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "bad args: inst=%p pubkey=%p addr=%p timeout=%d cb=%p",
(void*)instance, (void*)peer_pubkey_bin, (void*)addr, timeout_ms, (void*)(uintptr_t)cb);
return -1;
}
char addr_str[INET6_ADDRSTRLEN];
uint16_t port;
if (addr->ss_family == AF_INET) {
const struct sockaddr_in* sin = (const struct sockaddr_in*)addr;
snprintf(addr_str, sizeof(addr_str), "%s", ip_to_str(&sin->sin_addr, AF_INET).str);
port = ntohs(sin->sin_port);
} else if (addr->ss_family == AF_INET6) {
const struct sockaddr_in6* sin6 = (const struct sockaddr_in6*)addr;
snprintf(addr_str, sizeof(addr_str), "%s", ip_to_str(&sin6->sin6_addr, AF_INET6).str);
port = ntohs(sin6->sin6_port);
} else {
DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "unsupported addr family=%d", addr->ss_family);
return -2;
}
struct tcp_ping_adapter* a = u_malloc(sizeof(struct tcp_ping_adapter));
if (!a) { DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "malloc adapter"); return -3; }
a->cb = cb; a->arg = user_arg;
struct stcp_client* cli = stcp_ping_send(instance->ua, addr_str, port, &instance->my_keys, peer_pubkey_bin,
instance->my_ed25519_pubkey, instance->client_type,
instance->keepalive_interval, timeout_ms, tcp_ping_cb_adapter, a);
if (!cli) { u_free(a); return -4; }
DEBUG_DEBUG(DEBUG_CATEGORY_CONNECTION, "tcp ping to %s:%u timeout=%d", addr_str, (unsigned)port, timeout_ms);
return 0;
}
// === Helpers extracted from etcp_connections_read_callback_socket === // === Helpers extracted from etcp_connections_read_callback_socket ===
static int handle_ping(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt, const struct sockaddr_storage* addr, const uint8_t* decrypted_pubkey, size_t pkt_len) { static int handle_ping(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt, const struct sockaddr_storage* addr, const uint8_t* decrypted_pubkey, size_t pkt_len) {

6
src/transport_layer/etcp_connections.h

@ -391,6 +391,12 @@ int etcp_send_ping_to_socket(struct UTUN_INSTANCE* instance, struct ETCP_SOCKET*
const uint8_t* user_data, size_t user_data_len, const uint8_t* user_data, size_t user_data_len,
uint8_t flags); uint8_t flags);
// TCP-ping: подключение к TCP-адресу peer + хендшейк с флагом PING. RTT меряется
// по хендшейку (send→response). cb вызывается один раз (success=1/rtt или success=0).
int etcp_send_tcp_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);
void etcp_connections_read_callback_socket(socket_t sock, void* arg); void etcp_connections_read_callback_socket(socket_t sock, void* arg);

1
src/transport_layer/stcp.c

@ -143,6 +143,7 @@ void stcp_write_cb(socket_t sock, void *arg) {
if (c->send_offset >= c->send_len) { if (c->send_offset >= c->send_len) {
u_free(c->send_buf); c->send_buf = NULL; c->send_len = 0; c->send_offset = 0; u_free(c->send_buf); c->send_buf = NULL; c->send_len = 0; c->send_offset = 0;
uasync_set_socket_write(c->ua, c->socket_id, 0); uasync_set_socket_write(c->ua, c->socket_id, 0);
if (c->close_after_send) { stcp_conn_do_close(c, 0); return; }
stcp_flush_pending(c); stcp_flush_pending(c);
} }
} }

16
src/transport_layer/stcp.h

@ -18,16 +18,20 @@ extern "C" {
struct UTUN_INSTANCE; struct UTUN_INSTANCE;
struct ETCP_CONN; struct ETCP_CONN;
typedef void (*stcp_ping_cb)(int success, uint16_t rtt, void *arg);
#define STCP_MAX_MSG_SIZE 65535 #define STCP_MAX_MSG_SIZE 65535
#define STCP_RECV_BUF_INIT 8192 #define STCP_RECV_BUF_INIT 8192
#define STCP_RECV_BUF_MAX 131072 #define STCP_RECV_BUF_MAX 131072
#define STCP_HS_TIMEOUT 50000 // 5s in 0.1ms timebase units #define STCP_HS_TIMEOUT 50000 // 5s in 0.1ms timebase units
#define STCP_CONNECT_TIMEOUT 100000 // 10s 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+46=118 #define STCP_HS_CLIENT_MIN (SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT) // 40+47=87
#define STCP_HS_SERVER_MIN (SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER) // 72+46=118 #define STCP_HS_SERVER_MIN (SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER) // 40+47=87
#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_CLIENT 47 // ed25519_pubkey(32) + got_initial_pkt(1) + session_id(4) + padding_size(2) + device_type(1) + keepalive(2) + flags(1) + 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_HS_ENC_SERVER 47 // ed25519_pubkey(32) + got_initial_pkt(1) + session_id(4) + padding_size(2) + device_type(1) + keepalive(2) + flags(1) + CRC32(4)
#define STCP_HANDSHAKE_FLAG_PING 0x01
#define STCP_STREAM_CLIENT_SEND 0 #define STCP_STREAM_CLIENT_SEND 0
#define STCP_STREAM_SERVER_SEND 1 #define STCP_STREAM_SERVER_SEND 1
@ -82,6 +86,10 @@ struct stcp_conn {
uint16_t keepalive_interval; // keepalive interval 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 uint8_t peer_device_type; // CLIENT_TYPE_* from peer handshake
uint16_t peer_keepalive_interval; // keepalive interval from peer handshake uint16_t peer_keepalive_interval; // keepalive interval from peer handshake
uint8_t hs_flags; // client: handshake flags to send (STCP_HANDSHAKE_FLAG_*)
uint8_t peer_flags; // flags received from peer handshake
uint64_t hs_send_time; // client: handshake send time (0.1ms tb), for ping RTT
uint8_t close_after_send; // server: graceful close once pending send is flushed
struct ll_queue *rx_queue; struct ll_queue *rx_queue;
struct ll_queue *tx_queue; struct ll_queue *tx_queue;

67
src/transport_layer/stcp_client.c

@ -23,6 +23,9 @@ struct stcp_client {
struct UASYNC *ua; struct UASYNC *ua;
stcp_ready_cb ready_cb; stcp_ready_cb ready_cb;
void *ready_arg; void *ready_arg;
stcp_ping_cb ping_cb;
void *ping_arg;
uint8_t ping_done;
uint8_t peer_pubkey[SC_PUBKEY_SIZE]; uint8_t peer_pubkey[SC_PUBKEY_SIZE];
uint8_t my_ed25519_pubkey[SC_PUBKEY_SIZE]; uint8_t my_ed25519_pubkey[SC_PUBKEY_SIZE];
}; };
@ -57,21 +60,23 @@ static void client_send_handshake(struct stcp_conn *c, const uint8_t *server_pub
uint8_t gop = c->etcp_conn ? c->etcp_conn->got_initial_pkt : c->got_initial_pkt; uint8_t gop = c->etcp_conn ? c->etcp_conn->got_initial_pkt : c->got_initial_pkt;
uint32_t sid = c->etcp_conn ? c->etcp_conn->session_id : c->session_id; uint32_t sid = c->etcp_conn ? c->etcp_conn->session_id : c->session_id;
uint8_t plain[42]; memcpy(plain, my_ed25519, 32); uint8_t plain[43]; memcpy(plain, my_ed25519, 32);
plain[32] = gop; plain[32] = gop;
memcpy(plain + 33, &sid, 4); memcpy(plain + 33, &sid, 4);
plain[37] = (uint8_t)padding; plain[38] = (uint8_t)(padding >> 8); plain[37] = (uint8_t)padding; plain[38] = (uint8_t)(padding >> 8);
plain[39] = c->device_type; plain[39] = c->device_type;
*(uint16_t*)(plain + 40) = htobe16(c->keepalive_interval); *(uint16_t*)(plain + 40) = htobe16(c->keepalive_interval);
uint32_t crc = crc32_calc(plain, 42); plain[42] = c->hs_flags;
uint32_t crc = crc32_calc(plain, 43);
uint8_t *enc_dst = hs + SC_PUBKEY_ENC_SIZE; uint8_t *enc_dst = hs + SC_PUBKEY_ENC_SIZE;
memcpy(enc_dst, plain, 42); memcpy(enc_dst, plain, 43);
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); enc_dst[43] = (uint8_t)(crc >> 0); enc_dst[44] = (uint8_t)(crc >> 8); enc_dst[45] = (uint8_t)(crc >> 16); enc_dst[46] = (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; } 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); for (int i = 0; i < padding; i++) hs[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_CLIENT + i] = (uint8_t)(salt[0] ^ i);
c->hs_send_time = get_time_tb();
c->state = STCP_STATE_HS_CLIENT_SENT; c->state = STCP_STATE_HS_CLIENT_SENT;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_client: handshake sent (%zu bytes) gop=%d sid=%08x, entering HS_CLIENT_SENT", total, gop, sid); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_client: handshake sent (%zu bytes) gop=%d sid=%08x flags=%02x, entering HS_CLIENT_SENT", total, gop, sid, c->hs_flags);
c->send_buf = hs; c->send_len = total; c->send_offset = 0; c->send_buf = hs; c->send_len = total; c->send_offset = 0;
uasync_set_socket_write(c->ua, c->socket_id, 1); uasync_set_socket_write(c->ua, c->socket_id, 1);
} }
@ -101,14 +106,24 @@ static void client_hs_cb(struct stcp_conn *c, uint8_t *data, size_t len) {
uint16_t padding_size = (uint16_t)enc_hs[37] | ((uint16_t)enc_hs[38] << 8); uint16_t padding_size = (uint16_t)enc_hs[37] | ((uint16_t)enc_hs[38] << 8);
c->peer_device_type = enc_hs[39]; c->peer_device_type = enc_hs[39];
c->peer_keepalive_interval = ((uint16_t)enc_hs[40] << 8) | enc_hs[41]; 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_flags = enc_hs[42];
c->peer_got_initial_pkt, c->peer_session_id, padding_size, c->peer_device_type, c->peer_keepalive_interval); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_client: server response OK gop=%d sid=%08x padding=%u dev=%d ka=%u flags=%02x",
c->peer_got_initial_pkt, c->peer_session_id, padding_size, c->peer_device_type, c->peer_keepalive_interval, c->peer_flags);
stcp_recv_set(c, padding_size, 0, client_hs_padding_cb); stcp_recv_set(c, padding_size, 0, client_hs_padding_cb);
} }
static void client_hs_padding_cb(struct stcp_conn *c, uint8_t *data, size_t len) { static void client_hs_padding_cb(struct stcp_conn *c, uint8_t *data, size_t len) {
(void)data; (void)len; (void)data; (void)len;
if (c->hs_timer) { uasync_cancel_timeout(c->ua, c->hs_timer); c->hs_timer = NULL; } if (c->hs_timer) { uasync_cancel_timeout(c->ua, c->hs_timer); c->hs_timer = NULL; }
if (c->hs_flags & STCP_HANDSHAKE_FLAG_PING) {
struct stcp_client *cli = (struct stcp_client *)c;
uint16_t rtt = (uint16_t)(get_time_tb() - c->hs_send_time);
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_client: ping OK rtt=%u sock=%d", (unsigned)rtt, (int)c->sock);
cli->ping_done = 1;
if (cli->ping_cb) cli->ping_cb(1, rtt, cli->ping_arg);
stcp_client_destroy(cli);
return;
}
c->state = STCP_STATE_DATA; c->state = STCP_STATE_DATA;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_client: handshake OK, entering DATA state"); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_client: handshake OK, entering DATA state");
if (c->on_ready) { void (*cb)(struct stcp_conn*, void*) = c->on_ready; c->on_ready = NULL; cb(c, c->ready_arg); } if (c->on_ready) { void (*cb)(struct stcp_conn*, void*) = c->on_ready; c->on_ready = NULL; cb(c, c->ready_arg); }
@ -154,13 +169,17 @@ struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uin
uint8_t got_initial_pkt, uint32_t session_id, uint8_t got_initial_pkt, uint32_t session_id,
struct ETCP_CONN *etcp_conn, struct ETCP_CONN *etcp_conn,
uint8_t device_type, uint16_t keepalive_interval, uint8_t device_type, uint16_t keepalive_interval,
uint8_t hs_flags,
stcp_ready_cb ready_cb, void *arg, stcp_ready_cb ready_cb, void *arg,
stcp_ping_cb ping_cb, void *ping_arg,
stcp_close_cb close_cb, void *close_arg, stcp_close_cb close_cb, void *close_arg,
const struct sockaddr_storage *local_addr) { const struct sockaddr_storage *local_addr,
if (!ua || !addr || !keys || !peer_pubkey || !ready_cb) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "invalid args"); return NULL; } int timeout_ms) {
if (!ua || !addr || !keys || !peer_pubkey || (!ready_cb && !ping_cb)) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "invalid args"); return NULL; }
struct stcp_client *cli = u_calloc(1, sizeof(struct stcp_client)); struct stcp_client *cli = u_calloc(1, sizeof(struct stcp_client));
if (!cli) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "calloc failed"); return NULL; } if (!cli) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "calloc failed"); return NULL; }
cli->ua = ua; cli->ready_cb = ready_cb; cli->ready_arg = arg; cli->ua = ua; cli->ready_cb = ready_cb; cli->ready_arg = arg;
cli->ping_cb = ping_cb; cli->ping_arg = ping_arg;
memcpy(cli->peer_pubkey, peer_pubkey, SC_PUBKEY_SIZE); memcpy(cli->peer_pubkey, peer_pubkey, SC_PUBKEY_SIZE);
if (my_ed25519_pubkey) memcpy(cli->my_ed25519_pubkey, my_ed25519_pubkey, SC_PUBKEY_SIZE); if (my_ed25519_pubkey) memcpy(cli->my_ed25519_pubkey, my_ed25519_pubkey, SC_PUBKEY_SIZE);
struct stcp_conn *c = &cli->conn; struct stcp_conn *c = &cli->conn;
@ -174,6 +193,9 @@ struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uin
c->etcp_conn = etcp_conn; c->etcp_conn = etcp_conn;
c->device_type = device_type; c->device_type = device_type;
c->keepalive_interval = keepalive_interval; c->keepalive_interval = keepalive_interval;
c->hs_flags = hs_flags;
int hs_tb = (timeout_ms > 0) ? timeout_ms * 10 : STCP_CONNECT_TIMEOUT;
struct addrinfo hints = {0}; struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
@ -202,12 +224,12 @@ struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uin
} }
c->socket_id = uasync_add_socket_t(ua, c->sock, NULL, client_connect_write_cb, NULL, cli); c->socket_id = uasync_add_socket_t(ua, c->sock, NULL, client_connect_write_cb, NULL, cli);
if (!c->socket_id) { socket_close_wrapper(c->sock); u_free(cli); return NULL; } if (!c->socket_id) { socket_close_wrapper(c->sock); u_free(cli); return NULL; }
c->hs_timer = uasync_set_timeout(ua, STCP_CONNECT_TIMEOUT, c, hs_timeout_cb, "stcp_hs"); c->hs_timer = uasync_set_timeout(ua, hs_tb, c, hs_timeout_cb, "stcp_hs");
} else { } else {
int opt = 1; setsockopt(c->sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&opt, sizeof(opt)); int opt = 1; setsockopt(c->sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&opt, sizeof(opt));
c->socket_id = uasync_add_socket_t(ua, c->sock, client_conn_read_cb, stcp_write_cb, NULL, c); c->socket_id = uasync_add_socket_t(ua, c->sock, client_conn_read_cb, stcp_write_cb, NULL, c);
if (!c->socket_id) { socket_close_wrapper(c->sock); u_free(cli); return NULL; } if (!c->socket_id) { socket_close_wrapper(c->sock); u_free(cli); return NULL; }
c->hs_timer = uasync_set_timeout(ua, STCP_CONNECT_TIMEOUT, c, hs_timeout_cb, "stcp_hs"); c->hs_timer = uasync_set_timeout(ua, hs_tb, c, hs_timeout_cb, "stcp_hs");
if (client_derive_session(c, cli->peer_pubkey)) { c->free_on_close = cli; stcp_conn_do_close(c, 1); return NULL; } if (client_derive_session(c, cli->peer_pubkey)) { c->free_on_close = cli; stcp_conn_do_close(c, 1); return NULL; }
client_send_handshake(c, cli->peer_pubkey, cli->my_ed25519_pubkey); client_send_handshake(c, cli->peer_pubkey, cli->my_ed25519_pubkey);
stcp_recv_set(c, SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER, 0, client_hs_cb); stcp_recv_set(c, SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER, 0, client_hs_cb);
@ -224,3 +246,26 @@ void stcp_client_destroy(struct stcp_client *cli) {
struct stcp_conn *stcp_client_get_conn(struct stcp_client *cli) { struct stcp_conn *stcp_client_get_conn(struct stcp_client *cli) {
return cli ? &cli->conn : NULL; return cli ? &cli->conn : NULL;
} }
static void ping_close_cb(struct stcp_conn *c, int err, void *arg) {
(void)arg;
struct stcp_client *cli = (struct stcp_client *)c;
if (cli->ping_cb && !cli->ping_done) {
cli->ping_done = 1;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "stcp_client: ping failed err=%d sock=%d", err, (int)c->sock);
cli->ping_cb(0, 0, cli->ping_arg);
}
}
struct stcp_client *stcp_ping_send(struct UASYNC *ua, const char *addr, uint16_t port,
struct SC_MYKEYS *keys, const uint8_t *peer_pubkey,
const uint8_t *my_ed25519_pubkey,
uint8_t device_type, uint16_t keepalive_interval,
int timeout_ms, stcp_ping_cb cb, void *arg) {
struct stcp_client *cli = stcp_client_connect(ua, addr, port, keys, peer_pubkey, my_ed25519_pubkey,
0, 0, NULL, device_type, keepalive_interval,
STCP_HANDSHAKE_FLAG_PING,
NULL, NULL, cb, arg, ping_close_cb, NULL, NULL, timeout_ms);
if (cli) cli->conn.free_on_close = cli;
return cli;
}

13
src/transport_layer/stcp_client.h

@ -18,9 +18,20 @@ struct stcp_client *stcp_client_connect(struct UASYNC *ua, const char *addr, uin
uint8_t got_initial_pkt, uint32_t session_id, uint8_t got_initial_pkt, uint32_t session_id,
struct ETCP_CONN *etcp_conn, struct ETCP_CONN *etcp_conn,
uint8_t device_type, uint16_t keepalive_interval, uint8_t device_type, uint16_t keepalive_interval,
uint8_t hs_flags,
stcp_ready_cb ready_cb, void *arg, stcp_ready_cb ready_cb, void *arg,
stcp_ping_cb ping_cb, void *ping_arg,
stcp_close_cb close_cb, void *close_arg, stcp_close_cb close_cb, void *close_arg,
const struct sockaddr_storage *local_addr); const struct sockaddr_storage *local_addr,
int timeout_ms);
/* TCP-ping: подключается и шлёт хендшейк с флагом STCP_HANDSHAKE_FLAG_PING.
* Ответчик отвечает обычным хендшейк-ответом и мягко закрывает соединение.
* По завершении хендшейка вызывается cb(success=1, rtt, arg); при ошибке/таймауте — cb(0, 0, arg). */
struct stcp_client *stcp_ping_send(struct UASYNC *ua, const char *addr, uint16_t port,
struct SC_MYKEYS *keys, const uint8_t *peer_pubkey,
const uint8_t *my_ed25519_pubkey,
uint8_t device_type, uint16_t keepalive_interval,
int timeout_ms, stcp_ping_cb cb, void *arg);
void stcp_client_destroy(struct stcp_client *cli); void stcp_client_destroy(struct stcp_client *cli);
struct stcp_conn *stcp_client_get_conn(struct stcp_client *cli); struct stcp_conn *stcp_client_get_conn(struct stcp_client *cli);

3
src/transport_layer/stcp_link.c

@ -235,7 +235,8 @@ struct stcp_link *stcp_link_connect(struct ETCP_LINK *etcp_link,
inst->my_ed25519_pubkey, inst->my_ed25519_pubkey,
etcp->got_initial_pkt, etcp->session_id, etcp, etcp->got_initial_pkt, etcp->session_id, etcp,
inst->client_type, inst->keepalive_interval, inst->client_type, inst->keepalive_interval,
client_ready_cb, link, NULL, NULL, local_addr); 0,
client_ready_cb, link, NULL, NULL, NULL, NULL, local_addr, 0);
if (!link->cli) { u_free(link); return NULL; } if (!link->cli) { u_free(link); return NULL; }
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_link: connecting to %s:%u pubkey=%016llx bind=%s", DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_link: connecting to %s:%u pubkey=%016llx bind=%s",

24
src/transport_layer/stcp_server.c

@ -80,8 +80,9 @@ static void server_hs_phase1_cb(struct stcp_conn *c, uint8_t *data, size_t len)
uint16_t padding_size = (uint16_t)enc_hs[37] | ((uint16_t)enc_hs[38] << 8); uint16_t padding_size = (uint16_t)enc_hs[37] | ((uint16_t)enc_hs[38] << 8);
c->peer_device_type = enc_hs[39]; c->peer_device_type = enc_hs[39];
c->peer_keepalive_interval = ((uint16_t)enc_hs[40] << 8) | enc_hs[41]; 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_flags = enc_hs[42];
c->peer_got_initial_pkt, c->peer_session_id, padding_size, c->peer_device_type, c->peer_keepalive_interval); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: client handshake OK gop=%d sid=%08x padding=%u dev=%d ka=%u flags=%02x",
c->peer_got_initial_pkt, c->peer_session_id, padding_size, c->peer_device_type, c->peer_keepalive_interval, c->peer_flags);
stcp_recv_set(c, padding_size, 0, server_hs_phase2_cb); stcp_recv_set(c, padding_size, 0, server_hs_phase2_cb);
} }
@ -114,16 +115,17 @@ static void server_hs_phase2_cb(struct stcp_conn *c, uint8_t *data, size_t len)
memcpy(resp, salt2, SC_PUBKEY_ENC_SALT_SIZE); 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); sc_obfuscate_pubkey(salt2, c->peer_pubkey, c->my_keys.public_key, resp + SC_PUBKEY_ENC_SALT_SIZE);
uint8_t plain_hs[42]; memcpy(plain_hs, c->my_ed25519_pubkey, SC_PUBKEY_SIZE); uint8_t plain_hs[43]; memcpy(plain_hs, c->my_ed25519_pubkey, SC_PUBKEY_SIZE);
plain_hs[32] = server_gop; plain_hs[32] = server_gop;
memcpy(plain_hs + 33, &server_sid, 4); memcpy(plain_hs + 33, &server_sid, 4);
plain_hs[37] = (uint8_t)padding; plain_hs[38] = (uint8_t)(padding >> 8); plain_hs[37] = (uint8_t)padding; plain_hs[38] = (uint8_t)(padding >> 8);
plain_hs[39] = c->device_type; plain_hs[39] = c->device_type;
*(uint16_t*)(plain_hs + 40) = htobe16(c->keepalive_interval); *(uint16_t*)(plain_hs + 40) = htobe16(c->keepalive_interval);
uint32_t crc = crc32_calc(plain_hs, 42); plain_hs[42] = (c->peer_flags & STCP_HANDSHAKE_FLAG_PING);
uint32_t crc = crc32_calc(plain_hs, 43);
uint8_t *enc_dst = resp + SC_PUBKEY_ENC_SIZE; uint8_t *enc_dst = resp + SC_PUBKEY_ENC_SIZE;
memcpy(enc_dst, plain_hs, 42); memcpy(enc_dst, plain_hs, 43);
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); enc_dst[43] = (uint8_t)(crc >> 0); enc_dst[44] = (uint8_t)(crc >> 8); enc_dst[45] = (uint8_t)(crc >> 16); enc_dst[46] = (uint8_t)(crc >> 24);
log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_server hs_resp BEFORE xor", enc_dst, STCP_HS_ENC_SERVER); 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) { if (sc_stream_xor(&c->stream_send, enc_dst, STCP_HS_ENC_SERVER) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "encrypt failed"); DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "encrypt failed");
@ -132,11 +134,17 @@ static void server_hs_phase2_cb(struct stcp_conn *c, uint8_t *data, size_t len)
log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_server hs_resp AFTER xor", enc_dst, STCP_HS_ENC_SERVER); log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CRYPTO, "stcp_server hs_resp AFTER xor", enc_dst, STCP_HS_ENC_SERVER);
for (int i = 0; i < padding; i++) resp[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER + i] = (uint8_t)(salt2[0] ^ i); for (int i = 0; i < padding; i++) resp[SC_PUBKEY_ENC_SIZE + STCP_HS_ENC_SERVER + i] = (uint8_t)(salt2[0] ^ i);
int r = stcp_try_send(c, resp, total_resp);
if (r < 0) { u_free(resp); stcp_conn_do_close(c, 3); return; }
if (c->peer_flags & STCP_HANDSHAKE_FLAG_PING) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: ping answered, graceful close sock=%d", (int)c->sock);
if (r == 0) { stcp_conn_do_close(c, 0); return; }
c->close_after_send = 1;
return;
}
c->state = STCP_STATE_DATA; c->state = STCP_STATE_DATA;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: handshake OK, entering DATA state"); DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_server: handshake OK, entering DATA state");
log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_ETCP, "stcp_server FULL RESP", resp, total_resp); log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_ETCP, "stcp_server FULL RESP", resp, total_resp);
int r = stcp_try_send(c, resp, total_resp);
if (r < 0) { u_free(resp); stcp_conn_do_close(c, 3); return; }
if (c->on_ready) c->on_ready(c, c->ready_arg); if (c->on_ready) c->on_ready(c, c->ready_arg);
stcp_recv_set(c, 0, 1, server_data_cb); stcp_recv_set(c, 0, 1, server_data_cb);
} }

5
tests/Makefile.am

@ -35,6 +35,7 @@ check_PROGRAMS = \
test_etcp_router_reconnect \ test_etcp_router_reconnect \
test_etcp_bbr \ test_etcp_bbr \
test_etcp_ping \ test_etcp_ping \
test_stcp_ping \
test_route_ping \ test_route_ping \
test_nat_detection \ test_nat_detection \
test_nat_engine \ test_nat_engine \
@ -205,6 +206,10 @@ test_etcp_ping_SOURCES = test_etcp_ping.c
test_etcp_ping_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_etcp_ping_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_etcp_ping_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) test_etcp_ping_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
test_stcp_ping_SOURCES = test_stcp_ping.c
test_stcp_ping_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_stcp_ping_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
test_route_ping_SOURCES = test_route_ping.c test_route_ping_SOURCES = test_route_ping.c
test_route_ping_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_route_ping_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
test_route_ping_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) test_route_ping_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)

16
tests/test_stcp.c

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

4
tests/test_stcp_link.c

@ -52,7 +52,7 @@ static int test1_basic(void) {
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", BASE_PORT + 1, &c_keys, struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", BASE_PORT + 1, &c_keys,
s_keys.public_key, NULL, s_keys.public_key, NULL,
0, 0, NULL, 0, 0, NULL,
0, 200, on_cli_ready, &cli_ready, NULL, NULL, NULL); 0, 200, 0, on_cli_ready, &cli_ready, NULL, NULL, NULL, NULL, NULL, 0);
TASSERT(sc); TASSERT(sc);
int ticks = 0; int ticks = 0;
@ -81,7 +81,7 @@ static int test2_wrong_key(void) {
struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", BASE_PORT + 2, &c_keys, struct stcp_client *sc = stcp_client_connect(ua, "127.0.0.1", BASE_PORT + 2, &c_keys,
rogue.public_key, NULL, rogue.public_key, NULL,
0, 0, NULL, 0, 0, NULL,
0, 200, on_cli_ready, &cli_ready, NULL, NULL, NULL); 0, 200, 0, on_cli_ready, &cli_ready, NULL, NULL, NULL, NULL, NULL, 0);
TASSERT(sc); TASSERT(sc);
int ticks = 0; int ticks = 0;

97
tests/test_stcp_ping.c

@ -0,0 +1,97 @@
// test_stcp_ping.c — TCP-ping (etcp_send_tcp_ping / stcp_ping_send) end-to-end check
#include "stcp_client.h"
#include "etcp_connections.h"
#include "etcp.h"
#include "topo_group.h"
#include "topo_node.h"
#include "../src/utun_instance.h"
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
static int ping_ok = -1, ping_rtt = 0;
static void ping_cb(int success, uint16_t rtt, void *arg, uint64_t nonce,
const uint8_t *resp_data, size_t resp_data_len) {
(void)arg; (void)nonce; (void)resp_data; (void)resp_data_len;
ping_ok = success; ping_rtt = rtt;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "TCP ping cb: success=%d rtt=%u", success, rtt);
}
static char *build_config(const char *node_id, const char *priv, const char *pub, int port) {
static char buf[512];
snprintf(buf, sizeof(buf),
"[global]\n"
"my_node_id=0x%s\n"
"my_private_key=%s\n"
"my_public_key=%s\n"
"\n"
"[server: stcp]\n"
"addr=127.0.0.1:%d\n"
"type=public\n"
"transport=tcp\n",
node_id, priv, pub, port);
return buf;
}
int main(void) {
debug_config_init();
debug_set_level(DEBUG_LEVEL_INFO);
debug_set_categories(DEBUG_CATEGORY_GENERAL | DEBUG_CATEGORY_ETCP | DEBUG_CATEGORY_CONNECTION);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "=== TCP ping (etcp_send_tcp_ping) ===");
srand((unsigned)time(NULL));
struct UASYNC *ua = uasync_create();
if (!ua) return 1;
utun_instance_set_tun_init_enabled(0);
int port = 27000 + rand() % 1000;
struct UTUN_INSTANCE *srv = utun_instance_create_from_str(ua, build_config(
"AAAAAAAAAAAAAAAA",
"38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68",
"ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a", port));
if (!srv || utun_instance_init(srv) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "srv init failed"); return 1; }
struct UTUN_INSTANCE *cli = utun_instance_create_from_str(ua, build_config(
"BBBBBBBBBBBBBBBB",
"704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f",
"b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01", port + 1));
if (!cli || utun_instance_init(cli) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "cli init failed"); return 1; }
uint8_t srv_pub[SC_PUBKEY_SIZE];
memcpy(srv_pub, srv->my_keys.public_key, SC_PUBKEY_SIZE);
/* success path: ping listening STCP server */
struct sockaddr_storage addr; memset(&addr, 0, sizeof(addr));
struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
sin->sin_family = AF_INET; sin->sin_addr.s_addr = htonl(0x7f000001); sin->sin_port = htons(port);
int rc = etcp_send_tcp_ping(cli, srv_pub, &addr, 2000, ping_cb, NULL);
if (rc != 0) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "etcp_send_tcp_ping failed rc=%d", rc); return 1; }
int ticks = 0;
while (ping_ok < 0 && ticks < 5000) { uasync_poll(ua, 10); ticks++; }
if (ping_ok != 1) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "=== FAILED (success path): success=%d rtt=%u ===", ping_ok, ping_rtt); return 1; }
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "=== success path PASSED: rtt=%u ===", ping_rtt);
/* failure path: port with no listener */
ping_ok = -1; ping_rtt = 0;
struct sockaddr_storage bad_addr; memset(&bad_addr, 0, sizeof(bad_addr));
struct sockaddr_in *bsin = (struct sockaddr_in *)&bad_addr;
bsin->sin_family = AF_INET; bsin->sin_addr.s_addr = htonl(0x7f000001); bsin->sin_port = htons(port + 5000);
rc = etcp_send_tcp_ping(cli, srv_pub, &bad_addr, 1000, ping_cb, NULL);
ticks = 0;
while (ping_ok < 0 && ticks < 3000) { uasync_poll(ua, 10); ticks++; }
if (ping_ok != 0) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "=== FAILED (failure path): success=%d rtt=%u rc=%d ===", ping_ok, ping_rtt, rc); return 1; }
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "=== failure path PASSED: success=%d ===", ping_ok);
srv->running = 0; cli->running = 0;
utun_instance_destroy(srv); utun_instance_destroy(cli);
uasync_destroy(ua, 0);
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "=== PASSED ===");
return 0;
}
Loading…
Cancel
Save