diff --git a/src/routing_layer/route_connectivity.c b/src/routing_layer/route_connectivity.c index 4d22c9db..1dbee45f 100644 --- a/src/routing_layer/route_connectivity.c +++ b/src/routing_layer/route_connectivity.c @@ -29,6 +29,7 @@ struct conn_probe_ctx { struct TOPO_GROUP* group; struct TOPO_GROUP_NODE* nq; uint8_t addr_type; // ADDR_TYPE_* + uint8_t is_tcp; // 1 = TCP-ping (stcp_ping_send), 0 = UDP-ping struct sockaddr_storage target_addr; uint8_t peer_pubkey[SC_PUBKEY_SIZE]; @@ -37,7 +38,7 @@ struct conn_probe_ctx { uint8_t candidate_index; 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_ok; 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 e_sock = instance->etcp_sockets; 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) { struct sockaddr_in6* if_sin6 = (struct sockaddr_in6*)&e_sock->interface_addr; 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 e_sock = instance->etcp_sockets; 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; } int dup = 0; 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) 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; } int match = 0; 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: все остальные подходящие e_sock = instance->etcp_sockets; 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; } int dup = 0; 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) e_sock = instance->etcp_sockets; 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; } int dup = 0; 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 ---- +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) { if (ctx->candidate_index >= ctx->candidate_count) { // все сокеты перебраны @@ -192,25 +209,14 @@ static void conn_probe_start_series(struct conn_probe_ctx* ctx) { return; } - struct ETCP_SOCKET* sock = ctx->candidate_sockets[ctx->candidate_index]; ctx->count_sent = 0; ctx->count_ok = 0; ctx->min_rtt = 65535; - static const char* atype_names[] = {"INTERFACE", "NAT", "REAL"}; - 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); - + int ret = conn_probe_send(ctx); if (ret != 0) { - DEBUG_ERROR(DEBUG_CATEGORY_BGP, "probe: cannot start ping from socket %s", sock->name); - ctx->count_sent = 3; // simulate full failure + DEBUG_ERROR(DEBUG_CATEGORY_BGP, "probe: cannot start ping (tcp=%d)", ctx->is_tcp); + ctx->count_sent = ctx->count_total; // simulate full failure ctx->candidate_index++; 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; } - DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "probe ping: ok=%d rtt=%u sent=%d/%d socket=%s", - success, rtt, ctx->count_sent, ctx->count_total, - ctx->candidate_sockets[ctx->candidate_index]->name); + DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "probe ping: ok=%d rtt=%u sent=%d/%d tcp=%d", + success, rtt, ctx->count_sent, ctx->count_total, ctx->is_tcp); if (ctx->count_sent < ctx->count_total) { - // продолжаем с тем же сокетом - 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); + int ret = conn_probe_send(ctx); 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_ok = 0; } else return; // следующий пинг отправлен, ждём callback } - // серия из 3 пингов завершена + // серия пингов завершена if (ctx->count_ok > 0) { if (ctx->min_rtt < ctx->best_across_sockets) ctx->best_across_sockets = ctx->min_rtt; 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_in* sin = (struct sockaddr_in*)⌖ 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; 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->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->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*)); + 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++; 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*)⌖ sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, a6->addr, 16); sin6->sin6_port = htons(a6->port); - struct ETCP_SOCKET* candidates[CONN_MAX_SOCKET_CANDIDATES]; - int cand_count = conn_match_candidate_sockets(instance, a6->type, &target, candidates, CONN_MAX_SOCKET_CANDIDATES); - if (cand_count == 0) { DEBUG_INFO(DEBUG_CATEGORY_BGP, "probe: no local sockets for IPv6 target type=%d", a6->type); continue; } - struct conn_probe_ctx* ctx = u_calloc(1, sizeof(struct conn_probe_ctx)); if (!ctx) continue; ctx->instance = instance; ctx->nq = nq; ctx->addr_type = a6->type; ctx->target_addr = target; memcpy(ctx->peer_pubkey, ni->public_key, SC_PUBKEY_SIZE); - memcpy(ctx->candidate_sockets, candidates, cand_count * sizeof(struct ETCP_SOCKET*)); - ctx->candidate_count = cand_count; ctx->candidate_index = 0; ctx->count_total = CONN_PROBE_COUNT; ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535; + ctx->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*)); + 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++; conn_probe_start_series(ctx); } diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c index f619581e..70f6c8b2 100644 --- a/src/transport_layer/etcp_connections.c +++ b/src/transport_layer/etcp_connections.c @@ -17,6 +17,7 @@ #include "etcp.h" #include "stcp_link.h" #include "stcp.h" +#include "stcp_client.h" #include "topo_node.h" #include "topo_group.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"); 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) { DEBUG_ERROR(DEBUG_CATEGORY_CONNECTION, "user_data too long"); 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"); 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); - 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); + if (send_rc < 0) { + if (ctx->user_data) u_free(ctx->user_data); + u_free(ctx); + return -6; + } if (instance->pending_pings == NULL) { instance->pending_pings = ctx; } 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); } +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 === 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) { diff --git a/src/transport_layer/etcp_connections.h b/src/transport_layer/etcp_connections.h index e7378a20..95d4c9bf 100644 --- a/src/transport_layer/etcp_connections.h +++ b/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, 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); diff --git a/src/transport_layer/stcp.c b/src/transport_layer/stcp.c index 8205fa21..9fd4d23d 100644 --- a/src/transport_layer/stcp.c +++ b/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) { 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); + if (c->close_after_send) { stcp_conn_do_close(c, 0); return; } stcp_flush_pending(c); } } diff --git a/src/transport_layer/stcp.h b/src/transport_layer/stcp.h index 41be6c2a..61f15125 100644 --- a/src/transport_layer/stcp.h +++ b/src/transport_layer/stcp.h @@ -18,16 +18,20 @@ extern "C" { struct UTUN_INSTANCE; struct ETCP_CONN; +typedef void (*stcp_ping_cb)(int success, uint16_t rtt, void *arg); + #define STCP_MAX_MSG_SIZE 65535 #define STCP_RECV_BUF_INIT 8192 #define STCP_RECV_BUF_MAX 131072 #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+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_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) // 40+47=87 +#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 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_SERVER_SEND 1 @@ -82,6 +86,10 @@ struct stcp_conn { 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 + 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 *tx_queue; diff --git a/src/transport_layer/stcp_client.c b/src/transport_layer/stcp_client.c index 1cec671d..fe4bb3a6 100644 --- a/src/transport_layer/stcp_client.c +++ b/src/transport_layer/stcp_client.c @@ -23,6 +23,9 @@ struct stcp_client { struct UASYNC *ua; stcp_ready_cb ready_cb; void *ready_arg; + stcp_ping_cb ping_cb; + void *ping_arg; + uint8_t ping_done; uint8_t peer_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; 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; memcpy(plain + 33, &sid, 4); plain[37] = (uint8_t)padding; plain[38] = (uint8_t)(padding >> 8); plain[39] = c->device_type; *(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; - 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); + memcpy(enc_dst, plain, 43); + 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; } 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; - 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; 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); 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); + c->peer_flags = enc_hs[42]; + 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); } static void client_hs_padding_cb(struct stcp_conn *c, uint8_t *data, size_t len) { (void)data; (void)len; 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; 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); } @@ -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, struct ETCP_CONN *etcp_conn, uint8_t device_type, uint16_t keepalive_interval, + uint8_t hs_flags, stcp_ready_cb ready_cb, void *arg, + stcp_ping_cb ping_cb, void *ping_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; } + const struct sockaddr_storage *local_addr, + 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)); if (!cli) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "calloc failed"); return NULL; } 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); if (my_ed25519_pubkey) memcpy(cli->my_ed25519_pubkey, my_ed25519_pubkey, SC_PUBKEY_SIZE); 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->device_type = device_type; 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}; 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); 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 { 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); 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; } 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); @@ -224,3 +246,26 @@ void stcp_client_destroy(struct stcp_client *cli) { struct stcp_conn *stcp_client_get_conn(struct stcp_client *cli) { 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; +} diff --git a/src/transport_layer/stcp_client.h b/src/transport_layer/stcp_client.h index 7f0a23a3..5b16ed50 100644 --- a/src/transport_layer/stcp_client.h +++ b/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, struct ETCP_CONN *etcp_conn, uint8_t device_type, uint16_t keepalive_interval, + uint8_t hs_flags, stcp_ready_cb ready_cb, void *arg, + stcp_ping_cb ping_cb, void *ping_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); 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 f935ce21..7bca8b26 100644 --- a/src/transport_layer/stcp_link.c +++ b/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, etcp->got_initial_pkt, etcp->session_id, etcp, 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; } DEBUG_INFO(DEBUG_CATEGORY_ETCP, "stcp_link: connecting to %s:%u pubkey=%016llx bind=%s", diff --git a/src/transport_layer/stcp_server.c b/src/transport_layer/stcp_server.c index ba5cc749..7ce59744 100644 --- a/src/transport_layer/stcp_server.c +++ b/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); 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); + c->peer_flags = enc_hs[42]; + 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); } @@ -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); 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; memcpy(plain_hs + 33, &server_sid, 4); plain_hs[37] = (uint8_t)padding; plain_hs[38] = (uint8_t)(padding >> 8); plain_hs[39] = c->device_type; *(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; - 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); + memcpy(enc_dst, plain_hs, 43); + 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); if (sc_stream_xor(&c->stream_send, enc_dst, STCP_HS_ENC_SERVER) != SC_OK) { 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); 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; 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); - 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); stcp_recv_set(c, 0, 1, server_data_cb); } diff --git a/tests/Makefile.am b/tests/Makefile.am index f653ce56..d15272d3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -35,6 +35,7 @@ check_PROGRAMS = \ test_etcp_router_reconnect \ test_etcp_bbr \ test_etcp_ping \ + test_stcp_ping \ test_route_ping \ test_nat_detection \ 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_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_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_route_ping_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS) diff --git a/tests/test_stcp.c b/tests/test_stcp.c index 762e5547..6ad94505 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, 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}; 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, 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; 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, 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; 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, 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; 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, 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]); } @@ -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, 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; 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, 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 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, 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; while ((!srv.ready || !cli.ready) && ticks < 200) { uasync_poll(ua, 10); ticks++; } diff --git a/tests/test_stcp_link.c b/tests/test_stcp_link.c index 47329014..b9b4e33e 100644 --- a/tests/test_stcp_link.c +++ b/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, s_keys.public_key, 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); 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, rogue.public_key, 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); int ticks = 0; diff --git a/tests/test_stcp_ping.c b/tests/test_stcp_ping.c new file mode 100644 index 00000000..f768691c --- /dev/null +++ b/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 +#include +#include +#include + +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; +}