/* Управляемая сеть: настоящий BGP receiver/sender, подписи и очереди, без UDP. * Доставка по каждому ребру сохраняет FIFO, порядок между рёбрами меняется. */ #include #include #include #include "utun_instance.h" #include "config_updater.h" #include "topo_group.h" #include "etcp.h" #include "etcp_api.h" #include "node_conn_direct.h" #include "../lib/debug_config.h" #include "test_utils.h" #define N 4 #define GROUP 42 static struct UASYNC* ua; static struct UTUN_INSTANCE* nodes[N]; static struct TOPO_GROUP* groups[N]; static struct ETCP_CONN* conns[N][N]; static struct NODE_CONN_DIRECT* owners[N][N]; static unsigned graph, delivered; static uint32_t random_state = 1; static unsigned next_random(void) { random_state = random_state * 1664525U + 1013904223U; return random_state; } static unsigned edge(int a, int b) { if (a > b) { int t = a; a = b; b = t; } unsigned bit = 1; for (int i = 0; i < N; i++) for (int j = i + 1; j < N; j++, bit <<= 1) if (i == a && j == b) return bit; return 0; } static void release(struct ll_entry* e) { queue_dgram_free(e); queue_entry_free(e); } /* Единственный ручной потребитель send_input_q; штатный normalizer отключён. */ static int step(void) { uasync_poll(ua, 0); int work = 0, offset = next_random() % (N * N); for (int k = 0; k < N * N; k++) { int a = ((k + offset) % (N * N)) / N, b = (k + offset) % N; if (a == b) continue; struct ll_queue* q = conns[a][b]->send_input_q; assert(queue_entry_count(q) <= 1); struct ll_entry* e = queue_data_get(q); if (!e) continue; work++; if ((graph & edge(a, b)) && e->len && e->dgram[0] == ETCP_ID_TOPO_ENTRY) { delivered++; nodes[b]->api_bindings.callbacks[ETCP_ID_TOPO_ENTRY](conns[b][a], e); } else release(e); queue_resume_callback(q); } return work; } static void settle(void) { unsigned before = delivered; int idle = 0; for (int i = 0; i < 4000 && idle < 16; i++) idle = step() ? 0 : idle + 1; assert(idle == 16); DEBUG_INFO(DEBUG_CATEGORY_BGP, "model settled graph=%02x packets=%u", graph, delivered - before); } /* Смена графа без обработки очередей между событиями разрыва и восстановления. */ static void change(unsigned mask) { unsigned old = graph; graph = mask; for (int a = 0; a < N; a++) for (int b = a + 1; b < N; b++) { if (!(old & edge(a, b)) || (mask & edge(a, b))) continue; conns[a][b]->links_up = conns[b][a]->links_up = 0; topo_group_remove_conn(groups[a], conns[a][b], TOPO_REMOVE_REMOTE_LEAVE); topo_group_remove_conn(groups[b], conns[b][a], TOPO_REMOVE_REMOTE_LEAVE); struct ll_entry* e; while ((e = queue_data_get(conns[a][b]->send_input_q))) release(e); while ((e = queue_data_get(conns[b][a]->send_input_q))) release(e); } for (int a = 0; a < N; a++) for (int b = a + 1; b < N; b++) { if ((old & edge(a, b)) || !(mask & edge(a, b))) continue; conns[a][b]->links_up = conns[b][a]->links_up = 1; assert(topo_group_new_conn(groups[a], conns[a][b]) == 0); assert(topo_group_new_conn(groups[b], conns[b][a]) == 0); } } /* Независимый эталон — кратчайшие расстояния в графе реальных соединений. */ static void verify(void) { int distance[N][N]; for (int a = 0; a < N; a++) for (int b = 0; b < N; b++) distance[a][b] = a == b ? 0 : (graph & edge(a, b)) ? 1 : 100; for (int k = 0; k < N; k++) for (int a = 0; a < N; a++) for (int b = 0; b < N; b++) if (distance[a][k] + distance[k][b] < distance[a][b]) distance[a][b] = distance[a][k] + distance[k][b]; for (int a = 0; a < N; a++) for (int b = 0; b < N; b++) { if (a == b) continue; struct ETCP_CONN* conn = topo_group_find_conn_for_node(groups[a], nodes[b]->node_id); DEBUG_DEBUG(DEBUG_CATEGORY_BGP, "verify graph=%02x src=%d dst=%d expected=%d conn=%p", graph, a, b, distance[a][b], conn); assert((conn != NULL) == (distance[a][b] < 100)); if (!conn) continue; struct TOPO_GROUP_NODE* node = topo_node_find_by_id(groups[a], nodes[b]->node_id); uint8_t count; uint64_t* hops = topo_node_best_hop_list(node, &count, NULL); assert(count == distance[a][b]); int previous = a; for (int h = count - 1; h >= 0; h--) { int current = 0; while (current < N && nodes[current]->node_id != hops[h]) current++; assert(current < N && (graph & edge(previous, current))); previous = current; } assert(previous == b); for (int h = 0; h < count; h++) for (int j = h + 1; j < count; j++) assert(hops[h] != hops[j]); if (graph & edge(a, b)) assert(topo_group_peer_ready(groups[a], nodes[b]->node_id)); } } static void create(void) { ua = uasync_create(); assert(ua); for (int i = 0; i < N; i++) { struct SC_MYKEYS keys; assert(sc_generate_keypair(&keys) == SC_OK); char pub[65], priv[65], config[512]; bytes_to_hex(keys.public_key, 32, pub, sizeof(pub)); bytes_to_hex(keys.private_key, 32, priv, sizeof(priv)); snprintf(config, sizeof(config), "[global]\nmy_public_key=%s\nmy_private_key=%s\n", pub, priv); nodes[i] = utun_instance_create_from_str(ua, config); assert(nodes[i]); assert(utun_core_start(nodes[i]) == 0); groups[i] = topo_groups_create_group(nodes[i]->topo_groups, GROUP, TOPO_GROUP_TYPE_UTUN, NULL); assert(groups[i]); } for (int a = 0; a < N; a++) for (int b = 0; b < N; b++) { if (a == b) continue; struct ETCP_CONN* c = conns[a][b] = etcp_connection_create(nodes[a], "virtual_bgp"); assert(c); c->peer_node_id = nodes[b]->node_id; assert(sc_init_ctx(&c->crypto_ctx, &nodes[a]->my_keys) == SC_OK); assert(sc_set_peer_public_key(&c->crypto_ctx, nodes[b]->my_keys.public_key, SC_PEER_PUBKEY_BIN) == SC_OK); queue_set_callback(c->send_input_q, NULL, NULL); etcp_conn_ready(c); assert(node_conn_direct_open(nodes[a], c->peer_node_id, NULL, NULL, &owners[a][b], NULL) >= 0); } } int main(void) { debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_BGP, DEBUG_LEVEL_DEBUG); utun_instance_set_tun_init_enabled(0); create(); unsigned triangle = edge(0, 1) | edge(1, 2) | edge(0, 2) | edge(2, 3); change(triangle); settle(); verify(); /* One-sided REINIT with routes retained, then simultaneous REINIT at all peers. */ etcp_fire_conn_status(conns[0][1], ETCP_CONN_STATUS_REINIT); settle(); verify(); for (int a = 0; a < N; a++) for (int b = 0; b < N; b++) if (graph & edge(a, b)) etcp_fire_conn_status(conns[a][b], ETCP_CONN_STATUS_REINIT); settle(); verify(); change(triangle & ~edge(0, 2)); settle(); verify(); change(triangle); settle(); verify(); change(triangle & ~edge(1, 2) & ~edge(0, 2)); settle(); verify(); change(triangle); settle(); verify(); /* Все графы, включая изолированные компоненты; затем churn без ожидания сходимости. */ for (unsigned mask = 0; mask < 64; mask++) { change(mask); settle(); verify(); } for (int i = 0; i < 100; i++) { change(next_random() >> 26); for (unsigned n = next_random() % 5; n; n--) step(); } settle(); verify(); change(0); settle(); verify(); for (int a = 0; a < N; a++) for (int b = 0; b < N; b++) if (a != b) node_conn_direct_close(owners[a][b]); for (int a = 0; a < N; a++) utun_instance_destroy(nodes[a]); uasync_poll(ua, 0); uasync_destroy(ua, 0); DEBUG_INFO(DEBUG_CATEGORY_BGP, "BGP paths: triangle, partitions, all four-node graphs and churn passed packets=%u", delivered); return 0; }