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.
 
 
 
 
 
 

259 lines
14 KiB

/* Padding: реальные таймеры/UDP, проверка расшифрованных пакетов и независимости владельцев. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "etcp.h"
#include "etcp_keepalive.h"
#include "utun_instance.h"
#include "../lib/debug_config.h"
#include "../lib/mem.h"
#define CHECK(x) do { if (!(x)) { fprintf(stderr, "FAIL %s:%d: %s\n", __func__, __LINE__, #x); exit(1); } } while (0)
static struct { struct ETCP_PADDING* padding; timeout_callback_t callback; void* timer; int delay; } timers[128];
static size_t timer_count;
static int fail_timer;
static struct UTUN_INSTANCE *sender, *receiver;
static sc_context_t wire_crypto;
static int capture_ping, ping_requests, ping_responses, ping_done, expected_req_padding, expected_resp_padding, wire_calls;
static int keepalive_packets, expected_keepalive_padding;
static const uint8_t payload[] = {0, 0xff, 0x12, 0, 0x34, 0x56};
void* __real_uasync_set_timeout(struct UASYNC*, int, void*, timeout_callback_t, const char*);
void* __wrap_uasync_set_timeout(struct UASYNC* ua, int delay, void* arg, timeout_callback_t cb, const char* name) {
int padding = name && strcmp(name, "etcp_padding") == 0;
if (padding && fail_timer) return NULL;
void* timer = __real_uasync_set_timeout(ua, delay, arg, cb, name);
if (padding && timer) {
CHECK(timer_count < sizeof(timers) / sizeof(timers[0]));
CHECK(delay >= 500000 && delay <= 3000000);
timers[timer_count].padding = arg; timers[timer_count].callback = cb;
timers[timer_count].timer = timer; timers[timer_count++].delay = delay;
}
return timer;
}
static void check_zeroes(const uint8_t* data, size_t size) {
for (size_t i = 0; i < size; i++) CHECK(data[i] == 0);
}
ssize_t __real_socket_sendto(socket_t, const void*, size_t, const struct sockaddr*, socklen_t);
ssize_t __wrap_socket_sendto(socket_t sock, const void* buf, size_t len, const struct sockaddr* dest, socklen_t dest_len) {
wire_calls++;
if (capture_ping) {
CHECK(len <= PACKET_DATA_MAX_MTU - IP_UDP_OVERHEAD_V4);
CHECK(len >= 96);
const uint8_t* bytes = buf;
uint8_t plain[PACKET_DATA_SIZE]; size_t plain_len = 0;
CHECK(sc_decrypt(&wire_crypto, bytes, len - SC_PUBKEY_ENC_SIZE, plain, &plain_len) == SC_OK);
int code = plain[3]; CHECK(code == ETCP_PING || code == ETCP_PONG);
size_t ulen = ((size_t)plain[21] << 8) | plain[22];
CHECK(ulen == sizeof(payload) && memcmp(plain + 3 + 20, payload, ulen) == 0);
size_t padding = plain_len - 3 - 20 - ulen;
CHECK(padding == (size_t)(code == ETCP_PING ? expected_req_padding : expected_resp_padding));
check_zeroes(plain + 3 + 20 + ulen, padding);
if (code == ETCP_PING) ping_requests++; else ping_responses++;
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "wire PING/PONG code=%u payload=%zu padding=%zu bytes=%zu", code, ulen, padding, len);
}
return __real_socket_sendto(sock, buf, len, dest, dest_len);
}
static ssize_t keepalive_hook(socket_t fd, const void* buf, size_t len, const struct sockaddr* addr, socklen_t alen,
struct ETCP_LINK* link, void* arg) {
(void)fd; (void)addr; (void)alen; (void)arg;
uint8_t plain[PACKET_DATA_SIZE]; size_t plain_len = 0;
CHECK(sc_decrypt(&wire_crypto, buf, len, plain, &plain_len) == SC_OK);
CHECK(plain[3] == ETCP_KEEPALIVE && plain[4] == 0);
CHECK(plain_len == 3 + 4 + (size_t)expected_keepalive_padding);
check_zeroes(plain + 3 + 4, expected_keepalive_padding);
struct ETCP_DGRAM* pkt = memory_pool_alloc(sender->pkt_pool); CHECK(pkt);
memcpy(pkt->data, plain + 3, plain_len - 3); pkt->data_len = plain_len - 3;
uint32_t before = link->keepalive_recv_count;
CHECK(etcp_keepalive_on_recv(sender->etcp_sockets, pkt, link, plain_len) == 1);
CHECK(link->keepalive_recv_count == before + 1);
keepalive_packets++;
return len;
}
static void force_refresh(struct ETCP_PADDING* padding) {
size_t i = timer_count;
while (i && timers[i - 1].timer != padding->timer) i--;
CHECK(i > 0 && timers[i - 1].padding == padding);
CHECK(uasync_cancel_timeout(padding->ua, padding->timer) == 0);
timers[i - 1].callback(padding);
CHECK(padding->timer && padding->min <= 30);
CHECK(padding->max >= padding->min + 32 && padding->max <= padding->min + 128);
uint64_t now = get_time_tb();
CHECK(padding->next_refresh_tb > now && padding->next_refresh_tb <= now + 3000000);
}
static struct UTUN_INSTANCE* make_instance(struct UASYNC* ua, const char* private_key, const char* public_key) {
char config[1024];
snprintf(config, sizeof(config), "[global]\nmy_private_key=%s\nmy_public_key=%s\nauto_sockets=no\n"
"[server: padding]\naddr=127.0.0.1:0\ntype=public\n", private_key, public_key);
struct UTUN_INSTANCE* inst = utun_instance_create_from_str(ua, config); CHECK(inst);
CHECK(inst->padding.timer && inst->padding.min <= 30 && inst->padding.max <= ETCP_PADDING_MAX);
return inst;
}
static struct ETCP_CONN* make_connection(struct UTUN_INSTANCE* inst, const struct SC_MYKEYS* peer) {
struct ETCP_CONN* conn = etcp_connection_create(inst, "padding-test"); CHECK(conn);
conn->peer_node_id = sc_derive_node_id_from_pubkey(peer->public_key);
etcp_conn_queue_set_ready(conn);
CHECK(sc_init_ctx(&conn->crypto_ctx, &inst->my_keys) == SC_OK);
CHECK(sc_set_peer_public_key(&conn->crypto_ctx, peer->public_key, SC_PEER_PUBKEY_BIN) == SC_OK);
CHECK(conn->padding.timer && conn->padding.timer != inst->padding.timer);
return conn;
}
static void ping_cb(int success, uint16_t rtt, void* arg, uint64_t nonce, const uint8_t* data, size_t size) {
(void)rtt; (void)arg; (void)nonce;
CHECK(success && size == sizeof(payload) && memcmp(data, payload, size) == 0);
ping_done = 1;
}
static void ping_case(int req_padding, int resp_padding) {
expected_req_padding = req_padding; expected_resp_padding = resp_padding;
ping_requests = ping_responses = ping_done = 0;
struct sockaddr_storage addr; socklen_t size = sizeof(addr);
CHECK(getsockname(receiver->etcp_sockets->fd, (struct sockaddr*)&addr, &size) == 0);
capture_ping = 1;
CHECK(etcp_send_ping(sender, receiver->my_keys.public_key, &addr, 500, ping_cb, NULL, payload, sizeof(payload)) == 0);
for (int i = 0; !ping_done && i < 1000; i++) uasync_poll(sender->ua, 10);
capture_ping = 0;
CHECK(ping_done && ping_requests == 1 && ping_responses == 1 && !sender->pending_pings);
}
/* Слишком длинный user_data и лишний padding отбрасываются до эхо/доставки callback. */
static void malformed_ping_cases(void) {
for (int code = ETCP_PING; code <= ETCP_PONG; code++) {
for (int extra = 0; extra <= 1; extra++) {
struct sockaddr_storage addr; socklen_t alen = sizeof(addr);
CHECK(getsockname(receiver->etcp_sockets->fd, (struct sockaddr*)&addr, &alen) == 0);
uint8_t plain[3 + 20 + ETCP_PADDING_MAX + 1] = {0}, packet[512];
plain[3] = code;
plain[22] = extra ? 0 : 1;
struct UTUN_INSTANCE *source = sender, *target = receiver;
if (code == ETCP_PONG) {
/* Битый ответ имеет nonce настоящего ожидающего пинга и приходит раньше корректного ответа. */
ping_done = 0;
CHECK(etcp_send_ping(sender, receiver->my_keys.public_key, &addr, 500,
ping_cb, NULL, payload, sizeof(payload)) == 0);
CHECK(sender->pending_pings);
uint64_t nonce = sender->pending_pings->nonce;
for (int i = 0; i < 8; i++) plain[3 + 10 + i] = nonce >> (56 - i * 8);
source = receiver; target = sender;
alen = sizeof(addr);
CHECK(getsockname(target->etcp_sockets->fd, (struct sockaddr*)&addr, &alen) == 0);
}
size_t enc_len = 0;
size_t plain_len = extra ? sizeof(plain) : 23;
CHECK(sc_encrypt(&wire_crypto, plain, plain_len, packet, &enc_len) == SC_OK);
memset(packet + enc_len, 0, SC_PUBKEY_ENC_SALT_SIZE);
CHECK(sc_obfuscate_pubkey(packet + enc_len, target->my_keys.public_key, source->my_keys.public_key,
packet + enc_len + SC_PUBKEY_ENC_SALT_SIZE) == SC_OK);
int before = wire_calls;
size_t len = enc_len + SC_PUBKEY_ENC_SIZE;
CHECK(__real_socket_sendto(source->etcp_sockets->fd, packet, len, (const struct sockaddr*)&addr, alen) == (ssize_t)len);
if (code == ETCP_PING) {
for (int i = 0; i < 10; i++) uasync_poll(sender->ua, 10);
CHECK(wire_calls == before);
} else {
for (int i = 0; !ping_done && i < 1000; i++) uasync_poll(sender->ua, 10);
CHECK(ping_done && !sender->pending_pings && wire_calls == before + 1);
}
}
}
puts("PASS malformed PING/PONG: invalid user length and oversized padding rejected");
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN);
debug_set_category_level(DEBUG_CATEGORY_GENERAL, DEBUG_LEVEL_INFO);
debug_set_category_level(DEBUG_CATEGORY_ETCP, DEBUG_LEVEL_DEBUG);
debug_set_category_level(DEBUG_CATEGORY_KEEPALIVE, DEBUG_LEVEL_DEBUG);
utun_instance_set_tun_init_enabled(0); utun_instance_set_topo_group_enabled(0);
struct UASYNC* ua = uasync_create(); CHECK(ua);
sender = make_instance(ua, "38240cb82199e504686507f11f6eaa4f740fde6f0c425c495e49a523019a5d68",
"ce8871f07fa056c636d297115f231b08c29cdf94e0d440fce83a07c34416d36a");
receiver = make_instance(ua, "704f2e012c8fa8768130cb0f988a997dccb628372bc5ceccacc78dcbfec5916f",
"b3193173def895bd0fcea6f86af077c7d77216f10395275f627ac18242ec0f01");
sc_init_ctx(&wire_crypto, &sender->my_keys);
CHECK(sc_set_peer_public_key(&wire_crypto, receiver->my_keys.public_key, SC_PEER_PUBKEY_BIN) == SC_OK);
sender->padding.min = sender->padding.max = 0;
receiver->padding.min = receiver->padding.max = ETCP_PADDING_MAX;
ping_case(0, ETCP_PADDING_MAX);
sender->padding.min = sender->padding.max = ETCP_PADDING_MAX;
receiver->padding.min = receiver->padding.max = 0;
ping_case(ETCP_PADDING_MAX, 0);
puts("PASS stateless PING/PONG: encrypted padding boundaries and byte-exact echo");
struct ETCP_CONN* conn = make_connection(sender, &receiver->my_keys);
struct ETCP_CONN* peer_conn = make_connection(receiver, &sender->my_keys);
conn->padding.min = conn->padding.max = 7;
peer_conn->padding.min = peer_conn->padding.max = 13;
CHECK(etcp_padding_for_peer(sender, receiver->my_keys.public_key) == &conn->padding);
ping_case(7, 13);
puts("PASS PING/PONG: connection ranges override independent instance ranges");
malformed_ping_cases();
uint8_t large[1370] = {0}; struct sockaddr_storage addr; socklen_t alen = sizeof(addr);
CHECK(getsockname(receiver->etcp_sockets->fd, (struct sockaddr*)&addr, &alen) == 0);
int before = wire_calls;
CHECK(etcp_send_ping(sender, receiver->my_keys.public_key, &addr, 500, ping_cb, NULL, large, sizeof(large)) < 0);
CHECK(wire_calls == before && !sender->pending_pings);
puts("PASS oversized PING: minimum padding must fit MTU, user_data is not truncated");
struct ETCP_LINK link = {0}; link.etcp = conn; link.conn = sender->etcp_sockets;
link.mtu = 1280; link.remote_addr.ss_family = AF_INET; link.ka_period_ms = 200;
link.send_hook = keepalive_hook;
for (int i = 0; i < 2; i++) {
expected_keepalive_padding = i ? ETCP_PADDING_MAX : 0;
conn->padding.min = conn->padding.max = expected_keepalive_padding;
etcp_link_send_keepalive(&link);
}
CHECK(keepalive_packets == 2);
struct ETCP_DGRAM* malformed = memory_pool_alloc(sender->pkt_pool); CHECK(malformed);
memset(malformed->data, 0, 7); malformed->data[0] = ETCP_KEEPALIVE;
malformed->data[1] = KA_FLAG_SLEEP; malformed->data_len = 7;
uint32_t received = link.keepalive_recv_count;
CHECK(etcp_keepalive_on_recv(sender->etcp_sockets, malformed, &link, 10) == 1);
CHECK(link.keepalive_recv_count == received && !link.ka_peer_sleeping);
puts("PASS keepalive: encrypted padding boundaries and receive parsing");
struct ETCP_CONN* sibling = etcp_connection_create(sender, "padding-sibling"); CHECK(sibling);
struct ETCP_PADDING saved = sender->padding, saved_peer = peer_conn->padding, saved_sibling = sibling->padding;
force_refresh(&conn->padding);
CHECK(memcmp(&saved, &sender->padding, sizeof(saved)) == 0);
CHECK(memcmp(&saved_peer, &peer_conn->padding, sizeof(saved_peer)) == 0);
CHECK(memcmp(&saved_sibling, &sibling->padding, sizeof(saved_sibling)) == 0);
saved = conn->padding;
force_refresh(&sender->padding);
CHECK(memcmp(&saved, &conn->padding, sizeof(saved)) == 0);
int seen[ETCP_PADDING_MAX + 1] = {0}, different = 0;
for (int i = 0; i < 512; i++) {
int size = etcp_padding_size(&conn->padding, ETCP_PADDING_MAX);
CHECK(size >= conn->padding.min && size <= conn->padding.max);
if (!seen[size]++) different++;
}
CHECK(different > 1);
struct ETCP_PADDING boundary = {.min = 10, .max = 20};
CHECK(etcp_padding_size(&boundary, 9) == -1 && etcp_padding_size(&boundary, 10) == 10);
puts("PASS independent refresh timers, range invariants and MTU capacity");
CHECK(etcp_conn_ref_take(conn) == 0);
etcp_connection_close(conn); CHECK(!conn->padding.timer);
CHECK(etcp_padding_for_peer(sender, receiver->my_keys.public_key) == &sender->padding);
etcp_conn_ref_free(conn); etcp_connection_close(peer_conn); etcp_connection_close(sibling); uasync_poll(ua, 0);
fail_timer = 1;
CHECK(etcp_connection_create(sender, "padding-allocation-failure") == NULL);
size_t allocations = u_get_allocated_count();
CHECK(utun_instance_create_from_config(ua, sender->config) == NULL);
CHECK(u_get_allocated_count() == allocations);
fail_timer = 0; uasync_poll(ua, 0);
CHECK(!sender->connections->count);
utun_instance_destroy(sender); utun_instance_destroy(receiver); uasync_poll(ua, 0);
CHECK(ua->timer_alloc_count == ua->timer_free_count);
uasync_destroy(ua, 0);
puts("PASS timer cancellation and connection creation failure cleanup");
return 0;
}