21 changed files with 593 additions and 333 deletions
@ -0,0 +1,65 @@
|
||||
#ifndef TEST_CONNECTION_LOSS_H |
||||
#define TEST_CONNECTION_LOSS_H |
||||
|
||||
#include "etcp_api.h" |
||||
#include "etcp_connections.h" |
||||
#include "utun_instance.h" |
||||
#include "../lib/debug_config.h" |
||||
|
||||
/* Разрыв пары узлов охватывает и новые линки из свежего NODEINFO.
|
||||
* only/spared позволяют отдельно проверить потерю одного линка и работу |
||||
* единственного оставшегося линка. Контекст живёт до loss_destroy(). */ |
||||
struct test_connection_loss { |
||||
struct UTUN_INSTANCE* instance; |
||||
uint64_t peer_id; |
||||
struct ETCP_LINK* only; |
||||
struct ETCP_LINK* spared; |
||||
int blocked; |
||||
unsigned links; |
||||
unsigned long long dropped; |
||||
}; |
||||
|
||||
static ssize_t test_loss_send(socket_t fd, const void* buf, size_t len, const struct sockaddr* addr, |
||||
socklen_t addr_len, struct ETCP_LINK* link, void* arg) { |
||||
struct test_connection_loss* loss = arg; |
||||
if (loss->blocked && (!loss->only || loss->only == link) && loss->spared != link) { |
||||
loss->dropped++; |
||||
return (ssize_t)len; |
||||
} |
||||
return socket_sendto(fd, buf, len, addr, addr_len); |
||||
} |
||||
|
||||
static void test_loss_link(struct ETCP_CONN* conn, struct ETCP_LINK* link, int old_state, int old_status, void* arg) { |
||||
(void)old_state; (void)old_status; |
||||
struct test_connection_loss* loss = arg; |
||||
if (conn->peer_node_id != loss->peer_id || link->send_hook == test_loss_send) return; |
||||
link->send_hook = test_loss_send; link->send_hook_ctx = loss; loss->links++; |
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "test partition: peer=%016llx attached link=%p total=%u blocked=%d", |
||||
(unsigned long long)loss->peer_id, (void*)link, loss->links, loss->blocked); |
||||
} |
||||
|
||||
static void test_loss_init(struct test_connection_loss* loss, struct UTUN_INSTANCE* instance, uint64_t peer_id) { |
||||
loss->instance = instance; loss->peer_id = peer_id; |
||||
etcp_add_link_status_cbk(instance, test_loss_link, loss); |
||||
struct ETCP_CONN* conn = instance_find_conn(instance, peer_id); |
||||
for (struct ETCP_LINK* link = conn ? conn->links : NULL; link; link = link->next) |
||||
test_loss_link(conn, link, 0, 0, loss); |
||||
} |
||||
|
||||
static void test_loss_set(struct test_connection_loss* loss, int blocked, struct ETCP_LINK* only, struct ETCP_LINK* spared) { |
||||
loss->blocked = blocked; loss->only = only; loss->spared = spared; |
||||
DEBUG_INFO(DEBUG_CATEGORY_BGP, "test partition: peer=%016llx blocked=%d only=%p spared=%p links=%u dropped=%llu", |
||||
(unsigned long long)loss->peer_id, blocked, (void*)only, (void*)spared, loss->links, loss->dropped); |
||||
} |
||||
|
||||
static void test_loss_destroy(struct test_connection_loss* loss) { |
||||
if (!loss->instance) return; |
||||
etcp_remove_link_status_cbk(loss->instance, test_loss_link, loss); |
||||
struct ETCP_CONN* conn = instance_find_conn(loss->instance, loss->peer_id); |
||||
for (struct ETCP_LINK* link = conn ? conn->links : NULL; link; link = link->next) { |
||||
if (link->send_hook_ctx != loss) continue; |
||||
link->send_hook = NULL; link->send_hook_ctx = NULL; |
||||
} |
||||
loss->instance = NULL; |
||||
} |
||||
#endif |
||||
@ -0,0 +1,96 @@
|
||||
#include <assert.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include "utun_instance.h" |
||||
#include "config_parser.h" |
||||
#include "transport_layer/etcp.h" |
||||
#include "transport_layer/etcp_connections.h" |
||||
#include "transport_layer/node_conn_direct.h" |
||||
#include "../lib/u_async.h" |
||||
#include "../lib/debug_config.h" |
||||
|
||||
static int links(struct ETCP_CONN* conn) { |
||||
int count = 0; |
||||
for (struct ETCP_LINK* link = conn->links; link; link = link->next) count++; |
||||
return count; |
||||
} |
||||
|
||||
static void endpoint(struct CFG_CLIENT_LINK* link, struct CFG_SERVER* local, int port) { |
||||
memset(link, 0, sizeof(*link)); link->local_srv = local; |
||||
struct sockaddr_in* addr = (struct sockaddr_in*)&link->remote_addr; |
||||
addr->sin_family = AF_INET; addr->sin_addr.s_addr = htonl(0x7f000001); addr->sin_port = htons((uint16_t)port); |
||||
} |
||||
|
||||
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" |
||||
"[server: tcp]\naddr=127.0.0.1:0\ntype=public\ntransport=tcp\n"); |
||||
assert(inst && utun_instance_init(inst) >= 0); |
||||
struct CFG_SERVER *udp = NULL, *tcp = NULL; |
||||
for (struct CFG_SERVER* s = inst->config->servers; s; s = s->next) { |
||||
if (!strcmp(s->name, "udp")) udp = s; |
||||
if (!strcmp(s->name, "tcp")) tcp = s; |
||||
} |
||||
assert(udp && tcp); |
||||
struct CFG_CLIENT client = { .name = "peer" }; |
||||
struct SC_MYKEYS peer; assert(sc_generate_keypair(&peer) == SC_OK); |
||||
for (int i = 0; i < 32; i++) snprintf(client.peer_public_key_hex + i * 2, 3, "%02x", peer.public_key[i]); |
||||
struct CFG_CLIENT_LINK first, second, third; |
||||
endpoint(&first, udp, 9); endpoint(&second, udp, 10); endpoint(&third, tcp, 11); |
||||
first.next = &second; second.next = &third; client.links = &first; |
||||
assert(init_client_connections(inst, &client) == 0); |
||||
assert(inst->config_conn_handles && !inst->config_conn_handles->next); |
||||
struct ETCP_CONN* conn = node_conn_direct_get_conn(inst->config_conn_handles->handle); |
||||
assert(conn && links(conn) == 3 && !conn->fin_wait); |
||||
struct NODE_CONN_DIRECT* chat = NULL; |
||||
assert(node_conn_direct_open(inst, conn->peer_node_id, NULL, NULL, &chat, NULL) == NCD_REUSED); |
||||
assert(init_client_connections(inst, &client) == 0); |
||||
assert(node_conn_direct_get_conn(inst->config_conn_handles->handle) == conn && links(conn) == 3 && !conn->fin_wait); |
||||
|
||||
/* Ошибка нового набора не освобождает предыдущие handles. */ |
||||
struct CFG_CLIENT invalid = { .name = "invalid", .peer_public_key_hex = "not-a-key" }; |
||||
client.next = &invalid; |
||||
struct CONFIG_CONN_HANDLE* previous = inst->config_conn_handles; |
||||
assert(init_client_connections(inst, &client) < 0 && inst->config_conn_handles == previous); |
||||
assert(node_conn_direct_get_conn(chat) == conn && !conn->fin_wait); |
||||
client.next = NULL; |
||||
|
||||
/* REALITY идёт по тому же NCD пути; полное SNI не обрезается. */ |
||||
client.links = &third; client.reality_enabled = 1; |
||||
reality_client_config_set_defaults(&client.reality); |
||||
memcpy(client.reality.server_static_pubkey, peer.public_key, 32); |
||||
memset(client.reality.server_name, 'a', 180); client.reality.server_name[180] = 0; |
||||
assert(init_client_connections(inst, &client) == 0); |
||||
assert(links(conn) == 3); |
||||
struct ETCP_LINK* real = NULL; |
||||
for (struct ETCP_LINK* link = conn->links; link; link = link->next) if (link->is_tcp) real = link; |
||||
assert(real && real->reality_set && !strcmp(real->reality.server_name, client.reality.server_name)); |
||||
assert(init_client_connections(inst, &client) == 0 && links(conn) == 3); |
||||
assert(init_client_connections(inst, NULL) == 0 && !inst->config_conn_handles); |
||||
assert(node_conn_direct_get_conn(chat) == conn && !conn->fin_wait); |
||||
node_conn_direct_force_close(chat); |
||||
uasync_poll(ua, 0); |
||||
/* Клиентский TCP/REALITY не требует локального TCP listen-сокета. */ |
||||
struct ETCP_SOCKET* sock = inst->etcp_sockets; |
||||
while (sock) { |
||||
struct ETCP_SOCKET* next = sock->next; |
||||
if (sock->is_tcp) etcp_socket_remove(sock); |
||||
sock = next; |
||||
} |
||||
third.local_srv = NULL; |
||||
assert(init_client_connections(inst, &client) == 0); |
||||
conn = node_conn_direct_get_conn(inst->config_conn_handles->handle); |
||||
assert(conn && links(conn) == 1 && conn->links->is_tcp && !conn->links->conn); |
||||
assert(conn->links->reality_set && !strcmp(conn->links->reality.server_name, client.reality.server_name)); |
||||
assert(init_client_connections(inst, NULL) == 0); |
||||
inst->running = 0; utun_instance_destroy(inst); |
||||
uasync_poll(ua, 0); |
||||
uasync_destroy(ua, 0); |
||||
return 0; |
||||
} |
||||
Loading…
Reference in new issue