Browse Source

topo_group: RESYNC — пассивная сторона перезапускает BGP-обмен после флэпа

Сервер (пассивная сторона) на UP шлёт общий RESYNC, клиент по нему
заново анонсирует группы (JOIN_GROUP), которые ему положены по конфигу.
Чинит потерю маршрутов при одностороннем флэпе, когда клиент не замечает
разрыв и не перешлёт JOIN_GROUP повторно.
v2
evgeny 3 weeks ago
parent
commit
aa166a2030
  1. 45
      src/routing_layer/topo_group.c
  2. 11
      src/routing_layer/topo_group.h

45
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); } 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) { static void topo_group_send_table_complete(struct TOPO_GROUP* group, struct ETCP_CONN* conn) {
if (!group || !conn) return; if (!group || !conn) return;
struct TOPOMSG_TABLE_REQ* req = u_calloc(1, sizeof(struct TOPOMSG_TABLE_REQ)); 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_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_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_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) { static void nodeinfo_dump_log(const uint8_t* data, size_t len) {
if (!data || len < sizeof(struct TOPOMSG_NODEINFO_PKT)) return; 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_TABLE_COMPLETE: return "TABLE_COMPLETE";
case TOPO_SUBCMD_ERR_GROUP_MISMATCH: return "ERR_GROUP_MISMATCH"; case TOPO_SUBCMD_ERR_GROUP_MISMATCH: return "ERR_GROUP_MISMATCH";
case TOPO_SUBCMD_JOIN_GROUP: return "JOIN_GROUP"; case TOPO_SUBCMD_JOIN_GROUP: return "JOIN_GROUP";
case TOPO_SUBCMD_RESYNC: return "RESYNC";
default: return "?"; 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]; 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; } 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; uint64_t pkt_group_id = 0;
if (subcmd == TOPO_SUBCMD_NODEINFO && entry->len >= 3) { if (subcmd == TOPO_SUBCMD_NODEINFO && entry->len >= 3) {
pkt_group_id = ((struct TOPOMSG_NODEINFO_PKT*)data)->node.group_id; 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 (!conn || !groups) return;
if (status == ETCP_CONN_STATUS_UP && conn->peer_node_id) { 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; 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; struct ll_entry* fe = groups->group_list->head;
while (fe) { while (fe) {
struct TOPO_GROUP* g = (struct TOPO_GROUP*)fe; fe = fe->next; struct TOPO_GROUP* g = (struct TOPO_GROUP*)fe; fe = fe->next;
if (g->group_type != TOPO_GROUP_TYPE_CHAT) { 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) { if (is_client || explicit_conn) {
topo_group_new_conn(g, conn); /* добавить + инициировать BGP */ topo_group_new_conn(g, conn); /* добавить + инициировать BGP */
topo_group_send_join_group(g, conn); /* запросить членство у пира */ 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); 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 ── */ /* ── BGP node event callbacks ── */
void topo_group_add_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn, void* arg) { void topo_group_add_node_cbk(struct TOPO_GROUP* group, topo_node_event_fn fn, void* arg) {

11
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_TABLE_COMPLETE 0x0B // завершение начальной синхронизации таблицы
#define TOPO_SUBCMD_ERR_GROUP_MISMATCH 0x0C // ошибка несоответствия типа группы #define TOPO_SUBCMD_ERR_GROUP_MISMATCH 0x0C // ошибка несоответствия типа группы
#define TOPO_SUBCMD_JOIN_GROUP 0x0D // запрос членства в группе #define TOPO_SUBCMD_JOIN_GROUP 0x0D // запрос членства в группе
#define TOPO_SUBCMD_RESYNC 0x0E // общий запрос: «я переподключился, заново анонсируй свои группы»
#define MAX_HOPS 16 #define MAX_HOPS 16
#define BGP_NODES_HASH_SIZE 256 #define BGP_NODES_HASH_SIZE 256
@ -139,6 +140,16 @@ struct TOPOMSG_JOIN_GROUP {
uint64_t group_id; // идентификатор группы uint64_t group_id; // идентификатор группы
} __attribute__((packed)); } __attribute__((packed));
/**
* @brief Общий запрос ресинхронизации: отправитель (пассивная сторона) переподключился
* и просит пира заново анонсировать свои группы (без group_id — группа решается
* принимающей стороной по её конфигу).
*/
struct TOPOMSG_RESYNC {
uint8_t cmd; // ETCP_ID_TOPO_ENTRY
uint8_t subcmd; // TOPO_SUBCMD_RESYNC
} __attribute__((packed));
/** /**
* @brief Пакет ошибки несоответствия типа группы * @brief Пакет ошибки несоответствия типа группы
*/ */

Loading…
Cancel
Save