Browse Source

conn_mgr.h: document in ll_queue style; config_parser/utun_instance updates

chatgui
Evgeny 3 months ago
parent
commit
59d307f98b
  1. 62
      src/config_parser.c
  2. 9
      src/config_parser.h
  3. 491
      src/conn_mgr.h
  4. 69
      src/utun_instance.c
  5. 10
      src/utun_instance.h

62
src/config_parser.c

@ -47,7 +47,8 @@ typedef enum {
SECTION_NAT,
SECTION_TCP_PROXY_CLIENT,
SECTION_TCP_PROXY_SERVER,
SECTION_MSG_TRANSPORT
SECTION_MSG_TRANSPORT,
SECTION_NETWORK
} section_type_t;
static char* trim(char *str) {
@ -716,6 +717,21 @@ static int parse_client(const char *key, const char *value, struct CFG_CLIENT *c
return -1;
}
static int parse_network(const char *key, const char *value, struct CFG_NETWORK *net, const char *filename, int line_num) {
if (strcmp(key, "id") == 0) {
net->id = strtoull(value, NULL, 16);
return 0;
}
if (strcmp(key, "pubkey") == 0) {
return assign_string(net->pubkey_hex, sizeof(net->pubkey_hex), value);
}
if (strcmp(key, "signing_key") == 0) {
return assign_string(net->signing_key_hex, sizeof(net->signing_key_hex), value);
}
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Unknown network option '%s'. Valid: id, pubkey, signing_key", filename, line_num, key);
return -1;
}
static section_type_t parse_section_header(const char *line, char *name, size_t name_len) {
if (line[0] != '[') return SECTION_UNKNOWN;
@ -752,6 +768,7 @@ static section_type_t parse_section_header(const char *line, char *name, size_t
if (strcasecmp(type, "server") == 0) return SECTION_SERVER;
if (strcasecmp(type, "client") == 0) return SECTION_CLIENT;
if (strcasecmp(type, "network") == 0) return SECTION_NETWORK;
return SECTION_UNKNOWN;
}
@ -779,6 +796,7 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
section_type_t cur_section = SECTION_UNKNOWN;
struct CFG_SERVER *cur_server = NULL;
struct CFG_CLIENT *cur_client = NULL;
struct CFG_NETWORK *cur_network = NULL;
char line[MAX_LINE_LEN];
int line_num = 0;
@ -802,6 +820,11 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
cfg->clients = cur_client;
cur_client = NULL;
}
if (cur_section == SECTION_NETWORK && cur_network) {
cur_network->next = cfg->networks;
cfg->networks = cur_network;
cur_network = NULL;
}
char name[MAX_CONN_NAME_LEN];
cur_section = parse_section_header(trimmed, name, sizeof(name));
@ -821,6 +844,13 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
goto error;
}
strcpy(cur_client->name, name);
} else if (cur_section == SECTION_NETWORK) {
cur_network = u_calloc(1, sizeof(struct CFG_NETWORK));
if (!cur_network) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "parse_config_internal: failed to allocate memory for network %s", name);
goto error;
}
strcpy(cur_network->name, name);
}
continue;
}
@ -927,6 +957,11 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
// parse_msg_transport already printed the error
}
break;
case SECTION_NETWORK:
if (cur_network && parse_network(key, value, cur_network, filename, line_num) < 0) {
// parse_network already printed the error
}
break;
default:
DEBUG_WARN(DEBUG_CATEGORY_CONFIG, "%s:%d: Key outside section: %s", filename, line_num, key);
break;
@ -942,6 +977,10 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
cur_client->next = cfg->clients;
cfg->clients = cur_client;
}
if (cur_section == SECTION_NETWORK && cur_network) {
cur_network->next = cfg->networks;
cfg->networks = cur_network;
}
// Post-parse defaults: tun_enabled=1 if not explicitly disabled
if (!cfg->global.tun_enabled && cfg->global.tun_ifname[0] == '\0' && cfg->global.tun_ip.family == AF_UNSPEC) {
@ -965,6 +1004,7 @@ error:
free_cfg_client_links(cur_client->links);
u_free(cur_client);
}
if (cur_network) u_free(cur_network);
free_config(cfg);
return NULL;
}
@ -1003,6 +1043,14 @@ void free_config(struct utun_config *config) {
client = next;
}
// Free networks
struct CFG_NETWORK *network = config->networks;
while (network) {
struct CFG_NETWORK *next = network->next;
u_free(network);
network = next;
}
// Free route entries
free_route_entries(config->route_subnets);
free_route_entries(config->my_subnets);
@ -1097,8 +1145,16 @@ void print_config(const struct utun_config *cfg) {
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " allow=%s", ip_str);
} else {
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " allow=%s:%d", ip_str, r->port);
}
}
}
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Networks:");
struct CFG_NETWORK *net = cfg->networks;
while (net) {
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " [network:%s] id=%012llx pubkey=%.64s signing_key=%.64s",
net->name, (unsigned long long)net->id, net->pubkey_hex, net->signing_key_hex);
net = net->next;
}
}
} else {
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, " no rules");
}

