WIP: debug byte order diagnostics

This commit is contained in:
Evgeny committed 2026-04-22 02:14:47 +03:00
1 parent 6a971389b6
commit 77fd7c19de
21 files changed
+1389 -131

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
Есть 2 типа подключений - обычные (роутятся через подключения в конфиге) - по ним распространяется bgp и сигнальные пакеты
А для передачи данных между узлами можно установить прямое подключение (или подключение через лучший промежуточный узел).
Как работает:
в nodeinfo
+19 -5
View File
@@ -308,6 +308,11 @@ void route_bgp_destroy(struct UTUN_INSTANCE* instance) {
instance->bgp = NULL;
}
void route_bgp_set_nat_check_local(struct ROUTE_BGP* bgp, int allow) {
if (!bgp) return;
bgp->allow_nat_check_local = allow ? 1 : 0;
}
void route_bgp_new_conn(struct ETCP_CONN* conn) {
if (!conn) {
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "route_bgp_new_conn: conn is NULL");
@@ -549,8 +554,8 @@ struct nat_check_arg {
};
static void nat_link_check_cb(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok,
uint32_t recv_ip, uint16_t recv_port, void* arg) {
(void)avg_rtt; (void)count_sent; (void)count_ok; (void)recv_ip; (void)recv_port;
void* arg) {
(void)avg_rtt; (void)count_sent; (void)count_ok;
struct nat_check_arg* na = (struct nat_check_arg*)arg;
if (!na || !na->link) { u_free(na); return; }
struct ETCP_LINK* link = na->link;
@@ -614,7 +619,10 @@ static void route_bgp_start_link_nat_check(struct ROUTE_BGP* bgp, struct ETCP_LI
target_port = ntohs(sin->sin_port);
}
if (is_local_subnet(target_ip)) { DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "local connection: %s, skip nat check", ip_to_str(&target_ip, AF_INET).str); return; }
if (!bgp->allow_nat_check_local && is_local_subnet(target_ip)) {
DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "local connection: %s, skip nat check", ip_to_str(&target_ip, AF_INET).str);
return;
}
// Allocate callback arg
struct nat_check_arg* arg = u_calloc(1, sizeof(struct nat_check_arg));
@@ -630,7 +638,7 @@ static void route_bgp_start_link_nat_check(struct ROUTE_BGP* bgp, struct ETCP_LI
return;
}
// Send ping via third node - embed peer pubkey for NAT detection
int ret = route_ping_send_req_addr(bgp, third_conn, link->etcp->peer_node_id, target_ip, target_port,
int ret = route_ping_send_req_addr(bgp, third_conn, target_ip, target_port,
3, 500, 1000, 5000, nat_link_check_cb, arg,
pubkey);
if (ret == 0) {
@@ -875,8 +883,11 @@ static void route_bgp_handle_nat_info(struct ROUTE_BGP* bgp, struct ETCP_CONN* f
const struct BGP_NAT_INFO* info = (const struct BGP_NAT_INFO*)data;
uint32_t nat_ip = (info->nat_ip[0] << 24) | (info->nat_ip[1] << 16) |
(info->nat_ip[2] << 8) | info->nat_ip[3];
uint16_t nat_port = ((info->nat_port >> 8) & 0xFF) | ((info->nat_port & 0xFF) << 8);
uint16_t nat_port = ntohs(info->nat_port);
uint8_t socket_id = info->socket_id;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO recv: ip_bytes=0x%02x%02x%02x%02x ip_host=0x%08x port_net=%u port_host=%u",
info->nat_ip[0], info->nat_ip[1], info->nat_ip[2], info->nat_ip[3],
nat_ip, info->nat_port, nat_port);
uint8_t* dynamic = (uint8_t*)&bgp->local_node->node + sizeof(struct NODEINFO);
dynamic += bgp->local_node->node.node_name_len;
@@ -982,6 +993,9 @@ void route_bgp_send_nat_info(struct ETCP_CONN* conn, uint8_t socket_id, uint32_t
pkt->nat_ip[3] = nat_ip & 0xFF;
pkt->nat_port = htons(nat_port);
pkt->nat_type = nat_type;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NAT_INFO send: ip_host=0x%08x ip_bytes=0x%02x%02x%02x%02x port_host=%u port_net=%u",
nat_ip, pkt->nat_ip[0], pkt->nat_ip[1], pkt->nat_ip[2], pkt->nat_ip[3],
nat_port, pkt->nat_port);
struct ll_entry* e = queue_entry_new(0);
if (!e) {
+7
View File
@@ -84,6 +84,7 @@ struct ROUTE_BGP {
struct NODEINFO_Q* local_node;
struct route_ping_pending* ping_pending;
uint64_t next_ping_req_id;
uint8_t allow_nat_check_local; // 1 = разрешить NAT check для локальных подсетей (для тестов)
};
/**
@@ -186,4 +187,10 @@ void route_bgp_send_nat_info(struct ETCP_CONN* conn, uint8_t socket_id, uint32_t
*/
void route_bgp_send_nat_check_req(struct ETCP_CONN* conn, uint8_t socket_id);
/**
* @brief Разрешить/запретить NAT check для локальных подсетей (127.0.0.1, 10.x.x.x и т.д.)
* По умолчанию запрещено. Полезно для тестов.
*/
void route_bgp_set_nat_check_local(struct ROUTE_BGP* bgp, int allow);
#endif // ROUTE_BGP_H
+71
View File
@@ -0,0 +1,71 @@
подсистема роутинга
utun - это сеть узлов. у каждого узла есть собственные локальные подсети.
глобальная задача: создать у каждого узла полную таблицу маршрутизации.
Узлы преимущественно создают связь напрямую друг с другом. Но если это не получается отправляют трафик транзитом через доступные узлы.
Иногда бывает что через транзитные узлы метрики лучше чем напрямую. Используем приоритетно узлы с лучшей метриков, при нехватке bandwidth используем разные каналы (агрегируем).
Динамически обновлем метрики каналов чтобы при отказе быстро переключаться на другие и не фризить обмен из-за отказов.
и узлы обмениваются таблицой маршрутов между собой так чтобы у каждого была актуальная таблица подсетей всех узлов.
маршрутами меняются клиенты, подключения которых которые взяты из конфига. и сервера принявшие подключения если клиент инициировал обмен маршрутами.
инициируется подключение, клиент отправляет свою таблицу. когда сервер принимает таблицу - сервер помечает что по этому маршруту надо обмениваться маршрутами, далее;
- добавляет узел в список рассылки обновлений маршрутов
- отправляет свою таблицу
- добавляет в свою таблицу отсутствующие маршруты
- если что-то добавил:
- рассылает измененные маршруты по списку рассылки
- список рассылки - это linked-list очередей (также на базе ll_queue - каждый элемент = подписчик). один маршрут = одна отправленная кодограмма
при подключении узла или изменении таблицы: узел шлёт свою таблицу
формат кодограммы: [0x01 - routing module] [subcmd] [data]
subcmd:
1 [route] - отправка маршрута
2, без данных - больше данных нет
если сервер получил кодограмму маршрута - он помечает флаг в etcp что с узлом надо обмениваться маршрутами (etcp_conn->routing_exchange_active=2) и добавляет в очередь рассылки маршрутов
=========================================
механизм инкрементальной синхронизации (реализация - потом, пока мысли)
1. вычисляем хеш каждой записи в роутинг таблице. используем ip+mask+node_uid
2. потом из этих хешей создаем хеш таблицу (старшие n бит номер ячейки). далее вычисляются хеши каждой ячейки.
на первом этапе n=16, на втором - n=16*16 на третьем n=16*16*16.
отправляем хеши удаленному узлу в формате: [n, 1 байт] ([индекс хеша 2 байта - используется n старших бит][хеш - 8 байт])
удаленный узел считает свои хеши и сравнивает. где не совпало смотрит сколько записей.
если записей не много - передает эти записи.
если записей много - добавляет 4 бита к хеш таблице и строит субтаблицу для
==========================================
Формат роутинга:
Таблица узлов состоит из записей:
- uid
- name
- links
- 3 транзитных узла с метриками (RTT)
- маршруты узла
- текущая загрузка линка (за последние 10 сек) можно частоту адаптировать под размер сети
- bandidth limit
- transit bandwidth limit
две группы узлов:
- узлы за nat. подключаются через транзитные узлы. измеряют пинги до транзитных и выбирают N (3 default) лучшие линки. 3 лучших используем для распространения маршрутов
- транзитные узлы. имеют линки с загрузкой.
добавить кодограмму - отменить распространение маршрутов по линку (+ сделать важным линком)
карта маршрутизации:
План:
- сделать передачу роутинга в
- сделать фоновый probe для узлов (условно 1 нода в секунду). выигравшие по качеству соатновятся основными
- сделать etcp дизконнект:
- отправить disconnect request + дождаться ack дальше master удаляет, slave удаляет по down.
+3
View File
@@ -206,6 +206,9 @@ int route_bgp_update_my_nodeinfo(struct UTUN_INSTANCE* instance, struct ROUTE_BG
sa->port = ntohs(sin->sin_port);
sa->type = e_sock->nat_type;
sa->id = e_sock->sock_id;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "NODEINFO socket: addr=0x%02x%02x%02x%02x port=%u (from sin_addr=0x%08x sin_port=%u)",
sa->addr[0], sa->addr[1], sa->addr[2], sa->addr[3], sa->port,
sin->sin_addr.s_addr, ntohs(sin->sin_port));
sa++;
}
e_sock = e_sock->next;
+74 -72
View File
@@ -62,69 +62,11 @@ static void route_ping_pending_timeout(void* arg) {
cur = &(*cur)->next;
}
if (p->callback) {
p->callback(0, 0, 0, 0, 0, 0, p->arg);
p->callback(0, 0, 0, 0, p->arg);
}
u_free(p);
}
// отправить запрос удаленному узлу "пропингуй такой-то узел"
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, "route_ping");
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;
}
// прошел ответ "серия пигнов на удаленном узле завершена"
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)) {
@@ -147,10 +89,7 @@ void route_ping_handle_resp(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn,
}
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);
p->callback(success, resp->avg_rtt, resp->count_sent, resp->count_ok, p->arg);
}
u_free(p);
return;
@@ -163,7 +102,7 @@ void route_ping_handle_resp(struct ROUTE_BGP* bgp, struct ETCP_CONN* from_conn,
// ========================================================================
int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id,
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,
@@ -181,7 +120,6 @@ int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, u
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;
@@ -190,6 +128,10 @@ int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, u
if (pubkey) {
memcpy(req_pkt->pubkey, pubkey, SC_PUBKEY_SIZE);
}
DEBUG_INFO(DEBUG_CATEGORY_BGP, "SEND_REQ: target_ip host=0x%08x net=0x%02x%02x%02x%02x port host=%u net=%u",
target_ip,
req_pkt->target_ipv4[0], req_pkt->target_ipv4[1], req_pkt->target_ipv4[2], req_pkt->target_ipv4[3],
target_port, req_pkt->target_port);
struct ll_entry* e = queue_entry_new(0);
if (!e) {
@@ -222,8 +164,8 @@ int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, u
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 node=%016llx ip=%s port=%u pubkey=%s",
(unsigned long long)pending->request_id, (unsigned long long)node_id,
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;
}
@@ -248,9 +190,6 @@ static void route_ping_series_finish(struct route_ping_series_ctx* ctx) {
resp->count_sent = ctx->count_sent;
resp->count_ok = ctx->count_ok;
resp->avg_rtt = avg_rtt;
/* recv_ipv4/port = 0 (не используется) */
memset(resp->recv_ipv4, 0, 4);
resp->recv_port = 0;
struct ll_entry* e = queue_entry_new(0);
if (e) {
@@ -318,7 +257,7 @@ 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 < sizeof(struct BGP_PING_REQUEST)) {
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;
}
@@ -336,13 +275,76 @@ void route_ping_handle_req(struct ROUTE_BGP* bgp,
ctx->request_id = req_pkt->request_id;
ctx->count_total = req_pkt->count;
ctx->timeout_ms = req_pkt->timeout_ms;
memcpy(ctx->pubkey, req_pkt->pubkey, SC_PUBKEY_SIZE);
if (len >= sizeof(struct BGP_PING_REQUEST)) {
memcpy(ctx->pubkey, req_pkt->pubkey, SC_PUBKEY_SIZE);
}
/* Целевой адрес */
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);
sin->sin_port = req_pkt->target_port;
DEBUG_INFO(DEBUG_CATEGORY_BGP, "RECV_REQ: target_ip net_bytes=0x%02x%02x%02x%02x sin_addr=0x%08x port_net=%u port_host=%u",
req_pkt->target_ipv4[0], req_pkt->target_ipv4[1], req_pkt->target_ipv4[2], req_pkt->target_ipv4[3],
sin->sin_addr.s_addr, req_pkt->target_port, ntohs(req_pkt->target_port));
/* Если 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));
}
}
}
*/
/* Если pubkey не указан — ищем по target_ip:port в nodeinfo */
int pubkey_empty = 1;
for (int i = 0; i < SC_PUBKEY_SIZE; i++) {
if (ctx->pubkey[i] != 0) { pubkey_empty = 0; break; }
}
if (pubkey_empty) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "looking for pubkey for target %s:%u, nodes_count=%d",
ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port),
bgp->nodes ? bgp->nodes->count : -1);
struct ll_entry* e = bgp->nodes ? bgp->nodes->head : NULL;
while (e) {
struct NODEINFO_Q* nq = (struct NODEINFO_Q*)e;
const struct NODEINFO_IPV4_SOCKET* sockets;
int sc = get_node_v4_sockets(nq, &sockets);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "checking node %016llx with %d sockets",
(unsigned long long)nq->node.node_id, sc);
for (int i = 0; i < sc; i++) {
uint32_t socket_ip, target_ip;
memcpy(&socket_ip, sockets[i].addr, 4);
memcpy(&target_ip, req_pkt->target_ipv4, 4);
if (socket_ip == target_ip || socket_ip == htonl(target_ip) ||
htonl(socket_ip) == target_ip || htonl(socket_ip) == htonl(target_ip)) {
memcpy(ctx->pubkey, nq->node.public_key, SC_PUBKEY_SIZE);
DEBUG_INFO(DEBUG_CATEGORY_BGP, "resolved pubkey from nodeinfo for %s:%u",
ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port));
break;
}
}
pubkey_empty = 1;
for (int i = 0; i < SC_PUBKEY_SIZE; i++) {
if (ctx->pubkey[i] != 0) { pubkey_empty = 0; break; }
}
if (!pubkey_empty) break;
e = e->next;
}
if (pubkey_empty) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "pubkey not found for target %s:%u",
ip_to_str(&sin->sin_addr, AF_INET).str, ntohs(sin->sin_port));
}
}
/* Ищем первый IPv4-сокет (как в старом коде) */
struct ETCP_SOCKET* ls = bgp->instance->etcp_sockets;
+2 -11
View File
@@ -12,7 +12,6 @@ struct BGP_PING_REQUEST {
uint8_t cmd; // ETCP_ID_ROUTE_ENTRY
uint8_t subcmd; // ROUTE_SUBCMD_PING_REQ
uint64_t request_id; // для корреляции
uint64_t node_id; // целевой узел
uint8_t count; // число пингов
uint8_t socket_id; // id сокета пингуемого узла
uint16_t interval_ms; // интервал между пингами
@@ -29,22 +28,14 @@ struct BGP_PING_RESPONSE {
uint8_t count_sent;
uint8_t count_ok;
uint16_t avg_rtt; // средний RTT в 0.1ms
uint8_t recv_ipv4[4]; // IP:port с которого получен PING_REQ (STUN-like)
uint16_t recv_port; // network byte order
} __attribute__((packed));
typedef void (*route_ping_callback_t)(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok,
uint32_t recv_ip, uint16_t recv_port, void* arg);
// Отправить запрос пинга через BGP, ожидать ответа с таймаутом
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);
typedef void (*route_ping_callback_t)(int success, uint16_t avg_rtt, uint8_t count_sent, uint8_t count_ok, void* arg);
// Отправить запрос пинга по произвольному IP:port (используется для NAT-детекции)
// Если target_ip == 0, используется node_id из списка известных узлов
// Если pubkey != NULL, он передается в пакете (для пинга без локального nodeinfo)
int route_ping_send_req_addr(struct ROUTE_BGP* bgp, struct ETCP_CONN* to_conn, uint64_t node_id,
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,