#include #include #include "utun_instance.h" #include "routing_layer/topo_group.h" #include "transport_layer/etcp.h" #include "transport_layer/etcp_api.h" #include "transport_layer/node_conn_direct.h" #include "../lib/u_async.h" #include "../lib/debug_config.h" static struct TOPO_GROUP_CONN_ITEM* peer(struct TOPO_GROUP* group) { assert(group->senders_list->head && !group->senders_list->head->next); return (struct TOPO_GROUP_CONN_ITEM*)group->senders_list->head->data; } static void receive(struct ETCP_CONN* conn, uint64_t group_id, uint8_t cmd, uint64_t exchange_id, size_t length) { struct TOPOMSG_TABLE_REQ msg = { .cmd = ETCP_ID_TOPO_ENTRY, .subcmd = cmd, .group_id = group_id, .exchange_id = exchange_id }; assert(length <= sizeof(msg)); struct ll_entry* packet = ll_alloc_lldgram(sizeof(msg)); assert(packet); memcpy(packet->dgram, &msg, sizeof(msg)); packet->len = length; conn->instance->api_bindings.callbacks[ETCP_ID_TOPO_ENTRY](conn, packet); } static void complete(struct TOPO_GROUP* group, struct ETCP_CONN* conn, uint64_t exchange_id) { receive(conn, group->group_id, TOPO_SUBCMD_TABLE_COMPLETE, exchange_id, sizeof(struct TOPOMSG_TABLE_REQ)); } static void request(struct TOPO_GROUP* group, struct ETCP_CONN* conn) { receive(conn, group->group_id, TOPO_SUBCMD_REQUEST_TABLE, 123, sizeof(struct TOPOMSG_TABLE_REQ)); } int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); utun_instance_set_tun_init_enabled(0); struct UASYNC* ua = uasync_create(); assert(ua); struct UTUN_INSTANCE* inst = utun_instance_create_from_str(ua, "[global]\n" "my_private_key=704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f\n" "my_public_key=b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01\n" "[server: udp]\naddr=127.0.0.1:0\ntype=public\n"); assert(inst && utun_instance_init(inst) == 0); struct TOPO_GROUP* a = topo_groups_create_group(inst->topo_groups, 42, TOPO_GROUP_TYPE_UTUN, NULL); struct TOPO_GROUP* b = topo_groups_create_group(inst->topo_groups, 43, TOPO_GROUP_TYPE_UTUN, NULL); assert(a && b); struct SC_MYKEYS keys; assert(sc_generate_keypair(&keys) == SC_OK); struct TOPO_ADDR4 addr = { .addr = {127, 0, 0, 1}, .port = 9, .protocol = TOPO_PROTO_UDP }; struct TOPO_NODE node = { .v4_addrs = &addr }; memcpy(node.public_key, keys.public_key, SC_PUBKEY_SIZE); node.node_id = sc_derive_node_id_from_pubkey(node.public_key); struct NODE_CONN_DIRECT* owner = NULL; assert(node_conn_direct_open_node(inst, node.node_id, NULL, NULL, &owner, &node, NULL) == NCD_NEW); struct ETCP_CONN* conn = node_conn_direct_get_conn(owner); assert(conn); /* Детерминированная доставка управляющих пакетов через штатный BGP receiver. * Event loop не запускаем: транспортный handshake проверяют интеграционные тесты. */ conn->links_up = 1; assert(topo_group_new_conn(a, conn) == 0 && topo_group_new_conn(b, conn) == 0); uint64_t a_id = peer(a)->exchange_id, b_id = peer(b)->exchange_id; assert(a_id && b_id && a_id != b_id); assert(!topo_group_peer_ready(a, node.node_id) && !topo_group_peer_ready(b, node.node_id)); complete(a, conn, b_id); receive(conn, a->group_id, TOPO_SUBCMD_TABLE_COMPLETE, a_id, 2); assert(!peer(a)->table_received); complete(a, conn, a_id); assert(peer(a)->table_received && !topo_group_peer_ready(a, node.node_id)); request(a, conn); assert(topo_group_peer_ready(a, node.node_id) && !topo_group_peer_ready(b, node.node_id)); assert(topo_group_new_conn(a, conn) == 0 && peer(a)->exchange_id == a_id); receive(conn, a->group_id, TOPO_SUBCMD_JOIN_GROUP, 0, sizeof(struct TOPOMSG_JOIN_GROUP)); assert(peer(a)->exchange_id == a_id); assert(queue_entry_count(a->senders_list) == 1 && topo_group_peer_ready(a, node.node_id)); /* Другой порядок доставки: сначала наш снимок, потом подтверждение чужого. */ request(b, conn); assert(!topo_group_peer_ready(b, node.node_id)); complete(b, conn, b_id); assert(topo_group_peer_ready(b, node.node_id)); etcp_fire_conn_status(conn, ETCP_CONN_STATUS_REINIT); assert(peer(a)->exchange_id != a_id && peer(b)->exchange_id != b_id); assert(!topo_group_peer_ready(a, node.node_id) && !topo_group_peer_ready(b, node.node_id)); complete(a, conn, a_id); complete(b, conn, b_id); assert(!peer(a)->table_received && !peer(b)->table_received); /* Ошибка отправки снимка не разрешает TABLE_COMPLETE и READY. */ int saved_limit = conn->send_input_q->size_limit; conn->send_input_q->size_limit = 0; request(a, conn); assert(!peer(a)->table_sent); conn->send_input_q->size_limit = saved_limit; complete(a, conn, peer(a)->exchange_id); request(a, conn); assert(topo_group_peer_ready(a, node.node_id)); a_id = peer(a)->exchange_id; topo_group_remove_conn(a, conn, TOPO_REMOVE_LOCAL_LEAVE); complete(a, conn, a_id); assert(!topo_group_peer_ready(a, node.node_id)); assert(topo_group_new_conn(a, conn) == 0 && peer(a)->exchange_id != a_id); complete(a, conn, a_id); request(a, conn); assert(!topo_group_peer_ready(a, node.node_id)); complete(a, conn, peer(a)->exchange_id); assert(topo_group_peer_ready(a, node.node_id) && !topo_group_peer_ready(b, node.node_id)); conn->links_up = 0; assert(!topo_group_peer_ready(a, node.node_id)); node_conn_direct_close(owner); inst->running = 0; utun_instance_destroy(inst); uasync_poll(ua, 0); uasync_destroy(ua, 0); DEBUG_INFO(DEBUG_CATEGORY_BGP, "group exchange: isolation, duplicate attach, stale replies, send failure and REINIT passed"); return 0; }