Clarify group processing callbacks and failed route installation

This commit is contained in:
evgeny committed 2026-10-24 15:56:36 +02:00
1 parent 56d513ed85
commit 7ebfddfc4b
2 files changed
+48 -50

No files matched your search

+15 -40
View File
@@ -157,7 +157,8 @@ static void topo_group_peer_event(struct NODE_CONN_DIRECT* handle, enum ncd_even
int topo_group_peer_open(struct TOPO_GROUP* group, uint64_t node_id, struct TOPO_PEER_REQUEST** out) {
if (out) *out = NULL;
if (!group || !out || !node_id || !group->senders_list || group->stopping || !topo_group_peer_allowed(group, node_id)) {
if (!group || !group->instance || !out || !node_id || node_id == group->instance->node_id ||
!group->senders_list || group->stopping || !topo_group_peer_allowed(group, node_id)) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "cannot request group peer=%016llx: invalid arguments, stopped group or unknown member",
(unsigned long long)node_id); return -1;
}
@@ -352,38 +353,6 @@ static void nodeinfo_dump_log(const uint8_t* data, size_t len) {
ni->local_v4_sockets, ni->local_v4_addrs, ni->hop_count);
}
/* Форматирует NODEINFO в читаемую строку для лога (имя, подсети, адреса). */
static char* nodeinfo_format(const uint8_t* data, size_t len) {
if (!data || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return NULL;
struct TOPOMSG_NODEINFO_PKT* pkt = (struct TOPOMSG_NODEINFO_PKT*)data;
struct TOPOMSG_NODE* ni = &pkt->node;
uint64_t node_id = ni->node_id;
const uint8_t* dyn = data + sizeof(struct TOPOMSG_NODEINFO_PKT);
size_t off = 0;
char name_buf[64] = {0};
size_t nl = ni->node_name_len;
if (nl > 0 && nl < 63 && off + nl < len - sizeof(struct TOPOMSG_NODEINFO_PKT)) {
memcpy(name_buf, dyn + off, nl); off += nl;
}
off += ni->local_v4_sockets * sizeof(struct TOPOMSG_SOCKMETA4);
off += ni->local_v4_addrs * sizeof(struct TOPOMSG_ADDR4);
off += ni->local_v6_sockets * sizeof(struct TOPOMSG_SOCKMETA6);
off += ni->local_v6_addrs * sizeof(struct TOPOMSG_ADDR6);
char subs_buf[512] = {0};
if (ni->local_v4_subnets > 0) {
const struct TOPOMSG_SUBNET4* subs = (const struct TOPOMSG_SUBNET4*)(dyn + off);
size_t sl = 0;
for (uint8_t i = 0; i < ni->local_v4_subnets && sl < 400; i++) {
uint32_t addr = (subs[i].addr[0]<<24)|(subs[i].addr[1]<<16)|(subs[i].addr[2]<<8)|subs[i].addr[3];
char tmp[64]; snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%u ",(addr>>24)&255,(addr>>16)&255,(addr>>8)&255,addr&255,subs[i].prefix_length);
strcat(subs_buf,tmp); sl += strlen(tmp);
}
}
int need = snprintf(NULL,0,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v4a=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v4_addrs,(unsigned)ni->hop_count);
char* buf = u_malloc(need+1); if (!buf) return NULL;
snprintf(buf,need+1,"NODEINFO nid=%016llx ver=%u name=\"%s\" v4subs=\"%s\" v4s=%u v4a=%u hop=%u", (unsigned long long)node_id,(unsigned)ni->ver,name_buf,subs_buf,(unsigned)ni->local_v4_sockets,(unsigned)ni->local_v4_addrs,(unsigned)ni->hop_count);
return buf;
}
/* Читаемое имя sub-команды BGP для логов. */
static const char* group_subcmd_name(uint8_t subcmd) {
@@ -982,7 +951,10 @@ void topo_groups_set_node_updated_cb(struct TOPO_GROUPS* groups, topo_node_updat
static int topo_group_begin_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn, int retain) {
if (!group || !conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "invalid args"); return -1; }
if (group->stopping) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "join on stopped group"); return -1; }
if (!conn->instance) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "conn->instance is NULL"); return -1; }
if (conn->instance != group->instance || !conn->links_up || conn->close_requested) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "group join needs a live transport of the same instance peer=%016llx group=%016llx",
(unsigned long long)conn->peer_node_id, (unsigned long long)group->group_id); return -1;
}
if (!conn->instance->rt) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "conn->instance->rt is NULL"); return -1; }
if (!topo_group_peer_allowed(group, conn->peer_node_id)) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "group connect deferred: member unknown peer=%016llx group=%016llx",
@@ -1269,7 +1241,10 @@ static void topo_group_send_err_group_mismatch(struct TOPO_GROUP* group, struct
/* Обрабатывает NODEINFO: проверка подписи/версии, обновление узла, пути, роутинг, форвард. */
int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from, const uint8_t* data, size_t len) {
if (!group || !from || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return -1;
if (!group || !group->instance || !from || from->instance != group->instance || !data ||
len < sizeof(struct TOPOMSG_NODEINFO_PKT)) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "invalid NODEINFO context or length=%zu", len); return -1;
}
struct TOPOMSG_NODEINFO_PKT* pkt = (struct TOPOMSG_NODEINFO_PKT*)data;
struct TOPOMSG_NODE* ni = &pkt->node;
@@ -1483,7 +1458,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
/* Обрабатывает WITHDRAW: удаляет узел/пути, чистит роутинг, форвардит withdraw. */
int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* sender, const uint8_t* data, size_t len) {
if (!group || !sender || len != sizeof(struct TOPOMSG_WITHDRAW_PKT)) {
if (!group || !group->instance || !sender || sender->instance != group->instance || !data || len != sizeof(struct TOPOMSG_WITHDRAW_PKT)) {
DEBUG_WARN(DEBUG_CATEGORY_BGP, "invalid WITHDRAW len=%zu", len); return -1;
}
struct TOPOMSG_WITHDRAW_PKT* wp = (struct TOPOMSG_WITHDRAW_PKT*)data;
@@ -1954,9 +1929,9 @@ void topo_group_remove_peer_ready_cbk(struct TOPO_GROUP* group, topo_peer_ready_
}
void topo_group_add_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn, void* arg) {
if (!group || !fn) return;
if (!group || !fn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node subscription: invalid arguments"); return; }
struct topo_node_cbk_entry* e = u_malloc(sizeof(*e));
if (!e) return;
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "node subscription allocation failed"); return; }
e->fn = fn; e->arg = arg;
e->next = group->node_cbks;
group->node_cbks = e;
@@ -1980,9 +1955,9 @@ void topo_group_remove_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn,
/* Регистрирует глобальный колбэк изменения nodeinfo (уровень инстанса). */
void utun_add_nodeinfo_cbk(struct UTUN_INSTANCE* instance, nodeinfo_cbk_fn fn, void* arg) {
if (!instance || !fn) return;
if (!instance || !fn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "nodeinfo subscription: invalid arguments"); return; }
struct nodeinfo_cbk_entry* e = u_malloc(sizeof(*e));
if (!e) return;
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "nodeinfo subscription allocation failed"); return; }
e->fn = fn; e->arg = arg;
e->next = instance->nodeinfo_cbks;
instance->nodeinfo_cbks = e;
+33 -10
View File
@@ -76,6 +76,11 @@ typedef void (*topo_node_updated_fn)(struct UTUN_INSTANCE* inst, uint64_t node_i
typedef void (*topo_node_event_fn)(struct TOPO_GROUP* group, uint64_t node_id,
int event, void* arg);
/* Подписки принадлежат группе/instance; fn и arg заимствованы до remove/destroy.
* Вызов синхронный, в uasync. Callback не удаляет группу/узел и не меняет список
* подписчиков во время обхода; такие действия нужно отложить через uasync_call_soon.
* Повторная регистрация создаёт ещё одну подписку; remove снимает первое совпадение. */
struct topo_node_cbk_entry {
topo_node_event_fn fn;
void* arg;
@@ -300,12 +305,15 @@ struct TOPO_GROUP* topo_groups_get_default(struct TOPO_GROUPS* g);
struct TOPO_GROUP* topo_groups_find(struct TOPO_GROUPS* g, uint64_t group_id);
/**
* @brief Создаёт новую группу с указанным типом.
* @brief Создаёт и регистрирует новую группу; существующий group_id — ошибка.
* После регистрации присоединяет уже поднятые транспорты: CHAT — локальных мемберов,
* UTUN — пиров, которыми владеет конфигурация. Для CHAT по group_autoconnect
* (по умолчанию 1) включает подбор пиров. Ошибки этих попыток логируются отдельно.
*
* @param g контейнер групп
* @param group_id уникальный идентификатор
* @param group_type TOPO_GROUP_TYPE_UTUN или TOPO_GROUP_TYPE_CHAT
* @param channel_id идентификатор канала для групп типа CHAT (может быть NULL)
* @param channel_id локальный ключ таблиц CHAT; копируется до 63 байт, NULL оставляет пустой ключ
* @return TOPO_GROUP с local_node, conn_mgr, broadcast и radio или NULL с полным откатом создания
*/
struct TOPO_GROUP* topo_groups_create_group(struct TOPO_GROUPS* g, uint64_t group_id, uint8_t group_type, const char* channel_id);
@@ -320,7 +328,7 @@ 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);
/**
* @brief Присоединяет существующий транспорт к группе и начинает JOIN/ACCEPT.
* @brief Присоединяет UP-транспорт того же instance к группе и начинает JOIN/ACCEPT.
* Группа приобретает собственный NCD handle; повтор не сбрасывает сессию. 0/-1.
* Для обычного инициатора используйте отменяемый topo_group_peer_open().
*/
@@ -345,7 +353,7 @@ enum topo_peer_phase { TOPO_PEER_FAILED, TOPO_PEER_CONNECTING, TOPO_PEER_SYNCING
* Запрос остаётся валиден после удаления группы (phase=FAILED), caller обязан
* закрыть его. Все операции выполняются в потоке единственного uasync.
* progress — timebase последнего принятого NODEINFO/шага обмена, не polling time.
* open: 0 = запрос создан в *out, -1 = ошибка. */
* open: node_id ненулевой и не self; 0 = запрос создан в *out, -1 = ошибка (*out=NULL). */
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);
@@ -373,10 +381,18 @@ void topo_group_remove_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn, en
/**
* @brief Обрабатывает пакет NODEINFO.
*
* Проверка подписи и timestamp, обновление TOPO_GROUP_NODE, добавление пути,
* вставка в роутинг, broadcast если не max hops.
* data/len — полный TOPOMSG_NODEINFO_PKT с динамическими секциями; память заимствована.
* Диспетчер заранее проверяет поколения/членство сессии. Здесь проверяются длина,
* group_id тела, тип, подпись, конфликт timestamp и цепочка; добавляется ID from.
* Старый timestamp обновляет только путь. Для новой записи путь готовится до commit:
* отказ памяти сохраняет прежние запись/подсети/пути. Входная цепочка < MAX_HOPS,
* сохранённая после добавления соседа — 1..MAX_HOPS.
* Изменения распространяются из актуального состояния с backpressure.
* Для UTUN прежние IPv4-маршруты узла удаляются перед установкой нового набора.
* Пересечение/OOM маршрутов оставляет новый набор неустановленным, возвращает -1
* и запрещает READY; подписанная запись и групповые пути уже могут быть приняты.
*
* @return 0 при успехе
* @return 0 при применении/игнорировании собственного объявления, -1 при ошибке
*/
int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from, const uint8_t* data, size_t len);
/* Присоединить UP-транспорт; переданный handle остаётся у вызывающего. 0/-1. */
@@ -387,15 +403,20 @@ int topo_group_leave_peer(struct TOPO_GROUP* group, struct NODE_CONN_DIRECT* han
/**
* @brief Обрабатывает WITHDRAW.
*
* Удаляет затронутые пути; узел удаляется только без оставшихся путей.
* data/len — заимствованный полный TOPOMSG_WITHDRAW_PKT; поколения/членство
* проверяет диспетчер. Удаляет только путь sender; узел удаляется без оставшихся путей.
* Неизвестный узел/отсутствующий путь sender — успешный no-op.
*
* @return 0 при успехе
* @return 0 при успехе, -1 при неверных аргументах/длине
*/
int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* sender, const uint8_t* data, size_t len);
/**
* @brief Планирует NODEINFO узла одному пиру с backpressure.
* group/node/conn заимствованы на время вызова; очередь хранит только node_id.
* Запись и RTT читаются при отправке; повторные изменения узла объединяются.
* 0 — запланировано либо сессия ещё не ACCEPT, -1 — ошибка. Ошибка самой отложенной
* отправки сбрасывает READY и перезапускает обмен по watchdog.
*/
int topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, struct ETCP_CONN* conn);
@@ -414,7 +435,9 @@ struct ETCP_CONN* topo_group_find_conn_for_node(struct TOPO_GROUP* group, uint64
/**
* @brief Атомарно заменяет путь через conn; остальные соседи не затрагиваются.
*
* @return 0 при успехе
* nq/conn/hop_list заимствованы; цепочка 1..MAX_HOPS копируется. Семантику и петли
* проверяет вызывающий; функция проверяет размер и атомарно заменяет путь conn.
* @return 0 при успехе, -1 при неверных аргументах/OOM
*/
int topo_group_add_path(struct TOPO_GROUP_NODE* nq, struct ETCP_CONN* conn, uint64_t* hop_list, uint8_t hop_count, uint16_t cumulative_rtt);