|
|
|
|
@ -44,6 +44,11 @@ struct TOPO_PEER_REQUEST {
|
|
|
|
|
|
|
|
|
|
static int topo_group_begin_conn(struct TOPO_GROUP* group, struct ETCP_CONN* conn, int retain); |
|
|
|
|
static int topo_group_peer_allowed(struct TOPO_GROUP* group, uint64_t node_id); |
|
|
|
|
static void topo_tx_clear(struct TOPO_GROUP_CONN_ITEM* peer); |
|
|
|
|
static int topo_tx_control(struct TOPO_GROUP_CONN_ITEM* peer, const void* data, size_t size); |
|
|
|
|
static int topo_tx_node(struct TOPO_GROUP_CONN_ITEM* peer, uint64_t node_id); |
|
|
|
|
static int topo_tx_table(struct TOPO_GROUP_CONN_ITEM* peer); |
|
|
|
|
static int topo_control_wait(struct ETCP_CONN* conn, const void* data, size_t size); |
|
|
|
|
|
|
|
|
|
static struct TOPO_GROUP_CONN_ITEM* topo_group_peer(const struct TOPO_GROUP* group, uint64_t peer_id) { |
|
|
|
|
for (struct ll_entry* e = group && group->senders_list ? group->senders_list->head : NULL; e; e = e->next) { |
|
|
|
|
@ -59,6 +64,7 @@ int topo_group_peer_ready(const struct TOPO_GROUP* group, uint64_t peer_id) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void topo_group_peer_free(struct TOPO_GROUP_CONN_ITEM* peer) { |
|
|
|
|
topo_tx_clear(peer); |
|
|
|
|
while (peer->requests) { |
|
|
|
|
struct TOPO_PEER_REQUEST* request = peer->requests; |
|
|
|
|
peer->requests = request->next; request->peer = NULL; request->next = NULL; |
|
|
|
|
@ -119,7 +125,7 @@ int topo_group_peer_open(struct TOPO_GROUP* group, uint64_t node_id, struct TOPO
|
|
|
|
|
|
|
|
|
|
enum topo_peer_phase topo_group_peer_phase(const struct TOPO_PEER_REQUEST* request) { |
|
|
|
|
struct TOPO_GROUP_CONN_ITEM* peer = request ? request->peer : NULL; |
|
|
|
|
if (!peer) return TOPO_PEER_FAILED; |
|
|
|
|
if (!peer || peer->tx_failed) return TOPO_PEER_FAILED; |
|
|
|
|
if (!peer->conn || !peer->conn->links_up) return TOPO_PEER_CONNECTING; |
|
|
|
|
return topo_group_peer_ready(peer->group, peer->node_id) ? TOPO_PEER_READY : TOPO_PEER_SYNCING; |
|
|
|
|
} |
|
|
|
|
@ -163,7 +169,7 @@ static struct TOPOMSG_HEADER topo_header(struct TOPO_GROUP_CONN_ITEM* peer, uint
|
|
|
|
|
.src_epoch = peer->local_epoch, .dst_epoch = peer->peer_epoch }; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int topo_send_control(struct ETCP_CONN* conn, const void* data, size_t size) { |
|
|
|
|
static int topo_emit_control(struct ETCP_CONN* conn, const void* data, size_t size) { |
|
|
|
|
struct ll_entry* e = ll_alloc_lldgram(size); |
|
|
|
|
if (!e) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group control allocation failed size=%zu", size); return -1; } |
|
|
|
|
memcpy(e->dgram, data, size); e->len = size; |
|
|
|
|
@ -173,7 +179,20 @@ static int topo_send_control(struct ETCP_CONN* conn, const void* data, size_t si
|
|
|
|
|
queue_dgram_free(e); queue_entry_free(e); return -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Сообщения сессии проходят одну FIFO. Stateless RESYNC/REJECT также ждут
|
|
|
|
|
* пустой транспортной очереди, но не создают групповое участие. */ |
|
|
|
|
static int topo_send_control(struct ETCP_CONN* conn, const void* data, size_t size) { |
|
|
|
|
if (size >= sizeof(struct TOPOMSG_HEADER)) { |
|
|
|
|
const struct TOPOMSG_HEADER* h = data; |
|
|
|
|
struct TOPO_GROUP* group = topo_groups_find(conn->instance->topo_groups, h->group_id); |
|
|
|
|
struct TOPO_GROUP_CONN_ITEM* peer = topo_group_peer(group, conn->peer_node_id); |
|
|
|
|
if (peer && peer->conn == conn && peer->local_epoch == h->src_epoch) return topo_tx_control(peer, data, size); |
|
|
|
|
} |
|
|
|
|
return topo_control_wait(conn, data, size); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int topo_group_reset_exchange(struct TOPO_GROUP_CONN_ITEM* peer) { |
|
|
|
|
topo_tx_clear(peer); peer->tx_failed = 0; |
|
|
|
|
peer->accepted = 0; peer->table_received = 0; peer->table_sent = 0; peer->peer_epoch = 0; |
|
|
|
|
uint64_t epoch; |
|
|
|
|
if (random_bytes((uint8_t*)&epoch, sizeof(epoch)) != 0 || !epoch) { |
|
|
|
|
@ -212,7 +231,7 @@ static int topo_group_send_table_complete(struct TOPO_GROUP* group, struct ETCP_
|
|
|
|
|
struct TOPO_GROUP_CONN_ITEM* peer = topo_group_peer(group, conn->peer_node_id); |
|
|
|
|
if (!peer || !peer->accepted) return -1; |
|
|
|
|
struct TOPOMSG_HEADER msg = topo_header(peer, TOPO_SUBCMD_TABLE_COMPLETE); |
|
|
|
|
return topo_send_control(conn, &msg, sizeof(msg)); |
|
|
|
|
return topo_emit_control(conn, &msg, sizeof(msg)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void topo_group_reject(struct ETCP_CONN* conn, const struct TOPOMSG_HEADER* h, enum topo_join_reject reason) { |
|
|
|
|
@ -225,7 +244,6 @@ static void topo_group_reject(struct ETCP_CONN* conn, const struct TOPOMSG_HEADE
|
|
|
|
|
|
|
|
|
|
static int topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN* conn); |
|
|
|
|
static bool topo_group_should_send_to(const struct TOPO_GROUP_NODE* nq, uint64_t target_id); |
|
|
|
|
static int 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, const struct TOPOMSG_JOIN_GROUP* msg); |
|
|
|
|
static void topo_group_handle_resync(struct UTUN_INSTANCE* instance, struct ETCP_CONN* conn); |
|
|
|
|
@ -660,7 +678,7 @@ void topo_group_on_activity_change(struct UTUN_INSTANCE* instance, int active, v
|
|
|
|
|
struct ll_entry* se = g->senders_list->head; |
|
|
|
|
while (se) { |
|
|
|
|
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)se->data; |
|
|
|
|
if (item && item->conn) topo_group_send_nodeinfo(g, g->local_node, item->conn, 0); |
|
|
|
|
if (item && item->conn) topo_group_send_nodeinfo(g, g->local_node, item->conn); |
|
|
|
|
se = se->next; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -806,7 +824,7 @@ void topo_group_set_radio(struct TOPO_GROUP* group, int on) {
|
|
|
|
|
while (se) { |
|
|
|
|
struct TOPO_GROUP_CONN_ITEM* item = (struct TOPO_GROUP_CONN_ITEM*)se->data; |
|
|
|
|
if (item && item->conn && group->local_node) |
|
|
|
|
topo_group_send_nodeinfo(group, group->local_node, item->conn, 0); |
|
|
|
|
topo_group_send_nodeinfo(group, group->local_node, item->conn); |
|
|
|
|
se = se->next; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -1235,8 +1253,7 @@ int topo_group_process_nodeinfo(struct TOPO_GROUP* group, struct ETCP_CONN* from
|
|
|
|
|
if (id != node_id) { |
|
|
|
|
int found = 0; for (int i = 0; i < hop_count; i++) if (hop_list[i] == id) found = 1; |
|
|
|
|
if (found == 0) { |
|
|
|
|
uint16_t out_rtt = topo_get_chain_rtt(nodeinfo1); |
|
|
|
|
topo_group_send_nodeinfo(group, nodeinfo1, item->conn, out_rtt); |
|
|
|
|
topo_group_send_nodeinfo(group, nodeinfo1, item->conn); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -1289,7 +1306,7 @@ int topo_group_process_withdraw(struct TOPO_GROUP* group, struct ETCP_CONN* send
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Сериализует и шлёт NODEINFO узла конкретному пиру. */ |
|
|
|
|
int topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, struct ETCP_CONN* conn, uint16_t cumulative_rtt) { |
|
|
|
|
static int topo_emit_nodeinfo(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, struct ETCP_CONN* conn, uint16_t cumulative_rtt) { |
|
|
|
|
if (!group || !node || !conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "send_nodeinfo: invalid arguments"); return -1; } |
|
|
|
|
|
|
|
|
|
size_t max_sz = sizeof(struct TOPOMSG_NODEINFO_PKT) + TOPO_NODE_WIRE_MAX_SIZE; |
|
|
|
|
@ -1334,6 +1351,206 @@ int topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* n
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* NODEINFO сериализуется только при отправке: указатели на узлы не переживают callback. */ |
|
|
|
|
int topo_group_send_nodeinfo(struct TOPO_GROUP* group, struct TOPO_GROUP_NODE* node, struct ETCP_CONN* conn) { |
|
|
|
|
if (!group || !node || !conn) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "send_nodeinfo: invalid arguments"); return -1; } |
|
|
|
|
struct TOPO_GROUP_CONN_ITEM* peer = topo_group_peer(group, conn->peer_node_id); |
|
|
|
|
if (!peer || !peer->accepted) return 0; |
|
|
|
|
return topo_tx_node(peer, node->node_id); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
enum topo_tx_kind { TOPO_TX_CONTROL, TOPO_TX_NODE, TOPO_TX_TABLE }; |
|
|
|
|
struct topo_tx_item { |
|
|
|
|
struct topo_tx_item* next; |
|
|
|
|
enum topo_tx_kind kind; |
|
|
|
|
uint64_t node_id; |
|
|
|
|
struct ll_entry* packet; |
|
|
|
|
uint64_t* nodes; |
|
|
|
|
size_t count, pos, sent; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static void topo_tx_item_free(struct topo_tx_item* item) { |
|
|
|
|
if (item->packet) { queue_dgram_free(item->packet); queue_entry_free(item->packet); } |
|
|
|
|
u_free(item->nodes); u_free(item); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void topo_tx_clear(struct TOPO_GROUP_CONN_ITEM* peer) { |
|
|
|
|
if (peer->tx_wake) { uasync_call_soon_cancel(peer->group->instance->ua, peer->tx_wake); peer->tx_wake = NULL; } |
|
|
|
|
if (peer->conn && peer->conn->send_input_q) queue_waiter_cancel(peer->conn->send_input_q, &peer->tx_waiter); |
|
|
|
|
while (peer->tx_head) { |
|
|
|
|
struct topo_tx_item* item = peer->tx_head; peer->tx_head = item->next; topo_tx_item_free(item); |
|
|
|
|
} |
|
|
|
|
peer->tx_tail = NULL; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void topo_tx_fail(struct TOPO_GROUP_CONN_ITEM* peer) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group sender failed: group=%016llx peer=%016llx epoch=%016llx", |
|
|
|
|
(unsigned long long)peer->group->group_id, (unsigned long long)peer->node_id, (unsigned long long)peer->local_epoch); |
|
|
|
|
topo_tx_clear(peer); peer->tx_failed = 1; peer->table_sent = 0; |
|
|
|
|
topo_recovery_changed(peer->group); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void topo_tx_wait(void* arg); |
|
|
|
|
static void topo_tx_schedule(struct TOPO_GROUP_CONN_ITEM* peer) { |
|
|
|
|
if (!peer->tx_head || peer->tx_failed || peer->tx_wake || peer->tx_waiter.internal || peer->tx_waiter.call_soon_id) return; |
|
|
|
|
peer->tx_wake = uasync_call_soon(peer->group->instance->ua, peer, topo_tx_wait); |
|
|
|
|
if (!peer->tx_wake) topo_tx_fail(peer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int topo_tx_append(struct TOPO_GROUP_CONN_ITEM* peer, struct topo_tx_item* item) { |
|
|
|
|
if (!item || peer->tx_failed) { if (item) topo_tx_item_free(item); topo_tx_fail(peer); return -1; } |
|
|
|
|
if (peer->tx_tail) peer->tx_tail->next = item; else peer->tx_head = item; |
|
|
|
|
peer->tx_tail = item; topo_tx_schedule(peer); |
|
|
|
|
return peer->tx_failed ? -1 : 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int topo_tx_control(struct TOPO_GROUP_CONN_ITEM* peer, const void* data, size_t size) { |
|
|
|
|
struct topo_tx_item* item = u_calloc(1, sizeof(*item)); |
|
|
|
|
if (!item) { topo_tx_fail(peer); return -1; } |
|
|
|
|
item->packet = ll_alloc_lldgram(size); |
|
|
|
|
if (!item->packet) { u_free(item); topo_tx_fail(peer); return -1; } |
|
|
|
|
memcpy(item->packet->dgram, data, size); item->packet->len = size; |
|
|
|
|
item->kind = TOPO_TX_CONTROL; |
|
|
|
|
return topo_tx_append(peer, item); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int topo_tx_node(struct TOPO_GROUP_CONN_ITEM* peer, uint64_t node_id) { |
|
|
|
|
/* Последнее изменение занимает своё место после уже ожидающих WITHDRAW.
|
|
|
|
|
* На один узел хранится не более одного несериализованного NODEINFO. */ |
|
|
|
|
struct topo_tx_item** link = &peer->tx_head; |
|
|
|
|
struct topo_tx_item* prev = NULL; |
|
|
|
|
struct topo_tx_item* item = NULL; |
|
|
|
|
while (*link) { |
|
|
|
|
if ((*link)->kind == TOPO_TX_NODE && (*link)->node_id == node_id) { |
|
|
|
|
item = *link; *link = item->next; |
|
|
|
|
if (peer->tx_tail == item) peer->tx_tail = prev; |
|
|
|
|
item->next = NULL; break; |
|
|
|
|
} |
|
|
|
|
prev = *link; link = &(*link)->next; |
|
|
|
|
} |
|
|
|
|
if (!item) item = u_calloc(1, sizeof(*item)); |
|
|
|
|
if (!item) { topo_tx_fail(peer); return -1; } |
|
|
|
|
item->kind = TOPO_TX_NODE; item->node_id = node_id; |
|
|
|
|
return topo_tx_append(peer, item); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int topo_tx_table(struct TOPO_GROUP_CONN_ITEM* peer) { |
|
|
|
|
for (struct topo_tx_item* item = peer->tx_head; item; item = item->next) |
|
|
|
|
if (item->kind == TOPO_TX_TABLE) return 0; |
|
|
|
|
struct TOPO_GROUP* group = peer->group; |
|
|
|
|
struct topo_tx_item* item = u_calloc(1, sizeof(*item)); |
|
|
|
|
if (!item) { topo_tx_fail(peer); return -1; } |
|
|
|
|
item->kind = TOPO_TX_TABLE; |
|
|
|
|
size_t capacity = (size_t)queue_entry_count(group->nodes) + 1; |
|
|
|
|
item->nodes = u_malloc(capacity * sizeof(*item->nodes)); |
|
|
|
|
if (!item->nodes) { u_free(item); topo_tx_fail(peer); return -1; } |
|
|
|
|
item->nodes[item->count++] = group->local_node->node_id; |
|
|
|
|
for (struct ll_entry* e = group->nodes->head; e; e = e->next) { |
|
|
|
|
struct TOPO_GROUP_NODE* node = (struct TOPO_GROUP_NODE*)e; |
|
|
|
|
if (node != group->local_node && topo_group_should_send_to(node, peer->node_id)) item->nodes[item->count++] = node->node_id; |
|
|
|
|
} |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "group table queued: group=%016llx peer=%016llx epoch=%016llx nodes=%zu", |
|
|
|
|
(unsigned long long)group->group_id, (unsigned long long)peer->node_id, (unsigned long long)peer->local_epoch, item->count); |
|
|
|
|
return topo_tx_append(peer, item); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int topo_tx_emit_node(struct TOPO_GROUP_CONN_ITEM* peer, uint64_t id) { |
|
|
|
|
struct TOPO_GROUP* group = peer->group; |
|
|
|
|
struct TOPO_GROUP_NODE* node = id == group->local_node->node_id ? group->local_node : topo_node_find_by_id(group, id); |
|
|
|
|
if (!node || (node != group->local_node && !topo_group_should_send_to(node, peer->node_id))) return 0; |
|
|
|
|
return topo_emit_nodeinfo(group, node, peer->conn, node == group->local_node ? 0 : topo_get_chain_rtt(node)) < 0 ? -1 : 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void topo_tx_ready(struct ll_queue* queue, void* arg) { |
|
|
|
|
struct TOPO_GROUP_CONN_ITEM* peer = arg; |
|
|
|
|
/* Deferred waiters must recheck: another producer can occupy the queue first. */ |
|
|
|
|
if (queue_entry_count(queue)) { topo_tx_schedule(peer); return; } |
|
|
|
|
struct topo_tx_item* item = peer->tx_head; |
|
|
|
|
if (!item) return; |
|
|
|
|
int result = 0, complete = 1; |
|
|
|
|
if (item->kind == TOPO_TX_CONTROL) { |
|
|
|
|
result = etcp_send(peer->conn, item->packet); |
|
|
|
|
if (result == 0) item->packet = NULL; |
|
|
|
|
} else if (item->kind == TOPO_TX_NODE) result = topo_tx_emit_node(peer, item->node_id); |
|
|
|
|
else { |
|
|
|
|
if (item->pos < item->count) { |
|
|
|
|
result = topo_tx_emit_node(peer, item->nodes[item->pos++]); |
|
|
|
|
if (result > 0) item->sent++; |
|
|
|
|
complete = 0; |
|
|
|
|
} else { |
|
|
|
|
result = topo_group_send_table_complete(peer->group, peer->conn); |
|
|
|
|
if (result == 0) { |
|
|
|
|
peer->table_sent = 1; topo_group_log_exchange(peer->group, peer); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "group table sent: group=%016llx peer=%016llx epoch=%016llx nodes=%zu", |
|
|
|
|
(unsigned long long)peer->group->group_id, (unsigned long long)peer->node_id, |
|
|
|
|
(unsigned long long)peer->local_epoch, item->sent); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (result < 0) { topo_tx_fail(peer); return; } |
|
|
|
|
topo_group_peer_progressed(peer->group, peer->node_id); |
|
|
|
|
if (complete) { |
|
|
|
|
peer->tx_head = item->next; |
|
|
|
|
if (!peer->tx_head) peer->tx_tail = NULL; |
|
|
|
|
topo_tx_item_free(item); |
|
|
|
|
} |
|
|
|
|
topo_tx_schedule(peer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void topo_tx_wait(void* arg) { |
|
|
|
|
struct TOPO_GROUP_CONN_ITEM* peer = arg; |
|
|
|
|
peer->tx_wake = NULL; |
|
|
|
|
if (!peer->conn || !peer->conn->send_input_q || peer->conn->close_requested || |
|
|
|
|
queue_waiter_wait(peer->conn->send_input_q, &peer->tx_waiter, topo_tx_ready, peer) < 0) topo_tx_fail(peer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Небольшой ответ вне групповой сессии владеет NCD только до постановки пакета
|
|
|
|
|
* в транспорт. CLOSED/DOWN отменяет waiter до уничтожения транспортной очереди. */ |
|
|
|
|
struct topo_control { |
|
|
|
|
struct NODE_CONN_DIRECT* handle; |
|
|
|
|
struct ll_queue* queue; |
|
|
|
|
struct queue_waiter_handle waiter; |
|
|
|
|
struct ll_entry* packet; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static void topo_control_free(struct topo_control* control) { |
|
|
|
|
queue_waiter_cancel(control->queue, &control->waiter); |
|
|
|
|
node_conn_direct_close(control->handle); |
|
|
|
|
queue_dgram_free(control->packet); queue_entry_free(control->packet); u_free(control); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void topo_control_event(struct NODE_CONN_DIRECT* handle, enum ncd_event event, void* arg) { |
|
|
|
|
(void)handle; |
|
|
|
|
if (event == NCD_EVENT_UP) return; |
|
|
|
|
DEBUG_WARN(DEBUG_CATEGORY_BGP, "pending group control cancelled transport_event=%d", event); |
|
|
|
|
topo_control_free(arg); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void topo_control_ready(struct ll_queue* queue, void* arg) { |
|
|
|
|
struct topo_control* control = arg; |
|
|
|
|
if (queue_entry_count(queue)) { |
|
|
|
|
if (queue_waiter_wait(queue, &control->waiter, topo_control_ready, control) >= 0) return; |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "cannot rearm group control waiter"); |
|
|
|
|
} else if (etcp_send(node_conn_direct_get_conn(control->handle), control->packet) == 0) control->packet = NULL; |
|
|
|
|
else DEBUG_ERROR(DEBUG_CATEGORY_BGP, "cannot send pending group control"); |
|
|
|
|
topo_control_free(control); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int topo_control_wait(struct ETCP_CONN* conn, const void* data, size_t size) { |
|
|
|
|
if (!conn->send_input_q || conn->close_requested) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "group control on closing transport"); return -1; } |
|
|
|
|
if (!queue_entry_count(conn->send_input_q)) return topo_emit_control(conn, data, size); |
|
|
|
|
struct topo_control* control = u_calloc(1, sizeof(*control)); |
|
|
|
|
if (!control) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group control allocation failed"); return -1; } |
|
|
|
|
control->queue = conn->send_input_q; control->packet = ll_alloc_lldgram(size); |
|
|
|
|
if (!control->packet) { DEBUG_ERROR(DEBUG_CATEGORY_BGP, "group control packet allocation failed"); u_free(control); return -1; } |
|
|
|
|
memcpy(control->packet->dgram, data, size); control->packet->len = size; |
|
|
|
|
if (node_conn_direct_open(conn->instance, conn->peer_node_id, topo_control_event, control, &control->handle, NULL) == NCD_ERR || |
|
|
|
|
queue_waiter_wait(control->queue, &control->waiter, topo_control_ready, control) < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "cannot retain pending group control"); topo_control_free(control); return -1; |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Добавляет conn в senders_list (дедуп), если его там ещё нет. */ |
|
|
|
|
static int topo_group_add_to_senders(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { |
|
|
|
|
if (!group || !conn || !group->senders_list) return -1; |
|
|
|
|
@ -1420,7 +1637,7 @@ static void topo_leave_ready(struct ll_queue* queue, void* arg) {
|
|
|
|
|
(unsigned long long)leave->group_id); |
|
|
|
|
topo_leave_finish(leave); return; |
|
|
|
|
} |
|
|
|
|
int result = topo_send_control(conn, &leave->message, sizeof(leave->message)); |
|
|
|
|
int result = topo_emit_control(conn, &leave->message, sizeof(leave->message)); |
|
|
|
|
if (result != 0) DEBUG_ERROR(DEBUG_CATEGORY_BGP, "cannot send LEAVE_GROUP group=%016llx", (unsigned long long)leave->group_id); |
|
|
|
|
else DEBUG_INFO(DEBUG_CATEGORY_BGP, "LEAVE_GROUP sent: peer=%016llx group=%016llx", |
|
|
|
|
(unsigned long long)node_conn_direct_node_id(leave->handle), (unsigned long long)leave->group_id); |
|
|
|
|
@ -1454,38 +1671,10 @@ int topo_group_leave_peer(struct TOPO_GROUP* group, struct NODE_CONN_DIRECT* han
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Шлёт пиру полную таблицу узлов группы (все узлы, кроме достижимых через него). */ |
|
|
|
|
static int topo_group_send_full_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { |
|
|
|
|
uint64_t target = conn->peer_node_id; |
|
|
|
|
struct ll_entry* e = group->nodes ? group->nodes->head : NULL; |
|
|
|
|
while (e) { |
|
|
|
|
struct TOPO_GROUP_NODE* nq = (struct TOPO_GROUP_NODE*)e; |
|
|
|
|
if (nq->node_id == group->instance->node_id) { e = e->next; continue; } |
|
|
|
|
if (!nq->paths) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "node has no paths"); e = e->next; continue; } |
|
|
|
|
if (topo_group_should_send_to(nq, target) && topo_group_send_nodeinfo(group, nq, conn, topo_get_chain_rtt(nq)) < 0) return -1; |
|
|
|
|
e = e->next; |
|
|
|
|
} |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Обрабатывает REQUEST_TABLE: шлёт свой nodeinfo + полную таблицу + TABLE_COMPLETE. */ |
|
|
|
|
/* Повторный REQUEST_TABLE той же сессии не создаёт второй снимок. */ |
|
|
|
|
static void topo_group_handle_request_table(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { |
|
|
|
|
if (!group || !conn) return; |
|
|
|
|
if (!topo_group_peer_allowed(group, conn->peer_node_id)) { |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_BGP, "table request deferred: member unknown peer=%016llx group=%016llx", |
|
|
|
|
(unsigned long long)conn->peer_node_id, (unsigned long long)group->group_id); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
if (topo_group_send_nodeinfo(group, group->local_node, conn, 0) < 0 || topo_group_send_full_table(group, conn) < 0) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_BGP, "table exchange incomplete group=%016llx peer=%016llx", |
|
|
|
|
(unsigned long long)group->group_id, (unsigned long long)conn->peer_node_id); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
/* senders_list заполняется только через topo_group_new_conn (add + BGP);
|
|
|
|
|
здесь не добавляем, иначе new_conn (на JOIN_GROUP) упирается в дедуп и не шлёт TABLE_REQ обратно. */ |
|
|
|
|
if (topo_group_send_table_complete(group, conn) < 0) return; |
|
|
|
|
struct TOPO_GROUP_CONN_ITEM* peer = topo_group_peer(group, conn->peer_node_id); |
|
|
|
|
if (peer && peer->conn == conn && !peer->table_sent) { peer->table_sent = 1; topo_group_log_exchange(group, peer); } |
|
|
|
|
if (peer && peer->accepted && !peer->table_sent) topo_tx_table(peer); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Обрабатывает JOIN_GROUP: для UTUN — добавить запросившего и инициировать BGP обратно. */ |
|
|
|
|
|