You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

401 lines
14 KiB

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h>
#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);
}
}