#include #include #include #ifdef _WIN32 #include #include #else #include #endif #include "../lib/platform_compat.h" #include "../lib/debug_config.h" #include "../lib/mem.h" #include "utun_instance.h" #include "etcp_api.h" #include "etcp.h" #include "etcp_connections.h" #include "route_node.h" #include "route_bgp.h" #include "route_ping.h" struct route_ping_sock_ctx { struct ETCP_SOCKET* local_sock; uint16_t avg_rtt; // средний RTT в 0.1ms uint8_t count_sent; uint8_t count_ok; }; struct route_ping_req { struct ETCP_CONN* reply_conn; uint64_t request_id; struct NODEINFO_Q* target_node; uint8_t socket_count; uint8_t completed_count; uint16_t timeout_ms; uint8_t recv_ipv4[4]; // IP:port с которого получен PING_REQ (STUN-like) uint16_t recv_port; // network byte order struct route_ping_sock_ctx sockets[0]; }; struct route_ping_series { struct route_ping_req* req; struct route_ping_sock_ctx* sock_ctx; struct ETCP_SOCKET* local_sock; struct sockaddr_storage target_addr; uint8_t pubkey[SC_PUBKEY_SIZE]; uint8_t count_total; uint8_t count_sent; uint8_t count_ok; uint32_t rtt_sum; uint16_t timeout_ms; uint16_t interval_ms; void* next_timer; }; struct route_ping_pending { struct route_ping_pending* next; struct ROUTE_BGP* bgp; uint64_t request_id; route_ping_callback_t callback; void* arg; void* timeout_timer; uint8_t cancelled; }; static void route_ping_send_resp(struct route_ping_req* req, uint16_t avg_rtt); static void route_ping_next(void* arg); static void route_ping_finish(struct route_ping_req* req); static void route_ping_pending_timeout(void* arg) { struct route_ping_pending* p = (struct route_ping_pending*)arg; if (!p) return; p->timeout_timer = NULL; if (p->cancelled) { u_free(p); return; } struct ROUTE_BGP* bgp = p->bgp; struct route_ping_pending** cur = &bgp->ping_pending; while (*cur) { if (*cur == p) { *cur = p->next; break; } cur = &(*cur)->next; } if (p->callback) { p->callback(0, 0, 0, 0, 0, 0, p->arg); } u_free(p); } static void route_ping_series_free(struct route_ping_series* series) { if (!series) return; if (series->next_timer) { uasync_cancel_timeout(series->req->reply_conn->instance->ua, series->next_timer); series->next_timer = NULL; } u_free(series); } static void route_ping_cb(int success, uint16_t rtt, void* arg, uint64_t nonce, const uint8_t* resp_data, size_t resp_data_len) { (void)nonce; (void)resp_data; (void)resp_data_len; struct route_ping_series* series = (struct route_ping_series*)arg; if (!series) return; series->count_sent++; if (success) { series->count_ok++; series->rtt_sum += rtt; } if (series->count_sent < series->count_total && series->req->reply_conn) { if (success) { // Ответ получен — следующий пинг сразу (burst mode) route_ping_next(series); } else { // Таймаут — ждем interval_ms перед retry struct UTUN_INSTANCE* inst = series->req->reply_conn->instance; series->next_timer = uasync_set_timeout(inst->ua, series->interval_ms * 10, series, route_ping_next); } return; } // серия завершена if (series->count_ok > 0) { series->sock_ctx->avg_rtt = (uint16_t)(series->rtt_sum / series->count_ok); } else { series->sock_ctx->avg_rtt = series->timeout_ms * 10; // максимум при полном провале } series->sock_ctx->count_sent = series->count_sent; series->sock_ctx->count_ok = series->count_ok; series->req->completed_count++; if (series->req->completed_count >= series->req->socket_count) { route_ping_finish(series->req); } u_free(series); } static void route_ping_next(void* arg) { struct route_ping_series* series = (struct route_ping_series*)arg; if (!series) return; series->next_timer = NULL; if (!series->req->reply_conn) { u_free(series); return; } int ret = etcp_send_ping_to_socket(series->req->reply_conn->instance, series->local_sock, series->pubkey, &series->target_addr, series->timeout_ms, route_ping_cb, series, NULL, 0); if (ret != 0) { // не удалось отправить, считаем как fail и продолжаем/завершаем series->count_sent++; if (series->count_sent < series->count_total) { struct UTUN_INSTANCE* inst = series->req->reply_conn->instance; series->next_timer = uasync_set_timeout(inst->ua, series->interval_ms * 10, series, route_ping_next); } else { series->sock_ctx->avg_rtt = series->timeout_ms * 10; series->sock_ctx->count_sent = series->count_sent; series->sock_ctx->count_ok = series->count_ok; series->req->completed_count++; if (series->req->completed_count >= series->req->socket_count) { route_ping_finish(series->req); } u_free(series); } } } static void route_ping_finish(struct route_ping_req* req) { if (!req) return; uint32_t best_metric = 0xFFFFFFFF; uint16_t best_avg_rtt = 0; uint8_t best_count_sent = 0; uint8_t best_count_ok = 0; struct ETCP_SOCKET* best_sock = NULL; for (uint8_t i = 0; i < req->socket_count; i++) { struct route_ping_sock_ctx* sc = &req->sockets[i]; uint8_t loss = (sc->count_sent > 0) ? ((sc->count_sent - sc->count_ok) * 100 / sc->count_sent) : 0; uint32_t metric = (uint32_t)sc->avg_rtt * (loss * 3 + 1); if (metric < best_metric) { best_metric = metric; best_avg_rtt = sc->avg_rtt; best_count_sent = sc->count_sent; best_count_ok = sc->count_ok; best_sock = sc->local_sock; } } if (req->target_node) { req->target_node->last_ping_time = get_time_tb(); req->target_node->last_rtt = best_avg_rtt; req->target_node->best_socket = best_sock; DEBUG_INFO(DEBUG_CATEGORY_BGP, "node=%016llx best_rtt=%u best_sock=%p metric=%u", (unsigned long long)req->target_node->node.node_id, (unsigned)best_avg_rtt, (void*)best_sock, (unsigned)best_metric); } route_ping_send_resp(req, best_avg_rtt); u_free(req); } static void route_ping_send_resp(struct route_ping_req* req, uint16_t avg_rtt) { if (!req || !req->reply_conn) return; struct BGP_PING_RESPONSE* resp = u_calloc(1, sizeof(struct BGP_PING_RESPONSE)); if (!resp) return; resp->cmd = ETCP_ID_ROUTE_ENTRY; resp->subcmd = ROUTE_SUBCMD_PING_RESP; resp->request_id = req->request_id; // суммируем по всем сокетам для отчёта uint8_t total_sent = 0, total_ok = 0; for (uint8_t i = 0; i < req->socket_count; i++) { total_sent += req->sockets[i].count_sent; total_ok += req->sockets[i].count_ok; } resp->count_sent = total_sent; resp->count_ok = total_ok; resp->avg_rtt = avg_rtt; memcpy(resp->recv_ipv4, req->recv_ipv4, 4); resp->recv_port = htons(req->recv_port); struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(resp); return; } e->dgram = (uint8_t*)resp; e->len = sizeof(struct BGP_PING_RESPONSE); etcp_send(req->reply_conn, e); DEBUG_INFO(DEBUG_CATEGORY_BGP, "request_id=%016llx sent=%u ok=%u avg_rtt=%u", (unsigned long long)req->request_id, (unsigned)total_sent, (unsigned)total_ok, (unsigned)avg_rtt); } int route_ping_send_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id, uint8_t count, uint16_t interval_ms, uint16_t timeout_ms, uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg) { if (!bgp || !to_conn || count == 0 || timeout_ms == 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return -1; } struct BGP_PING_REQUEST* req_pkt = u_calloc(1, sizeof(struct BGP_PING_REQUEST)); if (!req_pkt) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc failed"); return -2; } req_pkt->cmd = ETCP_ID_ROUTE_ENTRY; req_pkt->subcmd = ROUTE_SUBCMD_PING_REQ; req_pkt->request_id = bgp->next_ping_req_id++; req_pkt->node_id = node_id; req_pkt->count = count; req_pkt->interval_ms = interval_ms; req_pkt->timeout_ms = timeout_ms; struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(req_pkt); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "queue_entry_new failed"); return -3; } e->dgram = (uint8_t*)req_pkt; e->len = offsetof(struct BGP_PING_REQUEST, target_ipv4); int ret = etcp_send(to_conn, e); if (ret != 0) { u_free(req_pkt); queue_entry_free(e); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send failed"); return -4; } struct route_ping_pending* pending = u_calloc(1, sizeof(struct route_ping_pending)); if (!pending) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "pending alloc failed"); return -5; } pending->bgp = bgp; pending->request_id = req_pkt->request_id; pending->callback = cb; pending->arg = arg; pending->next = bgp->ping_pending; bgp->ping_pending = pending; pending->timeout_timer = uasync_set_timeout(bgp->instance->ua, wait_timeout_ms * 10, pending, route_ping_pending_timeout); DEBUG_INFO(DEBUG_CATEGORY_BGP, "request_id=%016llx node=%016llx count=%u interval=%u timeout=%u wait=%u", (unsigned long long)pending->request_id, (unsigned long long)node_id, (unsigned)count, (unsigned)interval_ms, (unsigned)timeout_ms, (unsigned)wait_timeout_ms); return 0; } int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id, uint32_t target_ip, uint16_t target_port, uint8_t count, uint16_t interval_ms, uint16_t timeout_ms, uint16_t wait_timeout_ms, route_ping_callback_t cb, void* arg, const uint8_t* pubkey) { if (!bgp || !to_conn || count == 0 || timeout_ms == 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return -1; } size_t pkt_size = pubkey ? sizeof(struct BGP_PING_REQUEST) : offsetof(struct BGP_PING_REQUEST, pubkey); struct BGP_PING_REQUEST* req_pkt = u_calloc(1, pkt_size); if (!req_pkt) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc failed"); return -2; } req_pkt->cmd = ETCP_ID_ROUTE_ENTRY; req_pkt->subcmd = ROUTE_SUBCMD_PING_REQ; req_pkt->request_id = bgp->next_ping_req_id++; req_pkt->node_id = node_id; req_pkt->count = count; req_pkt->interval_ms = interval_ms; req_pkt->timeout_ms = timeout_ms; req_pkt->target_ipv4[0] = (target_ip >> 24) & 0xFF; req_pkt->target_ipv4[1] = (target_ip >> 16) & 0xFF; req_pkt->target_ipv4[2] = (target_ip >> 8) & 0xFF; req_pkt->target_ipv4[3] = target_ip & 0xFF; req_pkt->target_port = target_port; if (pubkey) { memcpy(req_pkt->pubkey, pubkey, SC_PUBKEY_SIZE); } struct ll_entry* e = queue_entry_new(0); if (!e) { u_free(req_pkt); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "queue_entry_new failed"); return -3; } e->dgram = (uint8_t*)req_pkt; e->len = pkt_size; int ret = etcp_send(to_conn, e); if (ret != 0) { u_free(req_pkt); queue_entry_free(e); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "etcp_send failed"); return -4; } struct route_ping_pending* pending = u_calloc(1, sizeof(struct route_ping_pending)); if (!pending) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "pending alloc failed"); return -5; } pending->bgp = bgp; pending->request_id = req_pkt->request_id; pending->callback = cb; pending->arg = arg; pending->next = bgp->ping_pending; bgp->ping_pending = pending; pending->timeout_timer = uasync_set_timeout(bgp->instance->ua, wait_timeout_ms * 10, pending, route_ping_pending_timeout); DEBUG_INFO(DEBUG_CATEGORY_BGP, "request_id=%016llx node=%016llx ip=%s port=%u pubkey=%s", (unsigned long long)pending->request_id, (unsigned long long)node_id, ip_to_str(&target_ip, AF_INET).str, (unsigned)target_port, pubkey ? "yes" : "no"); return 0; } void route_ping_destroy_pending(struct ROUTE_BGP* bgp) { if (!bgp) return; while (bgp->ping_pending) { struct route_ping_pending* p = bgp->ping_pending; bgp->ping_pending = p->next; p->next = NULL; if (p->timeout_timer) { err_t rc = uasync_cancel_timeout(bgp->instance->ua, p->timeout_timer); p->timeout_timer = NULL; if (rc == ERR_OK) { u_free(p); } else { p->cancelled = 1; } } else { u_free(p); } } } void route_ping_handle_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) { const size_t base_len = offsetof(struct BGP_PING_REQUEST, target_ipv4); const size_t addr_len = offsetof(struct BGP_PING_REQUEST, pubkey); if (!bgp || !from_conn || !data || (len != base_len && len != addr_len && len != sizeof(struct BGP_PING_REQUEST))) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args len=%zu", len); return; } const struct BGP_PING_REQUEST* req_pkt = (const struct BGP_PING_REQUEST*)data; // Check for custom target IP:port first (for NAT detection with STUN) struct NODEINFO_IPV4_SOCKET custom_socket; const struct NODEINFO_IPV4_SOCKET* sockets_v4 = NULL; const struct NODEINFO_IPV6_SOCKET* sockets_v6 = NULL; int sock_count_v4 = 0; int sock_count_v6 = 0; struct NODEINFO_Q* target = NULL; const uint8_t* embedded_pubkey = NULL; if (len == sizeof(struct BGP_PING_REQUEST) || len == addr_len) { // Extended packet with custom target (with or without pubkey) custom_socket.addr[0] = req_pkt->target_ipv4[0]; custom_socket.addr[1] = req_pkt->target_ipv4[1]; custom_socket.addr[2] = req_pkt->target_ipv4[2]; custom_socket.addr[3] = req_pkt->target_ipv4[3]; custom_socket.port = req_pkt->target_port; custom_socket.type = 0; custom_socket.id = 0; sockets_v4 = &custom_socket; sock_count_v4 = 1; DEBUG_INFO(DEBUG_CATEGORY_BGP, "using custom target %d.%d.%d.%d:%u for node %016llx", req_pkt->target_ipv4[0], req_pkt->target_ipv4[1], req_pkt->target_ipv4[2], req_pkt->target_ipv4[3], req_pkt->target_port, (unsigned long long)req_pkt->node_id); // Check if embedded pubkey is non-zero (only when full packet length) if (len == sizeof(struct BGP_PING_REQUEST)) { int pubkey_zero = 1; for (int i = 0; i < SC_PUBKEY_SIZE; i++) { if (req_pkt->pubkey[i] != 0) { pubkey_zero = 0; break; } } if (!pubkey_zero) { embedded_pubkey = req_pkt->pubkey; } } // For custom target, we still need target node for pubkey lookup if not embedded target = route_bgp_get_node(bgp, req_pkt->node_id); } else if (len == base_len) { // Base packet without custom target - use nodeinfo sockets target = route_bgp_get_node(bgp, req_pkt->node_id); if (!target) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "node %016llx not found", (unsigned long long)req_pkt->node_id); // отправляем ответ с нулями struct route_ping_req* req = u_calloc(1, sizeof(struct route_ping_req)); if (req) { req->reply_conn = from_conn; req->request_id = req_pkt->request_id; req->socket_count = 0; route_ping_send_resp(req, 0); u_free(req); } return; } sock_count_v4 = get_node_v4_sockets(target, &sockets_v4); sock_count_v6 = get_node_v6_sockets(target, &sockets_v6); } else { DEBUG_WARN(DEBUG_CATEGORY_BGP, "unexpected packet size %zu", len); return; } if ((sock_count_v4 <= 0 || !sockets_v4) && (sock_count_v6 <= 0 || !sockets_v6)) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "no sockets for node %016llx", (unsigned long long)req_pkt->node_id); struct route_ping_req* req = u_calloc(1, sizeof(struct route_ping_req)); if (req) { req->reply_conn = from_conn; req->request_id = req_pkt->request_id; req->socket_count = 0; route_ping_send_resp(req, 0); u_free(req); } return; } // считаем подходящие локальные сокеты (v4 + v6) uint8_t local_v4_count = 0; uint8_t local_v6_count = 0; struct ETCP_SOCKET* ls = bgp->instance->etcp_sockets; while (ls) { if (ls->local_addr.ss_family == AF_INET) local_v4_count++; else if (ls->local_addr.ss_family == AF_INET6) local_v6_count++; ls = ls->next; } uint8_t local_count = local_v4_count + local_v6_count; if (local_count == 0) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "no local sockets"); struct route_ping_req* req = u_calloc(1, sizeof(struct route_ping_req)); if (req) { req->reply_conn = from_conn; req->request_id = req_pkt->request_id; req->socket_count = 0; route_ping_send_resp(req, 0); u_free(req); } return; } struct route_ping_req* req = u_calloc(1, sizeof(struct route_ping_req) + local_count * sizeof(struct route_ping_sock_ctx)); if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "alloc failed"); return; } req->reply_conn = from_conn; req->request_id = req_pkt->request_id; req->target_node = target; req->timeout_ms = req_pkt->timeout_ms; req->socket_count = local_count; // Сохраняем IP:port запрашивающего (из первого линка) для STUN-ответа struct ETCP_LINK* l = from_conn->links; if (l && l->remote_addr.ss_family == AF_INET) { struct sockaddr_in* sin = (struct sockaddr_in*)&l->remote_addr; req->recv_ipv4[0] = (ntohl(sin->sin_addr.s_addr) >> 24) & 0xFF; req->recv_ipv4[1] = (ntohl(sin->sin_addr.s_addr) >> 16) & 0xFF; req->recv_ipv4[2] = (ntohl(sin->sin_addr.s_addr) >> 8) & 0xFF; req->recv_ipv4[3] = ntohl(sin->sin_addr.s_addr) & 0xFF; req->recv_port = ntohs(sin->sin_port); } else { memset(req->recv_ipv4, 0, 4); req->recv_port = 0; } ls = bgp->instance->etcp_sockets; uint8_t idx = 0; while (ls) { struct sockaddr_storage target_addr; memset(&target_addr, 0, sizeof(target_addr)); if (ls->local_addr.ss_family == AF_INET) { if (sock_count_v4 > 0 && sockets_v4) { struct sockaddr_in* sin = (struct sockaddr_in*)&target_addr; sin->sin_family = AF_INET; memcpy(&sin->sin_addr, sockets_v4[0].addr, 4); sin->sin_port = htons(sockets_v4[0].port); } else { ls = ls->next; continue; } } else if (ls->local_addr.ss_family == AF_INET6) { if (sock_count_v6 > 0 && sockets_v6) { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&target_addr; sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, sockets_v6[0].addr, 16); sin6->sin6_port = htons(sockets_v6[0].port); } else { ls = ls->next; continue; } } else { ls = ls->next; continue; } req->sockets[idx].local_sock = ls; struct route_ping_series* series = u_calloc(1, sizeof(struct route_ping_series)); if (series) { series->req = req; series->sock_ctx = &req->sockets[idx]; series->local_sock = ls; series->target_addr = target_addr; const uint8_t* pubkey_to_use = target ? target->node.public_key : embedded_pubkey; if (!pubkey_to_use) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "no pubkey available for node %016llx", (unsigned long long)req_pkt->node_id); series->sock_ctx->avg_rtt = req_pkt->timeout_ms * 10; series->sock_ctx->count_sent = req_pkt->count; req->completed_count++; u_free(series); idx++; ls = ls->next; continue; } memcpy(series->pubkey, pubkey_to_use, SC_PUBKEY_SIZE); series->count_total = req_pkt->count; series->count_sent = 0; series->count_ok = 0; series->rtt_sum = 0; series->timeout_ms = req_pkt->timeout_ms; series->interval_ms = req_pkt->interval_ms; int ret = etcp_send_ping_to_socket(bgp->instance, ls, pubkey_to_use, &target_addr, req_pkt->timeout_ms, route_ping_cb, series, NULL, 0); if (ret != 0) { series->sock_ctx->avg_rtt = req_pkt->timeout_ms * 10; series->sock_ctx->count_sent = req_pkt->count; req->completed_count++; u_free(series); } } else { req->sockets[idx].avg_rtt = req_pkt->timeout_ms * 10; req->sockets[idx].count_sent = req_pkt->count; req->completed_count++; } idx++; ls = ls->next; } if (req->completed_count >= req->socket_count) { route_ping_finish(req); } } void route_ping_handle_resp(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) { if (!bgp || !from_conn || !data || len < sizeof(struct BGP_PING_RESPONSE)) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return; } const struct BGP_PING_RESPONSE* resp = (const struct BGP_PING_RESPONSE*)data; DEBUG_INFO(DEBUG_CATEGORY_BGP, "from=%s request_id=%016llx sent=%u ok=%u avg_rtt=%u", from_conn->log_name, (unsigned long long)resp->request_id, (unsigned)resp->count_sent, (unsigned)resp->count_ok, (unsigned)resp->avg_rtt); struct route_ping_pending** cur = &bgp->ping_pending; while (*cur) { struct route_ping_pending* p = *cur; if (p->request_id == resp->request_id) { *cur = p->next; if (p->timeout_timer) { uasync_cancel_timeout(bgp->instance->ua, p->timeout_timer); p->timeout_timer = NULL; } if (p->callback) { int success = (resp->count_ok > 0) ? 1 : 0; uint32_t recv_ip = (resp->recv_ipv4[0] << 24) | (resp->recv_ipv4[1] << 16) | (resp->recv_ipv4[2] << 8) | resp->recv_ipv4[3]; uint16_t recv_port = ntohs(resp->recv_port); p->callback(success, resp->avg_rtt, resp->count_sent, resp->count_ok, recv_ip, recv_port, p->arg); } u_free(p); return; } cur = &(*cur)->next; } DEBUG_WARN(DEBUG_CATEGORY_BGP, "request_id=%016llx not found in pending list", (unsigned long long)resp->request_id); }