9
src/config_parser.h

@ -62,6 +62,14 @@ struct CFG_CLIENT {
struct CFG_CLIENT *next; // Next client in linked list
};
struct CFG_NETWORK {
struct CFG_NETWORK* next;
char name[64];
uint64_t id; // 56-bit network ID
char pubkey_hex[SC_PUBKEY_SIZE * 2 + 1]; // 64 hex chars
char signing_key_hex[SC_PRIVKEY_SIZE * 2 + 1]; // 64 hex chars
};
struct CFG_ROUTE_ENTRY {
struct CFG_ROUTE_ENTRY* next;
struct IP ip;
@ -180,6 +188,7 @@ struct utun_config {
struct CFG_CLIENT* clients;
struct CFG_ROUTE_ENTRY* route_subnets;
struct CFG_ROUTE_ENTRY* my_subnets;
struct CFG_NETWORK* networks;
};
struct utun_config* parse_config(const char *filename);

491
src/conn_mgr.h

@ -13,178 +13,427 @@ struct ETCP_CONN;
struct NODEINFO_Q;
struct ROUTE_BGP;
#define CONN_MGR_MAX_CANDIDATES 3
#define CONN_MGR_CANDIDATE_CACHE_MS 20000
#define CONN_MGR_CANDIDATE_STALE_TB 300000
#define CONN_MGR_CANDIDATE_PING_TB 20000
#define CONN_MGR_BG_PING_INTERVAL_TB 1000
#define CONN_MGR_BG_PING_CYCLE_MIN_TB 100000
#define CONN_MGR_IDLE_CHECK_INTERVAL_TB 10000
#define CONN_MGR_LOCAL_SCAN_ATTEMPTS 3
#define CONN_MGR_LOCAL_SCAN_TIMEOUT_MS 100
#define CONN_MGR_CONNECT_DIRECT_TIMEOUT_MS 5000
#define CONN_MGR_CONNECT_REVERSE_TIMEOUT_MS 15000
#define CONN_MGR_INTERM_EXCHANGE_TIMEOUT_MS 15000
#define CONN_MGR_OK 0
#define CONN_MGR_ERR_NOT_FOUND -1
#define CONN_MGR_ERR_NO_ADDRESSES -2
#define CONN_MGR_ERR_TIMEOUT -3
#define CONN_MGR_ERR_UNREACHABLE -4
#define CONN_MGR_ERR_REFUSED -5
#define CONN_MGR_ERR_ALREADY_CONNECTED -6
#define CONN_MGR_ERR_INTERNAL -7
/**
* @file conn_mgr.h
* @brief Менеджер установки соединений. Трёхфазная машина подключения к нодам
* с выбором оптимального пути: прямой → обратный → через посредников.
*
* Основные возможности:
* - Автоматический выбор лучшего способа подключения к целевой ноде
* - Фаза 1 (DIRECT): попытка прямого INIT-рукопожатия на все известные IPv4-адреса цели.
* Итерация по парам (наш сокет, целевой адрес) с проверкой NAT-совместимости.
* Таймаут: CONN_MGR_CONNECT_DIRECT_TIMEOUT_MS
* - Фаза 2 (REVERSE): если у нас есть прямой IP, а у цели нет — отправляем DIRECT_REQ
* с нашими адресами через BGP-маршрут. Цель сама инициирует INIT в нашу сторону.
* Таймаут: CONN_MGR_CONNECT_REVERSE_TIMEOUT_MS
* - Фаза 3 (INDIRECT): если DIRECT и REVERSE не сработали — обмениваемся через BGP
* списками лучших промежуточных узлов-кандидатов с RTT, выбираем общих с минимальным
* суммарным RTT. Трафик идёт через etcp_router (транзитная ретрансляция).
* Таймаут: CONN_MGR_INTERM_EXCHANGE_TIMEOUT_MS
* - Фоновое сканирование локальной сети (broadcast/multicast UDP-ping)
* - Фоновое поддержание списка лучших кандидатов-посредников (периодический ping,
* удаление stale-кандидатов)
* - Фоновый обход всех известных BGP-нод для поддержания RTT-замеров
* - Управление idle-таймаутом с автоматическим разрывом неактивных соединений
*
* @note Правила использования:
* 1. conn_mgr_init() вызывается один раз при старте экземпляра utun
* 2. conn_mgr_connect_node() — асинхронный вызов, результат приходит в callback
* 3. Повторный вызов conn_mgr_connect_node для уже подключённой ноды вернёт
* CONN_MGR_ERR_ALREADY_CONNECTED; для ноды в состоянии CONNECTING добавит
* дополнительный callback в список
* 4. conn_mgr_disconnect_node() отправляет DISCONNECT-пакет целевой ноде и
* сбрасывает conn_mgr_type в NODEINFO_Q
* 5. Для alien-нод (подключившихся к нам самостоятельно) используется
* conn_mgr_add_alien_node() вместо conn_mgr_connect_node()
* 6. conn_mgr_update_best_candidates() вызывается извне (обычно из route_connectivity)
* для обновления RTT кандидатов
*
* @note Внутренние таймеры (всё в timebase-единицах, 1 tb = 0.1 мс):
* - bg_ping_timer: фоновый ping нод (CONN_MGR_BG_PING_INTERVAL_TB)
* - candidate_ping_timer: обновление RTT кандидатов (CONN_MGR_CANDIDATE_PING_TB)
* - idle_timer: проверка неактивности подключённых нод (CONN_MGR_IDLE_CHECK_INTERVAL_TB)
*/
/* ==================== Константы и лимиты ==================== */
#define CONN_MGR_MAX_CANDIDATES 3 ///< Макс. число лучших кандидатов-посредников
#define CONN_MGR_CANDIDATE_CACHE_MS 20000 ///< Время жизни кандидата в кеше (мс) — не используется напрямую в .h
#define CONN_MGR_CANDIDATE_STALE_TB 300000 ///< Порог устаревания кандидата (tb), после которого удаляется из списка
#define CONN_MGR_CANDIDATE_PING_TB 20000 ///< Интервал фонового ping кандидатов (tb, ~2 сек)
#define CONN_MGR_BG_PING_INTERVAL_TB 1000 ///< Интервал фонового ping BGP-нод (tb, ~100 мс)
#define CONN_MGR_BG_PING_CYCLE_MIN_TB 100000 ///< Минимальная длительность полного цикла обхода нод (tb, ~10 сек)
#define CONN_MGR_IDLE_CHECK_INTERVAL_TB 10000 ///< Интервал проверки idle-таймаута (tb, ~1 сек)
#define CONN_MGR_LOCAL_SCAN_ATTEMPTS 3 ///< Число попыток локального сканирования
#define CONN_MGR_LOCAL_SCAN_TIMEOUT_MS 100 ///< Таймаут локального сканирования (мс)
#define CONN_MGR_CONNECT_DIRECT_TIMEOUT_MS 5000 ///< Таймаут фазы DIRECT (мс)
#define CONN_MGR_CONNECT_REVERSE_TIMEOUT_MS 15000 ///< Таймаут фазы REVERSE (мс)
#define CONN_MGR_INTERM_EXCHANGE_TIMEOUT_MS 15000 ///< Таймаут фазы INDIRECT (мс)
/* ==================== Коды возврата ==================== */
#define CONN_MGR_OK 0 ///< Успех
#define CONN_MGR_ERR_NOT_FOUND -1 ///< Нода не найдена в BGP
#define CONN_MGR_ERR_NO_ADDRESSES -2 ///< Нет известных адресов для подключения
#define CONN_MGR_ERR_TIMEOUT -3 ///< Таймаут соединения
#define CONN_MGR_ERR_UNREACHABLE -4 ///< Нода недостижима (все фазы провалились)
#define CONN_MGR_ERR_REFUSED -5 ///< Соединение отклонено (зарезервировано)
#define CONN_MGR_ERR_ALREADY_CONNECTED -6 ///< Уже подключены
#define CONN_MGR_ERR_INTERNAL -7 ///< Внутренняя ошибка
/* ==================== Состояния ==================== */
/**
* @enum CONN_MGR_STATE
* @brief Состояние подключения к ноде.
*/
enum CONN_MGR_STATE {
CONN_MGR_STATE_DISCONNECTED = 0,
CONN_MGR_STATE_CONNECTING = 1,
CONN_MGR_STATE_CONNECTED = 2,
CONN_MGR_STATE_DISCONNECTED = 0, ///< Не подключены
CONN_MGR_STATE_CONNECTING = 1, ///< В процессе установки соединения
CONN_MGR_STATE_CONNECTED = 2, ///< Подключены
};
/**
* @enum CM_TRY_STATE
* @brief Состояние отдельной попытки (локальное сканирование / основное подключение).
*/
enum CM_TRY_STATE {
CM_TRY_NONE = 0,
CM_TRY_PENDING = 1,
CM_TRY_OK = 2,
CM_TRY_FAILED = 3,
CM_TRY_NONE = 0, ///< Попытка не начиналась
CM_TRY_PENDING = 1, ///< Попытка в процессе
CM_TRY_OK = 2, ///< Попытка успешна
CM_TRY_FAILED = 3, ///< Попытка провалилась
};
#define CONN_MGR_SUBCMD_DIRECT_REQ 0x01
#define CONN_MGR_SUBCMD_DIRECT_RESP 0x02
#define CONN_MGR_SUBCMD_INTERM_EXCHANGE_REQ 0x03
#define CONN_MGR_SUBCMD_INTERM_EXCHANGE_RESP 0x04
#define CONN_MGR_SUBCMD_INTERM_SELECTED 0x05
#define CONN_MGR_SUBCMD_DISCONNECT 0x06
/* ==================== Протокол CONN_MGR (ETCP_RT_ID_CONN_MGR = 0x11) ==================== */
/**
* @defgroup conn_mgr_subcmd Подкоманды протокола CONN_MGR
* @{
*/
#define CONN_MGR_SUBCMD_DIRECT_REQ 0x01 ///< "Я за NAT, подключись ко мне" — передача наших адресов цели
#define CONN_MGR_SUBCMD_DIRECT_RESP 0x02 ///< Ответ на DIRECT_REQ (зарезервирован, не реализован)
#define CONN_MGR_SUBCMD_INTERM_EXCHANGE_REQ 0x03 ///< Запрос обмена кандидатами-посредниками
#define CONN_MGR_SUBCMD_INTERM_EXCHANGE_RESP 0x04 ///< Ответ с нашими кандидатами + замеры RTT до кандидатов инициатора
#define CONN_MGR_SUBCMD_INTERM_SELECTED 0x05 ///< Выбранные посредники (финальный шаг INDIRECT-фазы)
#define CONN_MGR_SUBCMD_DISCONNECT 0x06 ///< Уведомление о разрыве соединения
/** @} */
/**
* @struct CONN_MGR_CANDIDATE
* @brief Запись о кандидате-посреднике: идентификатор ноды + RTT до неё.
*
* Используется в списке best_candidates и во всех пакетах обмена кандидатами.
*/
struct CONN_MGR_CANDIDATE {
uint64_t node_id;
uint16_t rtt;
uint64_t node_id; ///< Идентификатор ноды-кандидата
uint16_t rtt; ///< RTT до кандидата (мс)
} __attribute__((packed));
#pragma pack(push, 1)
/**
* @struct CONN_MGR_DIRECT_REQ
* @brief Пакет фазы REVERSE: "подключись ко мне".
*
* Отправляется когда мы имеем прямой IP, но цель за NAT.
* Содержит наши адреса, по которым цель должна инициировать INIT.
*/
struct CONN_MGR_DIRECT_REQ {
uint8_t cmd;
uint8_t subcmd;
uint32_t request_id;
uint8_t addr_count;
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
uint8_t subcmd; ///< CONN_MGR_SUBCMD_DIRECT_REQ
uint32_t request_id; ///< Идентификатор запроса для сопоставления ответа
uint8_t addr_count; ///< Количество адресов, следующих за заголовком
/* далее: addr_count записей по 8 байт каждая (type:1, ip:4, port:2, sock_id:1) */
};
/**
* @struct CONN_MGR_DIRECT_RESP
* @brief Ответ на DIRECT_REQ (зарезервирован, не отправляется).
*/
struct CONN_MGR_DIRECT_RESP {
uint8_t cmd;
uint8_t subcmd;
uint32_t request_id;
uint8_t accepted;
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
uint8_t subcmd; ///< CONN_MGR_SUBCMD_DIRECT_RESP
uint32_t request_id; ///< Идентификатор запроса
uint8_t accepted; ///< 1 — принято, 0 — отклонено
};
/**
* @struct CONN_MGR_INTERM_EXCHANGE_REQ
* @brief Запрос обмена списками кандидатов-посредников (фаза INDIRECT, инициатор).
*
* Инициатор отправляет свой топ-4 кандидатов. Цель должна ответить
* CONN_MGR_INTERM_EXCHANGE_RESP со своими кандидатами и замерами RTT до наших.
*/
struct CONN_MGR_INTERM_EXCHANGE_REQ {
uint8_t cmd;
uint8_t subcmd;
uint32_t request_id;
uint8_t candidate_count;
struct CONN_MGR_CANDIDATE candidates[4];
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
uint8_t subcmd; ///< CONN_MGR_SUBCMD_INTERM_EXCHANGE_REQ
uint32_t request_id; ///< Идентификатор запроса
uint8_t candidate_count; ///< Количество кандидатов в массиве (≤4)
struct CONN_MGR_CANDIDATE candidates[4]; ///< Наши лучшие кандидаты с RTT
};
/**
* @struct CONN_MGR_INTERM_EXCHANGE_RESP
* @brief Ответ на INTERM_EXCHANGE_REQ (фаза INDIRECT, цель).
*
* Содержит два списка:
* - my_candidates: лучшие кандидаты цели с RTT до них (измеренным целью)
* - your_candidates: кандидаты инициатора, но с RTT, измеренным со стороны цели
*/
struct CONN_MGR_INTERM_EXCHANGE_RESP {
uint8_t cmd;
uint8_t subcmd;
uint32_t request_id;
uint8_t my_count;
uint8_t your_count;
struct CONN_MGR_CANDIDATE my_candidates[4];
struct CONN_MGR_CANDIDATE your_candidates[4];
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
uint8_t subcmd; ///< CONN_MGR_SUBCMD_INTERM_EXCHANGE_RESP
uint32_t request_id; ///< Идентификатор запроса
uint8_t my_count; ///< Количество наших кандидатов (≤4)
uint8_t your_count; ///< Количество кандидатов инициатора (≤4)
struct CONN_MGR_CANDIDATE my_candidates[4]; ///< Наши кандидаты + наш RTT до них
struct CONN_MGR_CANDIDATE your_candidates[4]; ///< Кандидаты инициатора + наш RTT до них
};
/**
* @struct CONN_MGR_INTERM_SELECTED
* @brief Финальный выбор посредников (фаза INDIRECT).
*
* Инициатор вычисляет лучших общих посредников по сумме RTT
* и отправляет выбранный список цели.
*/
struct CONN_MGR_INTERM_SELECTED {
uint8_t cmd;
uint8_t subcmd;
uint32_t request_id;
uint8_t count;
struct CONN_MGR_CANDIDATE selected[3];
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
uint8_t subcmd; ///< CONN_MGR_SUBCMD_INTERM_SELECTED
uint32_t request_id; ///< Идентификатор запроса (всегда 0 для извещения)
uint8_t count; ///< Количество выбранных посредников (≤3)
struct CONN_MGR_CANDIDATE selected[3]; ///< Выбранные посредники (node_id + RTT от нас до них)
};
/**
* @struct CONN_MGR_DISCONNECT
* @brief Уведомление о разрыве соединения.
*/
struct CONN_MGR_DISCONNECT {
uint8_t cmd;
uint8_t subcmd;
uint64_t node_id;
uint8_t cmd; ///< ETCP_RT_ID_CONN_MGR
uint8_t subcmd; ///< CONN_MGR_SUBCMD_DISCONNECT
uint64_t node_id; ///< Идентификатор ноды, с которой разрываем соединение
};
#pragma pack(pop)
#define CONN_MGR_DIRECT_REQ_HDR_SIZE sizeof(struct CONN_MGR_DIRECT_REQ)
#define CONN_MGR_DIRECT_RESP_HDR_SIZE sizeof(struct CONN_MGR_DIRECT_RESP)
#define CONN_MGR_INTERM_EXCHANGE_REQ_SIZE sizeof(struct CONN_MGR_INTERM_EXCHANGE_REQ)
#define CONN_MGR_INTERM_EXCHANGE_RESP_SIZE sizeof(struct CONN_MGR_INTERM_EXCHANGE_RESP)
#define CONN_MGR_INTERM_SELECTED_SIZE sizeof(struct CONN_MGR_INTERM_SELECTED)
#define CONN_MGR_DISCONNECT_SIZE sizeof(struct CONN_MGR_DISCONNECT)
/* ==================== Размеры пакетов протокола ==================== */
#define CONN_MGR_DIRECT_REQ_HDR_SIZE sizeof(struct CONN_MGR_DIRECT_REQ) ///< Размер заголовка DIRECT_REQ (без addr_count адресов)
#define CONN_MGR_DIRECT_RESP_HDR_SIZE sizeof(struct CONN_MGR_DIRECT_RESP) ///< Размер DIRECT_RESP
#define CONN_MGR_INTERM_EXCHANGE_REQ_SIZE sizeof(struct CONN_MGR_INTERM_EXCHANGE_REQ) ///< Размер INTERM_EXCHANGE_REQ
#define CONN_MGR_INTERM_EXCHANGE_RESP_SIZE sizeof(struct CONN_MGR_INTERM_EXCHANGE_RESP) ///< Размер INTERM_EXCHANGE_RESP (макс.)
#define CONN_MGR_INTERM_SELECTED_SIZE sizeof(struct CONN_MGR_INTERM_SELECTED) ///< Размер INTERM_SELECTED
#define CONN_MGR_DISCONNECT_SIZE sizeof(struct CONN_MGR_DISCONNECT) ///< Размер DISCONNECT
/* ==================== Callback-типы и внутренние структуры ==================== */
/**
* @typedef conn_mgr_connect_callback_t
* @brief Callback результата подключения.
* @param result код возврата (CONN_MGR_OK или CONN_MGR_ERR_*)
* @param node_id идентификатор ноды
* @param arg пользовательский аргумент
*/
typedef void (*conn_mgr_connect_callback_t)(int result, uint64_t node_id, void* arg);
/**
* @struct cm_cb_node
* @brief Узел связного списка callback'ов для одной ноды.
*
* Позволяет регистрировать несколько callback'ов на одно подключение.
* Новые callback'и добавляются в начало списка.
*/
struct cm_cb_node {
conn_mgr_connect_callback_t cb;
void* arg;
struct cm_cb_node* next;
conn_mgr_connect_callback_t cb; ///< Функция-callback
void* arg; ///< Пользовательский аргумент
struct cm_cb_node* next; ///< Следующий callback в списке
};
/**
* @struct CONN_MGR_ENTRY
* @brief Запись о подключении к одной ноде.
*
* Хранит состояние трёхфазной машины, список выбранных посредников,
* idle-таймер и список ожидающих callback'ов.
*/
struct CONN_MGR_ENTRY {
uint64_t node_id;
uint8_t state;
uint8_t conn_type;
uint8_t alien;
uint32_t idle_timeout_ms;
uint64_t last_traffic_tb;
void* idle_timer;
struct cm_cb_node* cb_list;
uint64_t intermediaries[CONN_MGR_MAX_INTERMEDIARIES];
uint8_t intermediariy_count;
uint8_t rr_idx;
uint8_t local_scan_state;
uint8_t main_connect_state;
uint64_t node_id; ///< Идентификатор целевой ноды
uint8_t state; ///< Текущее состояние (enum CONN_MGR_STATE)
uint8_t conn_type; ///< Тип соединения (CONN_TYPE_DIRECT/REVERSE/INDIRECT)
uint8_t alien; ///< 1 — alien-нода (сама подключилась к нам), 0 — мы инициаторы
uint32_t idle_timeout_ms; ///< Таймаут неактивности (мс), 0 — без таймаута
uint64_t last_traffic_tb; ///< Время последнего трафика (tb) для idle-проверки
void* idle_timer; ///< Хендл таймера idle-проверки
struct cm_cb_node* cb_list; ///< Связный список callback'ов результата
uint64_t intermediaries[CONN_MGR_MAX_INTERMEDIARIES]; ///< Выбранные посредники (node_id)
uint8_t intermediariy_count; ///< Количество выбранных посредников
uint8_t rr_idx; ///< Round-robin индекс для выбора посредника при отправке
uint8_t local_scan_state; ///< Состояние локального сканирования (enum CM_TRY_STATE)
uint8_t main_connect_state; ///< Состояние основного подключения (enum CM_TRY_STATE)
struct {
uint8_t phase;
void* timer;
uint32_t request_id;
void* conn_ctx;
uint8_t phase; ///< Текущая фаза: 1=DIRECT, 2=REVERSE, 3=INDIRECT
void* timer; ///< Хендл таймера текущей фазы
uint32_t request_id; ///< request_id для INDIRECT-обмена
void* conn_ctx; ///< Контекст ETCP-соединения (для REVERSE)
} main;
struct CONN_MGR* mgr;
struct CONN_MGR* mgr; ///< Обратная ссылка на менеджер
};
/**
* @struct CONN_MGR
* @brief Корневая структура менеджера соединений.
*
* Создаётся один раз в conn_mgr_init(), хранится в UTUN_INSTANCE.
* Содержит динамический массив entries, список лучших кандидатов,
* таймеры фонового ping и список ожидающих обменов (reverse/exchange pending).
*/
struct CONN_MGR {
struct UTUN_INSTANCE* instance;
struct CONN_MGR_ENTRY* entries;
size_t entry_count;
size_t entry_capacity;
struct UTUN_INSTANCE* instance; ///< Родительский экземпляр utun
void* bg_ping_timer;
size_t bg_ping_cursor;
uint64_t bg_ping_cycle_start_tb;
struct CONN_MGR_ENTRY* entries; ///< Динамический массив записей о подключениях
size_t entry_count; ///< Текущее количество записей
size_t entry_capacity; ///< Выделенная ёмкость массива entries
void* candidate_ping_timer;
void* bg_ping_timer; ///< Хендл таймера фонового ping BGP-нод
size_t bg_ping_cursor; ///< Курсор текущей ноды в цикле обхода
uint64_t bg_ping_cycle_start_tb; ///< Время старта текущего цикла обхода (tb)
struct CONN_MGR_CANDIDATE best_candidates[CONN_MGR_MAX_CANDIDATES];
uint8_t best_candidate_count;
void* candidate_ping_timer; ///< Хендл таймера обновления RTT кандидатов
uint32_t next_request_id;
uint8_t initialized;
struct CONN_MGR_CANDIDATE best_candidates[CONN_MGR_MAX_CANDIDATES]; ///< Топ кандидатов-посредников, отсортированных по RTT
uint8_t best_candidate_count; ///< Количество актуальных кандидатов (≤CONN_MGR_MAX_CANDIDATES)
struct cm_reverse_pending* reverse_pending;
struct cm_exchange_pending* exchange_pending;
uint32_t next_request_id; ///< Счётчик request_id для INTERM_EXCHANGE
uint8_t initialized; ///< Флаг инициализации
struct cm_reverse_pending* reverse_pending; ///< Связный список ожидающих REVERSE-подключений
struct cm_exchange_pending* exchange_pending; ///< Связный список ожидающих INDIRECT-обменов
};
/* ==================== Жизненный цикл ==================== */
/**
* @brief Инициализирует менеджер соединений.
* @param instance родительский экземпляр utun
* @return указатель на CONN_MGR или NULL при ошибке
*
* Регистрирует обработчик в etcp_router для ETCP_RT_ID_CONN_MGR,
* запускает фоновые таймеры (bg_ping, candidate_ping).
*/
struct CONN_MGR* conn_mgr_init(struct UTUN_INSTANCE* instance);
void conn_mgr_destroy(struct CONN_MGR* mgr);
int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id,
uint32_t idle_timeout_ms,
conn_mgr_connect_callback_t cb, void* cb_arg);
int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id);
int conn_mgr_set_idle_timeout(struct CONN_MGR* mgr, uint64_t node_id, uint32_t timeout_ms);
int conn_mgr_get_status(struct CONN_MGR* mgr, uint64_t node_id,
uint8_t* out_state, uint8_t* out_conn_type);
int conn_mgr_add_alien_node(struct CONN_MGR* mgr, const uint8_t* nodeinfo_data, size_t len);
int conn_mgr_send(struct CONN_MGR* mgr, uint64_t node_id, struct ll_entry* entry);
/**
* @brief Уничтожает менеджер соединений.
* @param mgr менеджер
*
* Освобождает все записи, таймеры, pending-структуры.
* Вызывает cm_clear_nodeinfo для каждой записи.
*/
void conn_mgr_destroy(struct CONN_MGR* mgr);
/* ==================== Подключение / отключение ==================== */
/**
* @brief Запускает асинхронное подключение к ноде.
* @param mgr менеджер
* @param node_id идентификатор целевой ноды
* @param idle_timeout_ms таймаут неактивности (мс), 0 = без таймаута
* @param cb callback результата (CONN_MGR_OK, CONN_MGR_ERR_TIMEOUT, CONN_MGR_ERR_UNREACHABLE и т.д.)
* @param cb_arg пользовательский аргумент для callback
* @return CONN_MGR_OK если процесс запущен, CONN_MGR_ERR_* при ошибке
*
* @note Если нода уже подключена — сразу вызывает callback с CONN_MGR_OK и возвращает CONN_MGR_OK.
* @note Если нода в процессе подключения — добавляет callback в список ожидания.
* @note Автоматически определяет наличие BGP-соединения (уже существующий ETCP-линк).
* @note Для alien-нод используйте conn_mgr_add_alien_node().
*/
int conn_mgr_connect_node(struct CONN_MGR* mgr, uint64_t node_id,
uint32_t idle_timeout_ms,
conn_mgr_connect_callback_t cb, void* cb_arg);
/**
* @brief Разрывает подключение к ноде.
* @param mgr менеджер
* @param node_id идентификатор ноды
* @return CONN_MGR_OK или CONN_MGR_ERR_NOT_FOUND
*
* Отправляет DISCONNECT-пакет целевой ноде, сбрасывает conn_mgr_type в NODEINFO_Q,
* уничтожает запись entry.
*/
int conn_mgr_disconnect_node(struct CONN_MGR* mgr, uint64_t node_id);
/* ==================== Управление idle-таймаутом ==================== */
/**
* @brief Устанавливает idle-таймаут для подключённой ноды.
* @param mgr менеджер
* @param node_id идентификатор ноды
* @param timeout_ms таймаут неактивности (мс), 0 = отключить проверку
* @return CONN_MGR_OK или CONN_MGR_ERR_NOT_FOUND / CONN_MGR_ERR_INTERNAL
*
* Если таймаут > 0 и нода в состоянии CONNECTED — запускает периодический таймер.
* При отсутствии трафика в течение timeout_ms соединение автоматически разрывается.
*/
int conn_mgr_set_idle_timeout(struct CONN_MGR* mgr, uint64_t node_id, uint32_t timeout_ms);
/**
* @brief Получает текущий статус подключения к ноде.
* @param mgr менеджер
* @param node_id идентификатор ноды
* @param out_state [out] состояние (enum CONN_MGR_STATE)
* @param out_conn_type [out] тип соединения (CONN_TYPE_DIRECT/REVERSE/INDIRECT)
* @return CONN_MGR_OK
*/
int conn_mgr_get_status(struct CONN_MGR* mgr, uint64_t node_id,
uint8_t* out_state, uint8_t* out_conn_type);
/* ==================== Alien-ноды ==================== */
/**
* @brief Регистрирует alien-ноду (подключившуюся к нам самостоятельно).
* @param mgr менеджер
* @param nodeinfo_data сырые данные NODEINFO (BGP-пакет)
* @param len длина данных
* @return CONN_MGR_OK или CONN_MGR_ERR_INTERNAL
*
* Alien-нода — это нода, которая сама инициировала INIT-подключение к нам.
* Для неё не запускаются фазы подключения, сразу устанавливается CONN_MGR_STATE_CONNECTED.
* Создаёт entry с флагом alien=1.
*/
int conn_mgr_add_alien_node(struct CONN_MGR* mgr, const uint8_t* nodeinfo_data, size_t len);
/**
* @brief Отправляет данные подключённой ноде через etcp_router.
* @param mgr менеджер
* @param node_id идентификатор целевой ноды
* @param entry ll_entry с данными для отправки (освобождается после отправки)
* @return CONN_MGR_OK или CONN_MGR_ERR_NOT_FOUND
*
* Для INDIRECT-соединений пакет автоматически маршрутизируется через
* выбранного посредника (round-robin по intermediaries[]).
* Обновляет last_traffic_tb для idle-проверки.
*/
int conn_mgr_send(struct CONN_MGR* mgr, uint64_t node_id, struct ll_entry* entry);
/* ==================== Управление кандидатами ==================== */
/**
* @brief Обновляет RTT кандидата в списке лучших посредников.
* @param mgr менеджер
* @param node_id идентификатор ноды-кандидата
* @param rtt RTT до кандидата (мс)
*
* Если кандидат уже в списке — обновляет RTT. Если нет — вставляет с сортировкой
* по возрастанию RTT. Если список заполнен и новый кандидат имеет худший RTT —
* игнорируется. Список всегда отсортирован: best_candidates[0] — наименьший RTT.
*
* @note Обычно вызывается из route_connectivity после успешного probe.
*/
void conn_mgr_update_best_candidates(struct CONN_MGR* mgr, uint64_t node_id, uint16_t rtt);
#endif

