#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" #include "../lib/u_async.h" struct route_ping_series_ctx { struct ETCP_CONN* reply_conn; uint64_t request_id; struct sockaddr_storage target_addr; uint8_t pubkey[SC_PUBKEY_SIZE]; struct ETCP_SOCKET* local_sock; uint8_t count_total; uint8_t count_sent; uint8_t count_ok; uint32_t sum_rtt; /* в 0.1 ms */ uint16_t timeout_ms; }; // ==================================== блок обработки запросов на удаленные пинги 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; struct ETCP_CONN* via_conn; // через кого отправили запрос (third_conn) }; // если удаленный узел долго не отвечает - вызываем коллбэк по таймауту 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, p->arg); } u_free(p); } // прошел ответ "серия пигнов на удаленном узле завершена" 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; p->callback(success, resp->avg_rtt, resp->count_sent, resp->count_ok, 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); } void route_ping_cancel_for_conn(struct ROUTE_BGP* bgp, struct ETCP_CONN* conn) { if (!bgp || !conn) return; if (!bgp->ping_pending) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "cancel_for_conn: ping_pending is NULL for %s", conn->log_name); return; } DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "cancel_for_conn: checking pending for %s", conn->log_name); int cancelled = 0; struct route_ping_pending** cur = &bgp->ping_pending; while (*cur) { struct route_ping_pending* p = *cur; struct nat_check_arg* na = (struct nat_check_arg*)p->arg; DEBUG_DEBUG(DEBUG_CATEGORY_BGP, " checking pending req_id=%016llx via=%s link->etcp=%s", (unsigned long long)p->request_id, p->via_conn ? p->via_conn->log_name : "NULL", (na && na->link && na->link->etcp) ? na->link->etcp->log_name : "NULL"); if ((na && na->link && na->link->etcp == conn) || p->via_conn == conn) { *cur = p->next; if (p->timeout_timer) { uasync_cancel_timeout(bgp->instance->ua, p->timeout_timer); p->timeout_timer = NULL; } p->cancelled = 1; na->link = NULL; u_free(p); cancelled++; DEBUG_INFO(DEBUG_CATEGORY_BGP, "cancel_for_conn: cancelled req_id=%016llx", (unsigned long long)p->request_id); } else { cur = &(*cur)->next; } } if (cancelled == 0) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "cancel_for_conn: no pending found for %s", conn->log_name); } } // ======================================================================== int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, 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->count = count; req_pkt->interval_ms = interval_ms; req_pkt->timeout_ms = timeout_ms; /* IP уже в network byte order, порт конвертируем */ memcpy(req_pkt->target_ipv4, &target_ip, 4); req_pkt->target_port = htons(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->via_conn = to_conn; 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, "route_ping"); DEBUG_INFO(DEBUG_CATEGORY_BGP, "request_id=%016llx ip=%s port=%u pubkey=%s", (unsigned long long)pending->request_id, ip_to_str(&target_ip, AF_INET).str, (unsigned)target_port, pubkey ? "yes" : "no"); return 0; } static void route_ping_series_finish(struct route_ping_series_ctx* ctx) { if (!ctx || !ctx->reply_conn) { u_free(ctx); DEBUG_ERROR(DEBUG_CATEGORY_BGP, "zero args"); return; } uint16_t avg_rtt = (ctx->count_ok > 0) ? (uint16_t)(ctx->sum_rtt / ctx->count_ok) : (uint16_t)(ctx->timeout_ms * 10); /* таймаут как "плохо" */ /* Отправляем ответ по BGP */ struct BGP_PING_RESPONSE* resp = u_calloc(1, sizeof(struct BGP_PING_RESPONSE)); if (resp) { resp->cmd = ETCP_ID_ROUTE_ENTRY; resp->subcmd = ROUTE_SUBCMD_PING_RESP; resp->request_id = ctx->request_id; resp->count_sent = ctx->count_sent; resp->count_ok = ctx->count_ok; resp->avg_rtt = avg_rtt; struct ll_entry* e = queue_entry_new(0); if (e) { e->dgram = (uint8_t*)resp; e->len = sizeof(struct BGP_PING_RESPONSE); etcp_send(ctx->reply_conn, e); DEBUG_INFO(DEBUG_CATEGORY_BGP, "PING series done request_id=%016llx sent=%u ok=%u avg_rtt=%u", (unsigned long long)ctx->request_id, (unsigned)ctx->count_sent, (unsigned)ctx->count_ok, (unsigned)avg_rtt); } else { u_free(resp); } } u_free(ctx); } /* Callback одного пинга из серии */ static void route_ping_single_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_ctx* ctx = (struct route_ping_series_ctx*)arg; if (!ctx) return; /* Обновляем статистику */ ctx->count_sent++; if (success) { ctx->count_ok++; ctx->sum_rtt += rtt; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "ping response nonce=%llu", (unsigned long long)nonce); /* Если ещё не все пакеты отправлены — сразу шлём следующий */ if (ctx->count_sent < ctx->count_total) { int ret = etcp_send_ping_to_socket( ctx->reply_conn->instance, ctx->local_sock, ctx->pubkey, &ctx->target_addr, ctx->timeout_ms, route_ping_single_cb, ctx, NULL, 0); if (ret != 0) { /* Не смогли отправить следующий — завершаем серию досрочно */ route_ping_series_finish(ctx); } /* else: следующий пинг запущен, ждём его callback */ } else { /* Все пакеты обработаны */ route_ping_series_finish(ctx); } } // Обработчик PING_REQ (нам запросили "пропингуй узел и верни результат") void route_ping_handle_req(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn, const uint8_t* data, size_t len) { if (!bgp || !from_conn || !data || len < offsetof(struct BGP_PING_REQUEST, pubkey)) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "ping req: bad args len=%zu", len); return; } const struct BGP_PING_REQUEST* req_pkt = (const struct BGP_PING_REQUEST*)data; /* Создаём контекст серии */ struct route_ping_series_ctx* ctx = u_calloc(1, sizeof(struct route_ping_series_ctx)); if (!ctx) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "ping req: alloc ctx failed"); return; } ctx->reply_conn = from_conn; ctx->request_id = req_pkt->request_id; ctx->count_total = req_pkt->count; ctx->timeout_ms = req_pkt->timeout_ms; if (len >= sizeof(struct BGP_PING_REQUEST)) { memcpy(ctx->pubkey, req_pkt->pubkey, SC_PUBKEY_SIZE); } /* Целевой адрес - в sockaddr храним в network byte order (как в пакете) */ struct sockaddr_in* sin = (struct sockaddr_in*)&ctx->target_addr; sin->sin_family = AF_INET; memcpy(&sin->sin_addr.s_addr, req_pkt->target_ipv4, 4); /* network byte order */ sin->sin_port = req_pkt->target_port; /* порт в network byte order */ /* Если target не указан — можно разрешить из nodeinfo */ /* if (sin->sin_addr.s_addr == 0 && sin->sin_port == 0) { struct NODEINFO_Q* nq = route_bgp_get_node(bgp, req_pkt->node_id); if (nq) { const struct NODEINFO_IPV4_SOCKET* sockets; int count = get_node_v4_sockets(nq, &sockets); if (count > 0) { memcpy(&sin->sin_addr.s_addr, sockets[0].addr, 4); sin->sin_port = sockets[0].port; DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "resolved target from nodeinfo: %s:%u", ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port)); } } } */ /* Ищем первый IPv4-сокет (как в старом коде) */ struct ETCP_SOCKET* ls = bgp->instance->etcp_sockets; while (ls) { if (ls->local_addr.ss_family == AF_INET) { ctx->local_sock = ls; break; } ls = ls->next; } if (!ctx->local_sock) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "ping req: no IPv4 socket"); route_ping_series_finish(ctx); /* отправит 0/0 */ return; } DEBUG_INFO(DEBUG_CATEGORY_BGP, "PING series start request_id=%016llx target=%s:%u count=%u timeout=%u", (unsigned long long)ctx->request_id, ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port), (unsigned)ctx->count_total, (unsigned)ctx->timeout_ms); /* Отладка: показываем что передаем в etcp_send_ping_to_socket */ { struct sockaddr_in* dbg_sin = (struct sockaddr_in*)&ctx->target_addr; uint8_t* k = ctx->pubkey; DEBUG_INFO(DEBUG_CATEGORY_BGP, "SEND_PING: family=%u addr=0x%08x port=%u pubkey=%02x%02x%02x%02x...%02x%02x", (unsigned)dbg_sin->sin_family, (unsigned)dbg_sin->sin_addr.s_addr, (unsigned)ntohs(dbg_sin->sin_port), k[0], k[1], k[2], k[3], k[60], k[61]); } /* Запускаем первый пинг (дальше цепочка через callback) */ int ret = etcp_send_ping_to_socket( bgp->instance, ctx->local_sock, ctx->pubkey, &ctx->target_addr, ctx->timeout_ms, route_ping_single_cb, ctx, NULL, 0); if (ret != 0) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "ping req: cannot start first ping"); route_ping_series_finish(ctx); } }