You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

267 lines
13 KiB

// Реальные STCP/REALITY-линки: встречное подключение и рестарт пира без reset выжившего узла.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "etcp.h"
#include "etcp_api.h"
#include "etcp_connections.h"
#include "etcp_session.h"
#include "node_conn_direct.h"
#include "topo_group.h"
#include "topo_node.h"
#include "utun_instance.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#include "test_clock.h"
#define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "FAIL %s:%d: %s", __func__, __LINE__, #x); exit(1); } } while (0)
#define TEST_CMD 253
#define MESSAGE_COUNT 16
#define FULL_MASK ((1u << MESSAGE_COUNT) - 1)
struct endpoint {
struct UTUN_INSTANCE* inst;
struct NODE_CONN_DIRECT* owner;
struct ETCP_CONN* conn;
uint64_t id;
uint16_t port;
unsigned received, reinit, deleted;
};
struct message { uint8_t cmd, from, phase, seq, body[64]; };
static struct endpoint endpoints[2];
static struct UASYNC* ua;
static unsigned phase;
static int failed;
static uint64_t stream_packets, session_packets;
static const char* public_keys[2] = {
"b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01",
"ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a"
};
static const char* private_keys[2] = {
"704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f",
"38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68"
};
#ifdef __linux__
// Считаем реальные отправки потока/control: тест ловит шторм независимо от скорости CPU.
int __real_etcp_encrypt_send(struct ETCP_DGRAM* packet);
int __wrap_etcp_encrypt_send(struct ETCP_DGRAM* packet) {
if (packet && packet->data_len) {
if (packet->data[0] <= ETCP_SECTION_ACK) stream_packets++;
if (packet->data[0] >= ETCP_SESSION_HELLO && packet->data[0] <= ETCP_SESSION_CONFIRM) session_packets++;
}
return __real_etcp_encrypt_send(packet);
}
#endif
static void fill_message(struct message* message, int from, unsigned seq) {
message->cmd = TEST_CMD; message->from = from; message->phase = phase; message->seq = seq;
for (unsigned i = 0; i < sizeof(message->body); i++) message->body[i] = (phase * 17 + seq * 3 + i) ^ from;
}
static void receive_message(struct ETCP_CONN* conn, struct ll_entry* entry) {
int to = conn->instance == endpoints[0].inst ? 0 : 1;
struct message expected;
int valid = entry->len == sizeof(expected) && entry->dgram;
const struct message* received = valid ? (const struct message*)entry->dgram : NULL;
if (valid) valid = received->phase == phase && received->from == 1 - to && received->seq < MESSAGE_COUNT;
if (valid) {
fill_message(&expected, 1 - to, received->seq);
valid = !memcmp(received, &expected, sizeof(expected)) && !(endpoints[to].received & (1u << received->seq));
}
if (valid) endpoints[to].received |= 1u << received->seq;
else { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "Invalid/duplicate message: to=%d phase=%u bytes=%u", to, phase, entry->len); failed = 1; }
queue_dgram_free(entry); queue_entry_free(entry);
}
static void connection_event(struct ETCP_CONN* conn, int event, void* arg) {
(void)conn;
struct endpoint* endpoint = arg;
if (event == ETCP_CBK_EVENT_REINIT) endpoint->reinit++;
if (event == ETCP_CBK_EVENT_DELETE) endpoint->deleted++;
}
static void connect_done(void* arg, struct ETCP_CONN* conn, int type) {
struct endpoint* endpoint = arg;
if (!conn) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "Connection failed: local=%016llx", (unsigned long long)endpoint->id); failed = 1; }
else if (type & ETCP_CONNECT_EARLY) endpoint->conn = conn;
}
static void start_endpoint(int i, int reality) {
struct endpoint* endpoint = &endpoints[i];
struct SC_MYKEYS keys;
CHECK(sc_init_local_keys(&keys, public_keys[i], private_keys[i]) == SC_OK);
uint64_t id = sc_derive_node_id_from_pubkey(keys.public_key);
char config[2048], reality_config[512] = "";
if (reality) snprintf(reality_config, sizeof(reality_config),
"[reality]\nenabled=1\nprivate_key=%s\nshort_ids=0102030405060708\nserver_name=localhost.test\n"
"dest=127.0.0.1:1\nfingerprint=openssl\n", private_keys[i]);
snprintf(config, sizeof(config),
"[global]\nmy_node_id=0x%016llx\nmy_node_name=transport-%d\nmy_public_key=%s\nmy_private_key=%s\n"
"[server:stcp]\naddr=127.0.0.1:%u\ntype=public\ntransport=tcp\nreality=%d\n%s",
(unsigned long long)id, i, public_keys[i], private_keys[i], endpoint->port, reality, reality_config);
endpoint->inst = utun_instance_create_from_str(ua, config); CHECK(endpoint->inst);
CHECK(utun_instance_init(endpoint->inst) >= 0);
CHECK(etcp_bind(endpoint->inst, TEST_CMD, receive_message) == 0);
endpoint->id = endpoint->inst->node_id;
struct ETCP_SOCKET* socket = endpoint->inst->etcp_sockets;
while (socket && !socket->is_tcp) socket = socket->next;
CHECK(socket && socket->local_addr.ss_family == AF_INET);
endpoint->port = ntohs(((struct sockaddr_in*)&socket->local_addr)->sin_port); CHECK(endpoint->port);
endpoint->conn = NULL; endpoint->owner = NULL; endpoint->received = endpoint->reinit = endpoint->deleted = 0;
}
static void stop_endpoint(int i) {
struct endpoint* endpoint = &endpoints[i];
if (endpoint->owner) { node_conn_direct_close(endpoint->owner); endpoint->owner = NULL; }
endpoint->inst->running = 0;
utun_instance_destroy(endpoint->inst); endpoint->inst = NULL; endpoint->conn = NULL;
}
static struct TOPO_NODE* import_peer(int to) {
struct UTUN_INSTANCE* from = endpoints[1-to].inst;
struct TOPO_GROUP_NODE* local = topo_groups_get_default(from->topo_groups)->local_node;
struct TOPO_NODE* info = topo_node_registry_find(from->topo_groups, from->node_id); CHECK(info);
uint8_t wire[4096]; int len = topo_node_serialize(info, local, 0, 0, wire, sizeof(wire), 0); CHECK(len > 0);
struct TOPO_NODE* imported = NULL;
CHECK(topo_node_deserialize(topo_groups_get_default(endpoints[to].inst->topo_groups), wire, len,
&imported, NULL, NULL, NULL, NULL) == 0);
CHECK(topo_node_registry_store(endpoints[to].inst->topo_groups, imported));
return imported;
}
static int pair_ready(void) {
for (int i = 0; i < 2; i++) {
endpoints[i].conn = instance_find_conn(endpoints[i].inst, endpoints[1-i].id);
struct ETCP_CONN* conn = endpoints[i].conn;
if (!conn || !conn->initialized || !conn->links_up || conn->session_required || conn->reinit_pending) return 0;
}
return endpoints[0].conn->peer_reset_id == endpoints[1].conn->reset_id &&
endpoints[1].conn->peer_reset_id == endpoints[0].conn->reset_id;
}
static void wait_pair(void) {
uint64_t end = get_time_tb() + 50000;
while (!failed && get_time_tb() < end) {
test_clock_poll(ua, 100);
if (pair_ready()) return;
}
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "Session recovery timed out: phase=%u stream=%llu control=%llu", phase,
(unsigned long long)stream_packets, (unsigned long long)session_packets);
CHECK(0);
}
static void check_unique(int i) {
unsigned count = 0;
for (struct ll_entry* e = endpoints[i].inst->connections->head; e; e = e->next)
if (((struct conn_queue_entry*)e->data)->peer_node_id == endpoints[1-i].id) count++;
CHECK(count == 1);
}
static void hold_connection(int i) {
struct endpoint* endpoint = &endpoints[i];
struct TOPO_NODE* peer = topo_node_registry_find(endpoint->inst->topo_groups, endpoints[1-i].id); CHECK(peer);
CHECK(node_conn_direct_open_node(endpoint->inst, peer->node_id, NULL, NULL, &endpoint->owner, peer, NULL) == NCD_REUSED);
CHECK(node_conn_direct_get_conn(endpoint->owner) == endpoint->conn);
etcp_conn_add_cbk(endpoint->conn, connection_event, endpoint, ETCP_CBK_EVENT_REINIT | ETCP_CBK_EVENT_DELETE);
}
static void exchange_messages(void) {
for (int i = 0; i < 2; i++) endpoints[i].received = 0;
for (int i = 0; i < 2; i++) for (unsigned seq = 0; seq < MESSAGE_COUNT; seq++) {
struct ll_entry* entry = ll_alloc_lldgram(sizeof(struct message)); CHECK(entry);
fill_message((struct message*)entry->dgram, i, seq); entry->len = sizeof(struct message);
CHECK(etcp_send(endpoints[i].conn, entry) == 0);
}
uint64_t end = get_time_tb() + 30000;
while (!failed && get_time_tb() < end) {
test_clock_poll(ua, 100);
if (endpoints[0].received != FULL_MASK || endpoints[1].received != FULL_MASK) continue;
int drained = 1;
for (int i = 0; i < 2; i++) {
struct ETCP_CONN* conn = endpoints[i].conn;
if (conn->input_wait_ack->count || conn->input_send_q->count || conn->input_queue->count || conn->send_input_q->count) drained = 0;
}
if (drained) break;
}
CHECK(!failed && endpoints[0].received == FULL_MASK && endpoints[1].received == FULL_MASK);
for (int i = 0; i < 2; i++) CHECK(!endpoints[i].conn->input_wait_ack->count && !endpoints[i].conn->input_send_q->count);
}
static void run_case(int reality, int simultaneous, int restarted) {
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "Transport case: reality=%d simultaneous=%d restart=%d", reality, simultaneous, restarted);
memset(endpoints, 0, sizeof(endpoints)); phase = 0; failed = 0; stream_packets = session_packets = 0;
ua = uasync_create(); CHECK(ua);
start_endpoint(0, reality); start_endpoint(1, reality);
struct TOPO_NODE* peers[2] = {import_peer(0), import_peer(1)};
for (int i = 0; i <= simultaneous; i++) {
struct TOPO_GROUP_NODE node = {0}; node.node_id = peers[i]->node_id;
CHECK(etcp_connect(endpoints[i].inst, &node, connect_done, &endpoints[i], ETCP_CONNECT_EARLY | ETCP_CONNECT_LATE) == 0);
}
wait_pair();
for (int i = 0; i < 2; i++) { check_unique(i); hold_connection(i); }
// Повторные владельцы и обновление адресов не создают дополнительные линки.
unsigned links = 0; for (struct ETCP_LINK* l = endpoints[0].conn->links; l; l = l->next) links++;
struct NODE_CONN_DIRECT* extra = NULL;
CHECK(node_conn_direct_open_node(endpoints[0].inst, peers[0]->node_id, NULL, NULL, &extra, peers[0], NULL) == NCD_REUSED);
CHECK(node_conn_direct_update_node(endpoints[0].inst, peers[0]->node_id) == 0);
unsigned updated = 0; for (struct ETCP_LINK* l = endpoints[0].conn->links; l; l = l->next) updated++;
CHECK(updated == links); node_conn_direct_close(extra);
exchange_messages();
if (restarted < 2) {
int survivor = 1 - restarted;
struct ETCP_CONN* retained = endpoints[survivor].conn;
CHECK(etcp_conn_ref_take(retained) == 0);
uint64_t local_epoch = retained->reset_id, peer_epoch = retained->peer_reset_id;
unsigned before = endpoints[survivor].reinit;
uint64_t packets_before = stream_packets;
stop_endpoint(restarted); start_endpoint(restarted, reality);
phase++; wait_pair();
CHECK(!endpoints[survivor].deleted && endpoints[survivor].conn == retained);
CHECK(retained->reset_id == local_epoch && retained->peer_reset_id != peer_epoch);
CHECK(endpoints[survivor].reinit == before + 1);
CHECK(node_conn_direct_get_conn(endpoints[survivor].owner) == retained);
#ifdef __linux__
CHECK(stream_packets - packets_before < 256);
#endif
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "Recovery complete: stream=%llu control=%llu REINIT=1 survivor_epoch=%016llx",
(unsigned long long)(stream_packets - packets_before), (unsigned long long)session_packets,
(unsigned long long)local_epoch);
etcp_conn_ref_free(retained);
check_unique(0); check_unique(1); exchange_messages();
} else {
uint64_t old_epochs[2] = {endpoints[0].conn->reset_id, endpoints[1].conn->reset_id};
stop_endpoint(0); stop_endpoint(1); start_endpoint(0, reality); start_endpoint(1, reality);
peers[0] = import_peer(0); peers[1] = import_peer(1);
for (int i = 0; i < 2; i++) {
struct TOPO_GROUP_NODE node = {0}; node.node_id = peers[i]->node_id;
CHECK(etcp_connect(endpoints[i].inst, &node, connect_done, &endpoints[i], ETCP_CONNECT_EARLY) == 0);
}
phase++; wait_pair();
for (int i = 0; i < 2; i++) { CHECK(endpoints[i].conn->reset_id != old_epochs[i]); check_unique(i); }
exchange_messages();
}
stop_endpoint(0); stop_endpoint(1);
uasync_poll(ua, 0);
CHECK(ua->timer_alloc_count == ua->timer_free_count && ua->socket_alloc_count == ua->socket_free_count);
uasync_destroy(ua, 0);
printf("PASS transport reality=%d simultaneous=%d restart=%d: epochs, bidirectional delivery and resource balance\n",
reality, simultaneous, restarted);
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
if (getenv("UTUN_TEST_DEBUG")) {
debug_set_category_level(DEBUG_CATEGORY_ETCP, DEBUG_LEVEL_DEBUG);
debug_set_category_level(DEBUG_CATEGORY_GENERAL, DEBUG_LEVEL_INFO);
}
test_clock_init(100); utun_instance_set_tun_init_enabled(0);
for (int reality = 0; reality < 2; reality++) {
for (int simultaneous = 0; simultaneous < 2; simultaneous++)
for (int restarted = 0; restarted < 2; restarted++) run_case(reality, simultaneous, restarted);
run_case(reality, 1, 2);
}
test_clock_finish();
return 0;
}