Specify NCD ownership callbacks matching and fixed little-endian control wire

This commit is contained in:
evgeny committed 2026-10-24 20:22:45 +02:00
1 parent b83a28becc
commit a353797edb
2 files changed
+42 -16

No files matched your search

+4 -1
View File
@@ -675,7 +675,8 @@ static void ncd_recv_control_handler(struct ETCP_CONN* conn, struct ll_entry* e)
struct ncd_control_msg message;
memcpy(&message, e->dgram, sizeof(message));
const struct ncd_control_msg* msg = &message;
uint64_t sender_id = msg->node_id;
uint64_t sender_id = 0;
for (unsigned i = 0; i < 8; i++) sender_id |= (uint64_t)e->dgram[2 + i] << (8 * i);
struct ncd_entry* entry = ncd_registry_find(conn->instance, sender_id);
if (!sender_id || sender_id != conn->peer_node_id || (entry && entry->conn != conn)) {
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] control from wrong transport node=%016llx", (unsigned long long)sender_id);
@@ -698,6 +699,7 @@ static void ncd_recv_control_handler(struct ETCP_CONN* conn, struct ll_entry* e)
if (!qe) DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] KEEP_ALIVE allocation failed");
else {
memcpy(qe->dgram, &resp, sizeof(resp)); qe->len = sizeof(resp);
for (unsigned i = 0; i < 8; i++) qe->dgram[2 + i] = (uint8_t)(conn->instance->node_id >> (8 * i));
if (etcp_send(conn, qe) != 0) {
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] KEEP_ALIVE send failed node=%016llx", (unsigned long long)sender_id);
queue_dgram_free(qe); queue_entry_free(qe);
@@ -933,6 +935,7 @@ static void ncd_handle_close(struct NODE_CONN_DIRECT* h, int force) {
if (!e) DEBUG_ERROR(DEBUG_CATEGORY_NCD, "[ncd] CLOSE allocation failed");
else {
memcpy(e->dgram, &msg, sizeof(msg)); e->len = sizeof(msg);
for (unsigned i = 0; i < 8; i++) e->dgram[2 + i] = (uint8_t)(entry->inst->node_id >> (8 * i));
if (etcp_send(conn,e) != 0) {
DEBUG_WARN(DEBUG_CATEGORY_NCD, "[ncd] CLOSE send failed"); queue_dgram_free(e); queue_entry_free(e);
}
+38 -15
View File
@@ -4,7 +4,7 @@
* Несколько handles делят один ETCP_CONN; close снимает только своё владение.
* NCD создаёт и обновляет UDP/STCP-линки, но не проверяет членство и готовность topo-группы.
* Для групповой сессии используйте topo_group_peer_open, для звонка/файла и выбора пути — conn_mgr_open.
* Все операции — в потоке uasync.
* Все операции — в потоке uasync. Диагностика — категория etcp, подробности на debug.
*/
/* Владение транспортом:
@@ -12,21 +12,28 @@
* Временный handle закрывают либо передают новому владельцу через transfer.
* Потребность сервиса в доставке на всё время звонка/файла выражает CONN_MGR_HANDLE (conn_mgr.h).
* Последний handle работающего транспорта начинает CLOSE/KEEP_ALIVE (5 с либо срок пробуждения пира);
* DOWN-транспорт удаляется по таймауту ETCP.
* Закрытие последнего handle до первого UP или после TIMEOUT удаляет транспорт сразу;
* ранее работавший DOWN-транспорт остаётся до deadline ETCP.
* Переходы UP/DOWN/TIMEOUT/CLOSED доставляются синхронно. Закрытие любого handle
* из callback безопасно. Начальное UP при open готового conn откладывается через uasync_call_soon.
* Вложенный переход отменяет оставшуюся рассылку прежнего события. После close указатель handle недействителен.
* CLOSED отменяет таймеры и отсоединяет conn; handle всё равно закрывает владелец, в том числе после destroy ядра.
*/
#ifndef NODE_CONN_DIRECT_H
#define NODE_CONN_DIRECT_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct UTUN_INSTANCE;
struct ETCP_CONN;
struct TOPO_NODE;
struct ETCP_SOCKET;
// Привязать CLOSE/KEEP_ALIVE при запуске ядра, в том числе без исходящих handles.
// Привязать CLOSE/KEEP_ALIVE при запуске ядра, в том числе без исходящих handles; 0 — успех, -1 — ошибка.
int node_conn_direct_init(struct UTUN_INSTANCE* inst);
// Число локальных владельцев конкретного транспорта; внешние ETCP refs сюда не входят.
@@ -42,7 +49,8 @@ struct NODE_CONN_DIRECT;
/* Рабочий ETCP, совместимая исходящая пара либо возможность reverse на наш опубликованный адрес.
* Без линков/handles/таймеров/проб. Исходящие пары — тот же обход, что open; reverse — sock_match/публикуемые адреса.
* Учитывает IPv6 и исходящий TCP fallback; возможность попытки не доказывает достижимость. */
* Учитывает IPv6 и исходящий TCP fallback; 1 — кандидат есть, 0 — нет/ошибка. SQLite не загружает.
* Возможность попытки не доказывает достижимость и не подтверждает готовность группы. */
int node_conn_direct_compatible(struct UTUN_INSTANCE* inst, uint64_t node_id);
// ─── Callback events (единый callback на handle) ───
@@ -58,10 +66,14 @@ typedef void (*ncd_callback)(struct NODE_CONN_DIRECT* h, enum ncd_event event, v
/*
* Открыть handle к узлу по node_id.
* Node info ищется через node_registry, fallback — SQLite.
* cb вызывается при изменении статуса / таймауте.
* cb/arg — callback и его контекст (cb=NULL допустим). Новый handle получает события только после возврата open.
* Если conn уже готов — cb(NCD_EVENT_UP) через uasync_call_soon (не синхронно).
* До первой отправки дождаться UP. Результат: NCD_NEW/NCD_REUSED или NCD_ERR.
* При успехе вызывающий обязан закрыть *out_handle, в том числе после CLOSED.
* out_handle обязателен; при ошибке *out_handle=NULL, старые handles и FIN_WAIT сохраняются.
* Принятый snapshot и уже добавленные линки могут сохраниться при частичной ошибке обновления.
* specific_sock=NULL выбирает все разрешённые пары; иначе нужен обычный сокет этого instance.
* Явный сокет задаёт локальную политику, но сохраняются проверки family, протокола, target и REALITY.
*/
int node_conn_direct_open(struct UTUN_INSTANCE* inst, uint64_t node_id,
ncd_callback cb, void* cb_arg,
@@ -70,7 +82,10 @@ int node_conn_direct_open(struct UTUN_INSTANCE* inst, uint64_t node_id,
/*
* То же, но с уже загруженным TOPO_NODE (ni не владеем — можно передать временный на стеке).
* ni должен содержать public_key, v4_addrs/v6_addrs для создания линков.
* ni заимствован до возврата; node_id должен совпадать с ни́м и производным public_key.
* timestamp>0 требует верной подписи: более свежая запись принимается целиком, старая уступает известной,
* одинаковый timestamp с другой подписью отклоняется. timestamp=0 — bootstrap, он не заменяет подписанную запись.
* Адреса могут отсутствовать: handle ожидает также входящего подключения. Обход адресов использует отдельную копию.
*/
int node_conn_direct_open_node(struct UTUN_INSTANCE* inst, uint64_t node_id,
ncd_callback cb, void* cb_arg,
@@ -82,7 +97,8 @@ int node_conn_direct_open_node(struct UTUN_INSTANCE* inst, uint64_t node_id,
void node_conn_direct_close(struct NODE_CONN_DIRECT* h);
/* Применить принятую запись из общего реестра к уже существующему соединению.
* Не создаёт соединение и не приобретает handle. Число новых линков или -1 при ошибке. */
* Не создаёт соединение и не приобретает handle. Число новых линков, 0 при отсутствии записи/транспорта, -1 при ошибке.
* Добавляет недостающие пары и обновляет TCP/REALITY; работающие линки по исчезнувшим адресам сохраняются. */
int node_conn_direct_update_node(struct UTUN_INSTANCE* inst, uint64_t node_id);
/* Как close, но последний handle закрывает транспорт сразу, без fin_wait.
@@ -90,7 +106,8 @@ int node_conn_direct_update_node(struct UTUN_INSTANCE* inst, uint64_t node_id);
void node_conn_direct_force_close(struct NODE_CONN_DIRECT* h);
/* Сменить или сбросить (cb=NULL) callback на уже открытом handle.
* Не влияет на refcounting и состояние conn. */
* Не влияет на refcounting и состояние conn, не повторяет уже доставленное событие.
* Ещё не доставленный начальный UP использует новый callback/arg. */
void node_conn_direct_set_callback(struct NODE_CONN_DIRECT* h, ncd_callback cb, void* cb_arg);
/* Передать владение handle новому владельцу (смена callback + arg).
@@ -104,7 +121,7 @@ uint64_t node_conn_direct_node_id(const struct NODE_CONN_DIRECT* h);
// Заимствованный транспорт, в том числе до UP; NULL после CLOSED. Не закрывать напрямую.
struct ETCP_CONN* node_conn_direct_get_conn(struct NODE_CONN_DIRECT* h);
// ─── Протокол CLOSE / KEEP_ALIVE (ETCP_ID_NCD_CONTROL = 0x12) ───
// Прямой ETCP control: ровно 10 байт, cmd=0x12, subcmd, node_id little-endian; sender сверяется с peer транспорта.
#define NCD_SUBCMD_CLOSE 0x01
#define NCD_SUBCMD_KEEP_ALIVE 0x02
@@ -113,7 +130,7 @@ struct ETCP_CONN* node_conn_direct_get_conn(struct NODE_CONN_DIRECT* h);
struct ncd_control_msg {
uint8_t cmd; /* ETCP_ID_NCD_CONTROL */
uint8_t subcmd; /* NCD_SUBCMD_CLOSE / KEEP_ALIVE */
uint64_t node_id; /* node_id отправителя */
uint64_t node_id; /* wire: 8 байт little-endian; не native uint64_t на big-endian платформе */
};
#pragma pack(pop)
@@ -122,18 +139,24 @@ struct ncd_control_msg {
// ─── Управление линками на конкретном сокете ───
/*
* ncd_add_socket_links — итерирует все активные NCD-соединения (ncd_registry),
* для каждого загружает TOPO_NODE с адресами пира и создаёт линки (etcp_link_new)
* на переданном сокете (только совместимые family: v4/v6). Аналог ncd_create_links
* но вызывается извне (auto_socket) при добавлении нового интерфейса.
* Возвращает количество созданных линков (0 если нечего добавить или ошибка).
* кроме FIN_WAIT; загружает TOPO_NODE и добавляет все разрешённые пары на новом сокете.
* Использует правила open(NULL): family/UDP/TCP, локальная политика, NAT-профиль, REALITY, IPv6 scope.
* sock — обычный зарегистрированный сокет inst, заимствован до возврата; callbacks не должны удалять его.
* Возвращает число новых линков, 0 если нечего добавить, -1 при ошибке (частичные добавления сохраняются).
*/
int ncd_add_socket_links(struct UTUN_INSTANCE* inst, struct ETCP_SOCKET* sock);
/*
* ncd_remove_socket_links — итерирует все активные NCD-соединения,
* для каждого закрывает линки (etcp_link_close) привязанные к переданному сокету.
* Вызывается при удалении сокета (интерфейс пропал). Возвращает количество закрытых линков.
* Вызывается перед удалением сокета; sock заимствован до возврата. Сам сокет не удаляет.
* Возвращает число линков, закрытых этим вызовом, или -1 при отказе памяти. NULL inst/sock — 0.
* Вложенные callbacks могут закрывать другие handles/линки. Транспорт вне NCD удаляет сам etcp_socket_remove.
*/
int ncd_remove_socket_links(struct UTUN_INSTANCE* inst, struct ETCP_SOCKET* sock);
#ifdef __cplusplus
}
#endif
#endif