Files
utun2/tests/test_dummynet_channels.c
T

134 lines
6.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Калибровка каналов: BWlim смешанных размеров, задержка, ограниченный HOLD, DROP и teardown с таймерами. */
#include <stdlib.h>
#include <string.h>
#include "dummynet.h"
#include "test_clock.h"
#include "../lib/platform_compat.h"
#include "../lib/mem.h"
#define CHECK(x) do { if (!(x)) { DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "FAIL %s:%d: %s", __func__, __LINE__, #x); exit(1); } } while (0)
static struct UASYNC* ua;
static struct dummynet* dn;
static socket_t peers[2];
static struct sockaddr_in proxy;
static unsigned received[2];
static uint64_t bytes[2], last_rx[2];
static uint8_t seen[2][512];
static void setup(unsigned bw, unsigned delay, unsigned capacity) {
ua = uasync_create(); CHECK(ua);
dn = dummynet_create(ua, "127.0.0.1", 0); CHECK(dn);
memset(received, 0, sizeof(received)); memset(bytes, 0, sizeof(bytes)); memset(seen, 0, sizeof(seen));
memset(&proxy, 0, sizeof(proxy)); proxy.sin_family = AF_INET;
proxy.sin_addr.s_addr = htonl(INADDR_LOOPBACK); proxy.sin_port = htons(dummynet_get_listen_port(dn));
for (int i = 0; i < 2; i++) {
peers[i] = socket_create_udp(AF_INET); CHECK(peers[i] != SOCKET_INVALID);
struct sockaddr_in address = proxy; address.sin_port = 0;
CHECK(bind(peers[i], (struct sockaddr*)&address, sizeof(address)) == 0);
CHECK(socket_set_nonblocking(peers[i]) == 0);
int size = 4 * 1024 * 1024;
CHECK(setsockopt(peers[i], SOL_SOCKET, SO_RCVBUF, (const char*)&size, sizeof(size)) == 0);
CHECK(dummynet_set_direction(dn, 1-i, delay, 0, bw, capacity, 0,
"127.0.0.1", socket_get_bound_port(peers[i])) == 0);
}
}
/* Проверяем каждый байт и уникальность независимо от статистики эмулятора. */
static void poll_channels(int timeout) {
test_clock_poll(ua, timeout);
for (int i = 0; i < 2; i++) {
uint8_t data[1500]; ssize_t n;
while ((n = socket_recvfrom(peers[i], data, sizeof(data), NULL, NULL)) >= 0) {
CHECK(n >= 4);
unsigned seq = data[0] | (data[1] << 8); CHECK(seq < 512 && !seen[i][seq]);
seen[i][seq] = 1;
for (ssize_t p = 2; p < n; p++) CHECK(data[p] == (uint8_t)(seq + p));
received[i]++; bytes[i] += n; last_rx[i] = get_time_us();
}
CHECK(socket_get_error() == ERR_WOULDBLOCK || socket_get_error() == ERR_AGAIN);
}
}
static void send_packet(int from, unsigned seq, unsigned size) {
uint8_t data[1500]; CHECK(size >= 4 && size <= sizeof(data));
data[0] = seq; data[1] = seq >> 8;
for (unsigned i = 2; i < size; i++) data[i] = seq + i;
CHECK(socket_sendto(peers[from], data, size, (struct sockaddr*)&proxy, sizeof(proxy)) == (ssize_t)size);
poll_channels(0);
}
static void run_for(unsigned ms) {
uint64_t end = get_time_us() + ms * 1000ULL;
while (get_time_us() < end) poll_channels(10);
}
static void teardown(void) {
dummynet_destroy(dn);
for (int i = 0; i < 2; i++) socket_close_wrapper(peers[i]);
uasync_poll(ua, 0);
CHECK(ua->timer_alloc_count == ua->timer_free_count);
uasync_destroy(ua, 0);
}
static void bandwidth(unsigned bw) {
setup(bw, 20, 512);
uint64_t start = get_time_us(), total = 0;
for (unsigned seq = 0; seq < 256; seq++) {
unsigned len = seq & 1 ? 1500 : 100;
total += len;
send_packet(0, seq, len); send_packet(1, seq, len);
}
uint64_t expected = total * 8000 / bw + 20000, end = start + expected + 2000000;
while ((received[0] < 256 || received[1] < 256) && get_time_us() < end) poll_channels(10);
for (int i = 0; i < 2; i++) {
uint64_t elapsed = last_rx[i] - start;
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "BW calibration: kbps=%u dir=%d bytes=%llu elapsed_us=%llu expected_us=%llu", bw, i,
(unsigned long long)bytes[i], (unsigned long long)elapsed, (unsigned long long)expected);
CHECK(received[i] == 256 && bytes[i] == total);
CHECK(elapsed * 100 >= expected * 95 && elapsed * 100 <= expected * 105 + 5000);
const struct dummynet_stats* stats = dummynet_get_stats(dn, 1-i);
CHECK(stats->sent_bytes == total && !stats->queue_size && !stats->lost && !stats->dropped);
}
teardown();
}
static void hold_drop(void) {
setup(80, 20, 8);
CHECK(dummynet_set_mode(dn, 0, DUMMYNET_HOLD) == 0);
for (unsigned seq = 0; seq < 12; seq++) send_packet(0, seq, 1000);
run_for(60);
const struct dummynet_stats* stats = dummynet_get_stats(dn, 0);
CHECK(!received[1] && stats->queue_size == 8 && stats->dropped == 4);
dummynet_reset_stats(dn, 0); CHECK(stats->queue_size == 8 && stats->queue_max == 8);
uint64_t resumed = get_time_us();
CHECK(dummynet_set_mode(dn, 0, DUMMYNET_RUN) == 0);
run_for(50); CHECK(!received[1]);
run_for(850); CHECK(received[1] == 8 && last_rx[1] - resumed >= 795000);
// DROP удаляет как delay, так и сериализуемую голову; RUN не возвращает потерянное.
send_packet(0, 20, 1000); run_for(5);
CHECK(dummynet_set_mode(dn, 0, DUMMYNET_DROP) == 0);
send_packet(0, 21, 1000); run_for(50);
CHECK(stats->blocked == 2 && !stats->queue_size && received[1] == 8);
CHECK(dummynet_set_mode(dn, 0, DUMMYNET_RUN) == 0);
send_packet(0, 22, 1000); run_for(25);
CHECK(dummynet_set_mode(dn, 0, DUMMYNET_DROP) == 0);
CHECK(stats->blocked == 3 && !stats->queue_size);
CHECK(dummynet_set_mode(dn, 0, DUMMYNET_RUN) == 0);
send_packet(0, 23, 1000); run_for(150); CHECK(received[1] == 9);
// Уничтожение одновременно с delay и shaper: после destroycallbacks не должны обращаться к dn.
send_packet(0, 24, 1000); run_for(25); send_packet(1, 25, 1000);
teardown();
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS bounded HOLD, paced resume, DROP of pending packets and active teardown");
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); test_clock_init(10);
CHECK(socket_platform_init() == 0);
size_t baseline = u_get_allocated_count();
bandwidth(2000); CHECK(u_get_allocated_count() == baseline);
bandwidth(8000); CHECK(u_get_allocated_count() == baseline);
hold_drop(); CHECK(u_get_allocated_count() == baseline);
test_clock_finish(); socket_platform_cleanup();
return 0;
}