Reconcile BGP paths and advertise selected routes per neighbor
This commit is contained in:
1 parent
546e0c185d
commit
b76e6553bc
9 files changed
+381
-171
No files matched your search
@@ -55,6 +55,7 @@ check_PROGRAMS = \
|
||||
test_socks_client \
|
||||
test_bgp_route_exchange \
|
||||
test_bgp_triangle \
|
||||
test_bgp_paths \
|
||||
test_broadcast \
|
||||
test_conn_mgr \
|
||||
test_conn_mgr_phases \
|
||||
@@ -401,6 +402,10 @@ test_group_exchange_SOURCES = test_group_exchange.c
|
||||
test_group_exchange_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
|
||||
test_group_exchange_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
|
||||
test_bgp_paths_SOURCES = test_bgp_paths.c
|
||||
test_bgp_paths_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
|
||||
test_bgp_paths_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
|
||||
test_group_recovery_SOURCES = test_group_recovery.c
|
||||
test_group_recovery_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib
|
||||
test_group_recovery_LDADD = $(LIBUTUN) $(CRYPTO_LIBS) $(COMMON_LIBS)
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/* Управляемая сеть: настоящий BGP receiver/sender, подписи и очереди, без UDP.
|
||||
* Доставка по каждому ребру сохраняет FIFO, порядок между рёбрами меняется. */
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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();
|
||||
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;
|
||||
}
|
||||
@@ -131,7 +131,7 @@ int main(void) {
|
||||
uint64_t relay_hops[] = { node.node_id, relay.peer_node_id };
|
||||
struct TOPO_GROUP_NODE* routed = topo_node_find_by_id(a, node.node_id);
|
||||
assert(topo_group_add_path(routed, &relay, relay_hops, 2, 10) == 0);
|
||||
struct TOPOMSG_WITHDRAW_PKT alternate_wd = { .node_id = node.node_id, .wd_source = relay.peer_node_id };
|
||||
struct TOPOMSG_WITHDRAW_PKT alternate_wd = { .node_id = node.node_id };
|
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "regression: withdraw one of two paths node=%016llx paths=%d",
|
||||
(unsigned long long)node.node_id, queue_entry_count(routed->paths));
|
||||
assert(topo_group_process_withdraw(a, &relay, (const uint8_t*)&alternate_wd, sizeof(alternate_wd)) == 0);
|
||||
@@ -164,7 +164,7 @@ int main(void) {
|
||||
assert(topo_node_find_by_id(a, node.node_id)->last_timestamp == 2);
|
||||
struct TOPOMSG_WITHDRAW_PKT wd = { .h = { .cmd = ETCP_ID_TOPO_ENTRY, .subcmd = TOPO_SUBCMD_WITHDRAW,
|
||||
.group_id = a->group_id, .src_epoch = a->group_id + 1000, .dst_epoch = a_id },
|
||||
.node_id = node.node_id, .wd_source = node.node_id };
|
||||
.node_id = node.node_id };
|
||||
packet(conn, &wd, sizeof(wd));
|
||||
assert(topo_node_find_by_id(a, node.node_id));
|
||||
wd.h.dst_epoch = peer(a)->local_epoch; packet(conn, &wd, sizeof(wd));
|
||||
|
||||
@@ -245,7 +245,8 @@ static void sender_backpressure(void) {
|
||||
if (e) { queue_dgram_free(e); queue_entry_free(e); }
|
||||
queue_resume_callback(f.conns[0]->send_input_q);
|
||||
}
|
||||
assert(complete == 1 && local == 2 && remote == 1 && peer(&f, 0)->table_sent);
|
||||
/* 50 одинаковых обновлений после снимка не повторяют уже переданный анонс. */
|
||||
assert(complete == 1 && local == 1 && remote == 1 && peer(&f, 0)->table_sent);
|
||||
/* Cancel a blocked old generation; only the new JOIN may be sent afterwards. */
|
||||
etcp_fire_conn_status(f.conns[0], ETCP_CONN_STATUS_REINIT); accept_join(&f, 0);
|
||||
table(&f, 0, TOPO_SUBCMD_REQUEST_TABLE, peer(&f, 0)->local_epoch);
|
||||
|
||||
@@ -41,6 +41,7 @@ static void test_bgp(struct UTUN_INSTANCE* signer) {
|
||||
struct ETCP_CONN from = { .instance = &receiver, .peer_node_id = 7 };
|
||||
group.nodes = queue_new(receiver.ua, 16, 0, 8, "snapshot_bgp");
|
||||
struct TOPO_NODE* ni = record(signer, INT64_MAX - 7, "BGP", 1);
|
||||
from.peer_node_id = ni->node_id; from.links_up = 1;
|
||||
struct TOPO_GROUP_NODE nq = {0};
|
||||
uint8_t packet[4096] = {0};
|
||||
int len = topo_node_serialize(ni, &nq, group.group_id, 0, packet + sizeof(struct TOPOMSG_HEADER), sizeof(packet) - sizeof(struct TOPOMSG_HEADER), 0);
|
||||
|
||||
Reference in new issue
Block a user