69
src/utun_instance.c

@ -27,6 +27,7 @@
#include <unistd.h>
#include "../lib/platform_compat.h"
#include "../lib/mem.h"
#include "../lib/ll_queue.h"
@ -85,6 +86,35 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
return -1;
}
// Initialize networks queue (indexed by 56-bit network id)
instance->networks = queue_new(ua, 16, offsetof(struct NETWORK_ENTRY, id), 7, "networks");
if (!instance->networks) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to create networks queue");
return -1;
}
struct CFG_NETWORK *net = config->networks;
while (net) {
struct ll_entry *entry = queue_entry_new(sizeof(struct NETWORK_ENTRY));
if (!entry) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to allocate network entry for %s", net->name);
net = net->next;
continue;
}
struct NETWORK_ENTRY *ne = (struct NETWORK_ENTRY*)entry->data;
ne->id = net->id & 0x00FFFFFFFFFFFFFFULL;
for (int i = 0; i < SC_PUBKEY_SIZE; i++) {
unsigned int b; sscanf(net->pubkey_hex + i * 2, "%2x", &b); ne->pubkey[i] = (uint8_t)b;
}
for (int i = 0; i < SC_PRIVKEY_SIZE; i++) {
unsigned int b; sscanf(net->signing_key_hex + i * 2, "%2x", &b); ne->signing_key[i] = (uint8_t)b;
}
strncpy(ne->name, net->name, sizeof(ne->name) - 1);
ne->name[sizeof(ne->name) - 1] = '\0';
queue_data_put_with_index(instance->networks, entry);
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Network added: [%s] id=%012llx", ne->name, (unsigned long long)ne->id);
net = net->next;
}
// Create memory pools
instance->ack_pool = memory_pool_init(sizeof(struct ACK_PACKET), "ack_pool");
instance->data_pool = memory_pool_init(PACKET_DATA_SIZE, "data_pool");
@ -408,6 +438,16 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
instance->stcp_server = NULL;
}
// Cleanup networks queue
if (instance->networks) {
struct ll_entry *entry;
while ((entry = queue_data_get(instance->networks)) != NULL) {
queue_entry_free(entry);
}
queue_free(instance->networks);
instance->networks = NULL;
}
// Cleanup config
if (instance->config) {
DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "[INSTANCE_DESTROY] Freeing configuration");
@ -754,6 +794,35 @@ struct UTUN_INSTANCE *utun_instance_reload(struct UTUN_INSTANCE *instance, struc
l = next_l;
}
}
// Reload networks: clear and repopulate
if (instance->networks) {
struct ll_entry *entry;
while ((entry = queue_data_get(instance->networks)) != NULL) {
queue_entry_free(entry);
}
struct CFG_NETWORK *net = new_config->networks;
while (net) {
struct ll_entry *new_entry = queue_entry_new(sizeof(struct NETWORK_ENTRY));
if (!new_entry) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to allocate network entry for reload: %s", net->name);
net = net->next;
continue;
}
struct NETWORK_ENTRY *ne = (struct NETWORK_ENTRY*)new_entry->data;
ne->id = net->id & 0x00FFFFFFFFFFFFFFULL;
for (int i = 0; i < SC_PUBKEY_SIZE; i++) {
unsigned int b; sscanf(net->pubkey_hex + i * 2, "%2x", &b); ne->pubkey[i] = (uint8_t)b;
}
for (int i = 0; i < SC_PRIVKEY_SIZE; i++) {
unsigned int b; sscanf(net->signing_key_hex + i * 2, "%2x", &b); ne->signing_key[i] = (uint8_t)b;
}
strncpy(ne->name, net->name, sizeof(ne->name) - 1);
ne->name[sizeof(ne->name) - 1] = '\0';
queue_data_put_with_index(instance->networks, new_entry);
net = net->next;
}
DEBUG_INFO(DEBUG_CATEGORY_CONFIG, "Networks reloaded");
}
fw_free(&instance->fw);
fw_init(&instance->fw);
fw_load_rules(&instance->fw, &new_config->global);

10
src/utun_instance.h

@ -36,6 +36,13 @@ struct PING_CONTEXT;
struct CONN_MGR;
struct ETCP_CONNECT;
struct NETWORK_ENTRY {
uint64_t id; // 56-bit (offset 0 = index key)
uint8_t pubkey[SC_PUBKEY_SIZE]; // 32 bytes binary
uint8_t signing_key[SC_PRIVKEY_SIZE]; // 32 bytes binary
char name[64];
};
// uTun instance configuration
struct UTUN_INSTANCE {
// Identification
@ -119,6 +126,9 @@ struct UTUN_INSTANCE {
// TCP proxy server (exit node)
struct tcp_proxy_server tcp_proxy_server;
// Networks (queue of NETWORK_ENTRY, indexed by 56-bit id)
struct ll_queue* networks;
// Pending background connections (etcp_connect API)
struct ETCP_CONNECT* pending_connects;
uint32_t etcp_connect_timeout_tb; // Initial timeout in 0.1ms units, default 20000 (2s)

Loading…
Cancel
Save