|
|
|
@ -1,32 +1,13 @@ |
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @file topo_group.h |
|
|
|
* @file topo_group.h |
|
|
|
* @brief BGP-подобный обмен топологией узлов между пирами через ETCP. |
|
|
|
* @brief Групповые сессии и обмен маршрутами поверх общего транспорта NCD. |
|
|
|
* |
|
|
|
* |
|
|
|
* Идея: модуль автоматически выстраивает карту маршрутизации между узлами, |
|
|
|
* У каждой группы своя топология; идентичности и адреса узлов общие (topo_node.h). |
|
|
|
* используя только пассивное наблюдение (подписка на события) |
|
|
|
* UTUN обменивается также подсетями, CHAT допускает только локально известных участников. |
|
|
|
* Можно иметь много групп узлов. Группа - это список узлов.
|
|
|
|
* Для подключения используйте peer_open/close: группа владеет NCD с начала попытки. |
|
|
|
* Для каждой группы выстраивается своя независимая таблица маршрутизации. |
|
|
|
* JOIN/ACCEPT согласует сессию, NODEINFO/WITHDRAW поддерживают её маршруты. |
|
|
|
* Один узел может входить в любое число групп. |
|
|
|
* Потерю путей обрабатывает topo_recovery, выбор CHAT-пиров — topo_group_connect. |
|
|
|
|
|
|
|
* Все операции — в потоке uasync. Сервисы создают и удаляют свои группы явно. |
|
|
|
* Механика: |
|
|
|
|
|
|
|
* - Узлы обмениваются информацией друг о друге (pubkey, адреса, подсети) |
|
|
|
|
|
|
|
* через NODEINFO/WITHDRAW сообщения, маршрутизируемые по ETCP. |
|
|
|
|
|
|
|
* - Каждый узел хранит полную таблицу известных узлов (структура TOPO_NODEQ). |
|
|
|
|
|
|
|
* - При подключении нового пира — полная синхронизация таблицы. |
|
|
|
|
|
|
|
* - При отключении пира — withdraw всех узлов, достижимых только через него. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Группы (TOPO_GROUP): |
|
|
|
|
|
|
|
* - TOPO_GROUP_TYPE_UTUN — основная VPN-сеть (обмен маршрутами, подсетями) |
|
|
|
|
|
|
|
* - TOPO_GROUP_TYPE_CHAT — чат-группа (только узлы, без подсетей) |
|
|
|
|
|
|
|
* - Каждая группа изолирована: узлы из utun-группы не видны в чат-группе и наоборот |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Дополнительные функции: |
|
|
|
|
|
|
|
* - NAT-детекция: выделена в отдельный модуль nat_detection.h |
|
|
|
|
|
|
|
* - Поиск оптимального маршрута до узла (topo_group_find_conn_for_node) |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Хранение: все узлы сохраняются в SQLite (таблицы nodes, node_addresses, |
|
|
|
|
|
|
|
* channels, peers_<channel_id>). База открывается в topo_groups_init() |
|
|
|
|
|
|
|
* если в конфиге задан db_path. |
|
|
|
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
#ifndef TOPO_GROUP_H |
|
|
|
#ifndef TOPO_GROUP_H |
|
|
|
#define TOPO_GROUP_H |
|
|
|
#define TOPO_GROUP_H |
|
|
|
@ -170,19 +151,19 @@ struct TOPOMSG_ERR_GROUP_MISMATCH { |
|
|
|
} __attribute__((packed)); |
|
|
|
} __attribute__((packed)); |
|
|
|
|
|
|
|
|
|
|
|
struct TOPO_GROUP_CONN_ITEM { |
|
|
|
struct TOPO_GROUP_CONN_ITEM { |
|
|
|
struct TOPO_GROUP* group; |
|
|
|
struct TOPO_GROUP* group; // владелец сессии
|
|
|
|
uint64_t node_id; |
|
|
|
uint64_t node_id; // идентичность пира, включая CONNECTING
|
|
|
|
struct ETCP_CONN* conn; // NULL до UP, в состоянии CONNECTING
|
|
|
|
struct ETCP_CONN* conn; // NULL до UP, в состоянии CONNECTING
|
|
|
|
struct NODE_CONN_DIRECT* handle; // владение BGP этим conn (NCD-handle)
|
|
|
|
struct NODE_CONN_DIRECT* handle; // владение BGP этим conn (NCD-handle)
|
|
|
|
struct TOPO_PEER_REQUEST* requests; |
|
|
|
struct TOPO_PEER_REQUEST* requests; // запросы инициаторов; каждый закрывает свой
|
|
|
|
uint64_t progress; // время последнего прогресса обмена (timebase)
|
|
|
|
uint64_t progress; // время последнего прогресса обмена (timebase)
|
|
|
|
uint8_t retained; // участие запрошено постоянным владельцем или достигло READY
|
|
|
|
uint8_t retained; // участие запрошено постоянным владельцем или достигло READY
|
|
|
|
uint64_t local_epoch, peer_epoch; // поколения сторон текущего присоединения
|
|
|
|
uint64_t local_epoch, peer_epoch; // поколения сторон текущего присоединения
|
|
|
|
uint8_t accepted; // JOIN_ACCEPT подтвердил local_epoch
|
|
|
|
uint8_t accepted; // JOIN_ACCEPT подтвердил local_epoch
|
|
|
|
uint8_t tx_failed, transport_down; |
|
|
|
uint8_t tx_failed, transport_down; // ошибка отправки / потеря транспорта
|
|
|
|
struct topo_tx_item *tx_head, *tx_tail; |
|
|
|
struct topo_tx_item *tx_head, *tx_tail; // FIFO команд, ещё не переданных транспорту
|
|
|
|
struct queue_waiter_handle tx_waiter; |
|
|
|
struct queue_waiter_handle tx_waiter; // ожидание свободной send_input_q
|
|
|
|
void* tx_wake; |
|
|
|
void* tx_wake; // отложенный запуск отправки
|
|
|
|
uint8_t table_received; // TABLE_COMPLETE текущей сессии принят
|
|
|
|
uint8_t table_received; // TABLE_COMPLETE текущей сессии принят
|
|
|
|
uint8_t table_sent; // ответ на запрос пира целиком передан транспорту
|
|
|
|
uint8_t table_sent; // ответ на запрос пира целиком передан транспорту
|
|
|
|
}; |
|
|
|
}; |
|
|
|
@ -206,7 +187,7 @@ struct TOPO_GROUP { |
|
|
|
struct ll_queue* senders_list; // ll_entry.data = TOPO_GROUP_CONN_ITEM; включает CONNECTING с conn=NULL
|
|
|
|
struct ll_queue* senders_list; // ll_entry.data = TOPO_GROUP_CONN_ITEM; включает CONNECTING с conn=NULL
|
|
|
|
struct ll_queue* nodes; // TOPO_GROUP_NODE{ll_entry,node_id,paths,...} — per-group узлы, хеш-индекс по node_id(8B)
|
|
|
|
struct ll_queue* nodes; // TOPO_GROUP_NODE{ll_entry,node_id,paths,...} — per-group узлы, хеш-индекс по node_id(8B)
|
|
|
|
struct TOPO_GROUP_NODE* local_node; // свой узел в этой группе
|
|
|
|
struct TOPO_GROUP_NODE* local_node; // свой узел в этой группе
|
|
|
|
uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; |
|
|
|
uint8_t ed25519_public_key[SC_PUBKEY_SIZE]; // публичный ключ локального узла
|
|
|
|
char channel_id[64]; // channel_id для групп типа CHAT
|
|
|
|
char channel_id[64]; // channel_id для групп типа CHAT
|
|
|
|
struct CONN_MGR* conn_mgr; // менеджер соединений для этой группы
|
|
|
|
struct CONN_MGR* conn_mgr; // менеджер соединений для этой группы
|
|
|
|
struct TOPO_RECOVERY_CTX* recovery; // один последовательный recovery на группу
|
|
|
|
struct TOPO_RECOVERY_CTX* recovery; // один последовательный recovery на группу
|
|
|
|
@ -215,7 +196,7 @@ struct TOPO_GROUP { |
|
|
|
struct radio_ctx* radio; // radio (PTT walkie-talkie) per-group context
|
|
|
|
struct radio_ctx* radio; // radio (PTT walkie-talkie) per-group context
|
|
|
|
uint8_t radio_active; // 1 = я слушаю рацию этого канала (TOPO_FLAG_RADIO в NODEINFO)
|
|
|
|
uint8_t radio_active; // 1 = я слушаю рацию этого канала (TOPO_FLAG_RADIO в NODEINFO)
|
|
|
|
struct topo_node_cbk_entry* node_cbks; // цепочка подписчиков на события узлов
|
|
|
|
struct topo_node_cbk_entry* node_cbks; // цепочка подписчиков на события узлов
|
|
|
|
uint8_t stopping; |
|
|
|
uint8_t stopping; // teardown начат; новые попытки запрещены
|
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
@ -235,7 +216,8 @@ struct TOPO_GROUPS { |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @brief Создаёт контейнер групп и группу utun по умолчанию (group_id=1). |
|
|
|
* @brief Создаёт реестр и локальную идентичность; сервисных групп пока нет. |
|
|
|
|
|
|
|
* Открывает общую SQLite: db_path/chats.db, без db_path — :memory:. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param instance экземпляр utun с node_id и конфигом |
|
|
|
* @param instance экземпляр utun с node_id и конфигом |
|
|
|
* @return TOPO_GROUPS или NULL при ошибке |
|
|
|
* @return TOPO_GROUPS или NULL при ошибке |
|
|
|
@ -257,12 +239,12 @@ void topo_group_on_activity_change(struct UTUN_INSTANCE* instance, int active, v |
|
|
|
void topo_groups_destroy(struct UTUN_INSTANCE* instance); |
|
|
|
void topo_groups_destroy(struct UTUN_INSTANCE* instance); |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @brief Возвращает группу по умолчанию (group_id=TOPO_GROUP_UTUN=0x8000000000000000). |
|
|
|
* @brief Возвращает UTUN-группу или NULL, если сервис не запущен. Указатель заимствован. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
struct TOPO_GROUP* topo_groups_get_default(struct TOPO_GROUPS* g); |
|
|
|
struct TOPO_GROUP* topo_groups_get_default(struct TOPO_GROUPS* g); |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @brief Находит группу по id. |
|
|
|
* @brief Заимствованный указатель на группу по id; NULL, если её нет. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
struct TOPO_GROUP* topo_groups_find(struct TOPO_GROUPS* g, uint64_t group_id); |
|
|
|
struct TOPO_GROUP* topo_groups_find(struct TOPO_GROUPS* g, uint64_t group_id); |
|
|
|
|
|
|
|
|
|
|
|
@ -287,9 +269,9 @@ struct TOPO_GROUP* topo_groups_create_group(struct TOPO_GROUPS* g, uint64_t grou |
|
|
|
void topo_groups_remove_group(struct TOPO_GROUPS* g, uint64_t group_id); |
|
|
|
void topo_groups_remove_group(struct TOPO_GROUPS* g, uint64_t group_id); |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @brief Добавляет conn в senders_list (если нет), отправляет запрос таблицы (nodeinfo). |
|
|
|
* @brief Присоединяет существующий транспорт к группе и начинает JOIN/ACCEPT. |
|
|
|
* |
|
|
|
* Группа приобретает собственный NCD handle; повтор не сбрасывает сессию. 0/-1. |
|
|
|
* Вызывается при ETCP on_up. |
|
|
|
* Для обычного инициатора используйте отменяемый topo_group_peer_open(). |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
int topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn); |
|
|
|
int topo_group_new_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn); |
|
|
|
|
|
|
|
|
|
|
|
@ -311,19 +293,20 @@ enum topo_peer_phase { TOPO_PEER_FAILED, TOPO_PEER_CONNECTING, TOPO_PEER_SYNCING |
|
|
|
* CLOSED/leave/удаление группы отсоединяет запрос окончательно. |
|
|
|
* CLOSED/leave/удаление группы отсоединяет запрос окончательно. |
|
|
|
* Запрос остаётся валиден после удаления группы (phase=FAILED), caller обязан |
|
|
|
* Запрос остаётся валиден после удаления группы (phase=FAILED), caller обязан |
|
|
|
* закрыть его. Все операции выполняются в потоке единственного uasync. |
|
|
|
* закрыть его. Все операции выполняются в потоке единственного uasync. |
|
|
|
* progress — timebase последнего принятого NODEINFO/шага обмена, не polling time. */ |
|
|
|
* progress — timebase последнего принятого NODEINFO/шага обмена, не polling time. |
|
|
|
|
|
|
|
* open: 0 = запрос создан в *out, -1 = ошибка. */ |
|
|
|
int topo_group_peer_open(struct TOPO_GROUP* group, uint64_t node_id, struct TOPO_PEER_REQUEST** out); |
|
|
|
int topo_group_peer_open(struct TOPO_GROUP* group, uint64_t node_id, struct TOPO_PEER_REQUEST** out); |
|
|
|
|
|
|
|
/* Освободить свой запрос; после вызова request недействителен. NULL допустим. */ |
|
|
|
void topo_group_peer_close(struct TOPO_PEER_REQUEST* request); |
|
|
|
void topo_group_peer_close(struct TOPO_PEER_REQUEST* request); |
|
|
|
struct ETCP_CONN* topo_group_peer_conn(const struct TOPO_PEER_REQUEST* request); /* borrowed, including CONNECTING */ |
|
|
|
struct ETCP_CONN* topo_group_peer_conn(const struct TOPO_PEER_REQUEST* request); /* borrowed, including CONNECTING */ |
|
|
|
|
|
|
|
/* Завершить всю сессию этой пары: LEAVE, удаление путей, отсоединение всех запросов. */ |
|
|
|
void topo_group_peer_leave(struct TOPO_GROUP* group, uint64_t node_id); |
|
|
|
void topo_group_peer_leave(struct TOPO_GROUP* group, uint64_t node_id); |
|
|
|
|
|
|
|
/* Текущая фаза; NULL или отсоединённый запрос дают FAILED. */ |
|
|
|
enum topo_peer_phase topo_group_peer_phase(const struct TOPO_PEER_REQUEST* request); |
|
|
|
enum topo_peer_phase topo_group_peer_phase(const struct TOPO_PEER_REQUEST* request); |
|
|
|
|
|
|
|
/* Время последнего прогресса в единицах 0.1 ms; 0, если сессии нет. */ |
|
|
|
uint64_t topo_group_peer_progress(const struct TOPO_PEER_REQUEST* request); |
|
|
|
uint64_t topo_group_peer_progress(const struct TOPO_PEER_REQUEST* request); |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/* Причина завершения групповой сессии определяет необходимость recovery. */ |
|
|
|
* @brief Удаляет conn из senders_list, очищает paths во всех nodes, отправляет withdraw если node unreachable. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* Вызывается при ETCP on_down. |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
enum topo_group_remove_reason { |
|
|
|
enum topo_group_remove_reason { |
|
|
|
TOPO_REMOVE_TRANSPORT_DOWN, |
|
|
|
TOPO_REMOVE_TRANSPORT_DOWN, |
|
|
|
TOPO_REMOVE_LOCAL_LEAVE, |
|
|
|
TOPO_REMOVE_LOCAL_LEAVE, |
|
|
|
@ -331,25 +314,29 @@ enum topo_group_remove_reason { |
|
|
|
TOPO_REMOVE_MEMBER_INVALID, |
|
|
|
TOPO_REMOVE_MEMBER_INVALID, |
|
|
|
TOPO_REMOVE_REJECTED |
|
|
|
TOPO_REMOVE_REJECTED |
|
|
|
}; |
|
|
|
}; |
|
|
|
/* Только потеря транспорта запускает recovery каскадно потерянных маршрутов. */ |
|
|
|
/* Сбросить сессию и удалить пути через conn, разослать WITHDRAW для потерянных узлов.
|
|
|
|
|
|
|
|
* При DOWN живые запросы сохраняют NCD для повторного UP. Только потеря транспорта |
|
|
|
|
|
|
|
* запускает recovery каскадно потерянных маршрутов. */ |
|
|
|
void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn, enum topo_group_remove_reason reason); |
|
|
|
void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn, enum topo_group_remove_reason reason); |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @brief Обрабатывает пакет NODEINFO. |
|
|
|
* @brief Обрабатывает пакет NODEINFO. |
|
|
|
* |
|
|
|
* |
|
|
|
* Проверка версии, обновление или создание TOPO_NODEQ, добавление пути, |
|
|
|
* Проверка подписи и timestamp, обновление TOPO_GROUP_NODE, добавление пути, |
|
|
|
* вставка в роутинг, broadcast если не max hops. |
|
|
|
* вставка в роутинг, broadcast если не max hops. |
|
|
|
* |
|
|
|
* |
|
|
|
* @return 0 при успехе |
|
|
|
* @return 0 при успехе |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from, const uint8_t* data, size_t len); |
|
|
|
int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from, const uint8_t* data, size_t len); |
|
|
|
|
|
|
|
/* Присоединить UP-транспорт; переданный handle остаётся у вызывающего. 0/-1. */ |
|
|
|
int topo_group_join_peer(struct TOPO_GROUP* group, struct NODE_CONN_DIRECT* handle); |
|
|
|
int topo_group_join_peer(struct TOPO_GROUP* group, struct NODE_CONN_DIRECT* handle); |
|
|
|
|
|
|
|
/* Отсоединить пира и запланировать LEAVE. Чужой handle не забирает. 0/-1. */ |
|
|
|
int topo_group_leave_peer(struct TOPO_GROUP* group, struct NODE_CONN_DIRECT* handle); |
|
|
|
int topo_group_leave_peer(struct TOPO_GROUP* group, struct NODE_CONN_DIRECT* handle); |
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @brief Обрабатывает WITHDRAW. |
|
|
|
* @brief Обрабатывает WITHDRAW. |
|
|
|
* |
|
|
|
* |
|
|
|
* Удаляет node из роутинга и nodes, broadcast withdraw. |
|
|
|
* Удаляет затронутые пути; узел удаляется только без оставшихся путей. |
|
|
|
* |
|
|
|
* |
|
|
|
* @return 0 при успехе |
|
|
|
* @return 0 при успехе |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
@ -369,11 +356,12 @@ void topo_group_send_withdraw(struct TOPO_GROUP* group, uint64_t node_id); |
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* @brief Поиск оптимального ETCP соединения для указанного node_id. |
|
|
|
* @brief Поиск оптимального ETCP соединения для указанного node_id. |
|
|
|
* |
|
|
|
* |
|
|
|
* Перебирает paths узла, выбирает путь с минимальным hop_count. |
|
|
|
* Выбирает минимум hop_count среди живых путей. Если живых нет, возвращает |
|
|
|
|
|
|
|
* лучший сохранённый путь: ненулевой результат сам по себе не гарантирует UP. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param group указатель на TOPO_GROUP |
|
|
|
* @param group указатель на TOPO_GROUP |
|
|
|
* @param node_id целевой узел |
|
|
|
* @param node_id целевой узел |
|
|
|
* @return оптимальный ETCP_CONN* или NULL |
|
|
|
* @return заимствованный ETCP_CONN* или NULL |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64_t node_id); |
|
|
|
struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64_t node_id); |
|
|
|
|
|
|
|
|
|
|
|
|