From aa166a20303049cbe1c844ebbdc0f272d4f9acba Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 8 Sep 2026 16:39:55 +0300 Subject: [PATCH] =?UTF-8?q?topo=5Fgroup:=20RESYNC=20=E2=80=94=20=D0=BF?= =?UTF-8?q?=D0=B0=D1=81=D1=81=D0=B8=D0=B2=D0=BD=D0=B0=D1=8F=20=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=BE=D0=BD=D0=B0=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B7?= =?UTF-8?q?=D0=B0=D0=BF=D1=83=D1=81=D0=BA=D0=B0=D0=B5=D1=82=20BGP-=D0=BE?= =?UTF-8?q?=D0=B1=D0=BC=D0=B5=D0=BD=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20?= =?UTF-8?q?=D1=84=D0=BB=D1=8D=D0=BF=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Сервер (пассивная сторона) на UP шлёт общий RESYNC, клиент по нему заново анонсирует группы (JOIN_GROUP), которые ему положены по конфигу. Чинит потерю маршрутов при одностороннем флэпе, когда клиент не замечает разрыв и не перешлёт JOIN_GROUP повторно. --- src/routing_layer/topo_group.c | 45 ++++++++++++++++++++++++++++++++-- src/routing_layer/topo_group.h | 11 +++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/routing_layer/topo_group.c b/src/routing_layer/topo_group.c index 7426cc9c..fd52e80f 100644 --- a/src/routing_layer/topo_group.c +++ b/src/routing_layer/topo_group.c @@ -62,6 +62,19 @@ static void topo_group_send_join_group(struct TOPO_GROUP* group, struct ETCP_CON if (etcp_send(conn, e) != 0) { u_free(req); queue_entry_free(e); } } +static void topo_group_send_resync(struct ETCP_CONN* conn) { + if (!conn) return; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "Sending RESYNC to %s", conn->log_name); + struct TOPOMSG_RESYNC* req = u_calloc(1, sizeof(struct TOPOMSG_RESYNC)); + if (!req) return; + req->cmd = ETCP_ID_TOPO_ENTRY; + req->subcmd = TOPO_SUBCMD_RESYNC; + struct ll_entry* e = queue_entry_new(0); + if (!e) { u_free(req); return; } + e->dgram = (uint8_t*)req; e->len = sizeof(struct TOPOMSG_RESYNC); + if (etcp_send(conn, e) != 0) { u_free(req); queue_entry_free(e); } +} + static void topo_group_send_table_complete(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { if (!group || !conn) return; struct TOPOMSG_TABLE_REQ* req = u_calloc(1, sizeof(struct TOPOMSG_TABLE_REQ)); @@ -80,6 +93,8 @@ static bool topo_group_should_send_to(const struct TOPO_GROUP_NODE* nq, uint64_t static void topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn); static void topo_group_handle_request_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn); static void topo_group_handle_join_group(struct TOPO_GROUP* group, struct ETCP_CONN* conn); +static void topo_group_send_resync(struct ETCP_CONN* conn); +static void topo_group_handle_resync(struct UTUN_INSTANCE* instance, struct ETCP_CONN* conn); static void nodeinfo_dump_log(const uint8_t* data, size_t len) { if (!data || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return; @@ -130,6 +145,7 @@ static const char* group_subcmd_name(uint8_t subcmd) { case TOPO_SUBCMD_TABLE_COMPLETE: return "TABLE_COMPLETE"; case TOPO_SUBCMD_ERR_GROUP_MISMATCH: return "ERR_GROUP_MISMATCH"; case TOPO_SUBCMD_JOIN_GROUP: return "JOIN_GROUP"; + case TOPO_SUBCMD_RESYNC: return "RESYNC"; default: return "?"; } } @@ -169,6 +185,12 @@ static void topo_group_receive_cbk(struct ETCP_CONN* from_conn, struct ll_entry* uint8_t* data = entry->dgram; uint8_t cmd = data[0]; uint8_t subcmd = data[1]; if (cmd != ETCP_ID_TOPO_ENTRY) { queue_dgram_free(entry); queue_entry_free(entry); return; } + /* RESYNC — общий сигнал без group_id, обрабатывается до поиска группы */ + if (subcmd == TOPO_SUBCMD_RESYNC) { + topo_group_handle_resync(instance, from_conn); + queue_dgram_free(entry); queue_entry_free(entry); return; + } + uint64_t pkt_group_id = 0; if (subcmd == TOPO_SUBCMD_NODEINFO && entry->len >= 3) { pkt_group_id = ((struct TOPOMSG_NODEINFO_PKT*)data)->node.group_id; @@ -218,13 +240,18 @@ static void topo_group_conn_status(struct ETCP_CONN* conn, int status, void* arg if (!conn || !groups) return; if (status == ETCP_CONN_STATUS_UP && conn->peer_node_id) { - /* non-CHAT (UTUN): узлы из конфига (clients) + явные подключения (etcp_connect). */ + int is_client = conn->instance->config && config_peer_in_clients(conn->instance->config, conn->crypto_ctx.peer_public_key); int explicit_conn = conn->bgp_ready_cbk != NULL; + + /* пассивная сторона: просим пира заново анонсировать свои группы (после флэпа/переподключения) */ + if (!is_client && !explicit_conn) + topo_group_send_resync(conn); + + /* non-CHAT (UTUN): узлы из конфига (clients) + явные подключения (etcp_connect). */ struct ll_entry* fe = groups->group_list->head; while (fe) { struct TOPO_GROUP* g = (struct TOPO_GROUP*)fe; fe = fe->next; if (g->group_type != TOPO_GROUP_TYPE_CHAT) { - int is_client = conn->instance->config && config_peer_in_clients(conn->instance->config, conn->crypto_ctx.peer_public_key); if (is_client || explicit_conn) { topo_group_new_conn(g, conn); /* добавить + инициировать BGP */ topo_group_send_join_group(g, conn); /* запросить членство у пира */ @@ -1006,6 +1033,20 @@ static void topo_group_handle_join_group(struct TOPO_GROUP* group, struct ETCP_C topo_group_new_conn(group, conn); } +static void topo_group_handle_resync(struct UTUN_INSTANCE* instance, struct ETCP_CONN* conn) { + if (!instance || !conn || !conn->instance || !instance->topo_groups) return; + /* общий сигнал от пассивной стороны: рестартуем обмен, если мы для этого пира VPN-клиент */ + int is_client = conn->instance->config && config_peer_in_clients(conn->instance->config, conn->crypto_ctx.peer_public_key); + if (!is_client && conn->bgp_ready_cbk == NULL) return; + DEBUG_INFO(DEBUG_CATEGORY_BGP, "handle_resync: from %s — re-announce groups", conn->log_name); + struct ll_entry* fe = instance->topo_groups->group_list->head; + while (fe) { + struct TOPO_GROUP* g = (struct TOPO_GROUP*)fe; fe = fe->next; + if (g->group_type != TOPO_GROUP_TYPE_CHAT) + topo_group_send_join_group(g, conn); + } +} + /* ── BGP node event callbacks ── */ void topo_group_add_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn, void* arg) { diff --git a/src/routing_layer/topo_group.h b/src/routing_layer/topo_group.h index 3b74e50e..3e1ee6bb 100644 --- a/src/routing_layer/topo_group.h +++ b/src/routing_layer/topo_group.h @@ -97,6 +97,7 @@ void topo_fire_nodeinfo_cbk(struct UTUN_INSTANCE* instance, struct TOPO_GROUP* g #define TOPO_SUBCMD_TABLE_COMPLETE 0x0B // завершение начальной синхронизации таблицы #define TOPO_SUBCMD_ERR_GROUP_MISMATCH 0x0C // ошибка несоответствия типа группы #define TOPO_SUBCMD_JOIN_GROUP 0x0D // запрос членства в группе +#define TOPO_SUBCMD_RESYNC 0x0E // общий запрос: «я переподключился, заново анонсируй свои группы» #define MAX_HOPS 16 #define BGP_NODES_HASH_SIZE 256 @@ -139,6 +140,16 @@ struct TOPOMSG_JOIN_GROUP { uint64_t group_id; // идентификатор группы } __attribute__((packed)); +/** + * @brief Общий запрос ресинхронизации: отправитель (пассивная сторона) переподключился + * и просит пира заново анонсировать свои группы (без group_id — группа решается + * принимающей стороной по её конфигу). + */ +struct TOPOMSG_RESYNC { + uint8_t cmd; // ETCP_ID_TOPO_ENTRY + uint8_t subcmd; // TOPO_SUBCMD_RESYNC +} __attribute__((packed)); + /** * @brief Пакет ошибки несоответствия типа группы */