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.
 
 
 
 
 
 

324 lines
10 KiB

// test_pkt_normalizer_standalone.c - Standalone test for pkt_normalizer with mock ETCP loopback
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "../lib/debug_config.h"
#include "../lib/u_async.h"
#include "../lib/ll_queue.h"
#include "../lib/memory_pool.h"
#include "../src/pkt_normalizer.h"
#include "../src/etcp.h"
#define TOTAL_PACKETS 100
#define MIN_PACKET_SIZE 10
#define MAX_PACKET_SIZE 3000
#define MTU_SIZE 1500
#define TEST_TIMEOUT_MS 10000
// Use the real UTUN_INSTANCE structure but only initialize fields we need
#include "../src/utun_instance.h"
#include "../lib/mem.h"
// Test state
static struct UTUN_INSTANCE mock_instance;
static struct ETCP_CONN mock_etcp;
static struct PKTNORM* pn = NULL;
static int test_completed = 0;
static int packets_sent = 0;
static int packets_received = 0;
static int packet_sizes[TOTAL_PACKETS];
static struct timespec start_time, end_time;
static int fragments_sent = 0;
static int fragments_received = 0;
// Generate packet data with pattern
static void generate_packet_data(int seq, uint8_t* buffer, int size) {
buffer[0] = (uint8_t)(seq & 0xFF);
buffer[1] = (uint8_t)((seq >> 8) & 0xFF);
buffer[2] = (uint8_t)(size & 0xFF);
buffer[3] = (uint8_t)((size >> 8) & 0xFF);
for (int i = 4; i < size; i++) {
buffer[i] = (uint8_t)((seq * 7 + i * 13) % 256);
}
}
// Verify packet data integrity
static int verify_packet_data(uint8_t* buffer, int size, int expected_seq) {
if (size < 4) return 0;
int seq = buffer[0] | (buffer[1] << 8);
int pkt_size = buffer[2] | (buffer[3] << 8);
if (seq != expected_seq || pkt_size != size) {
return 0;
}
for (int i = 4; i < size; i++) {
if (buffer[i] != (uint8_t)((seq * 7 + i * 13) % 256)) {
return 0;
}
}
return 1;
}
// Loopback callback: moves all fragments from input_queue to output_queue
static void loopback_callback(struct ll_queue* q, void* arg) {
(void)arg;
struct ETCP_FRAGMENT* frag;
while ((frag = (struct ETCP_FRAGMENT*)queue_data_get(q)) != NULL) {
fragments_sent++;
// Move fragment from input to output (loopback)
queue_data_put(mock_etcp.output_queue, (struct ll_entry*)frag, 0);
}
queue_resume_callback(q);
}
// Send all packets
static void send_packets(void) {
printf("Sending %d packets...\n", TOTAL_PACKETS);
clock_gettime(CLOCK_MONOTONIC, &start_time);
for (int i = 0; i < TOTAL_PACKETS; i++) {
int size = packet_sizes[i];
uint8_t* buffer = u_malloc(size);
if (!buffer) {
printf("Failed to allocate buffer for packet %d\n", i);
test_completed = 2;
return;
}
generate_packet_data(i, buffer, size);
pn_packer_send(pn, buffer, size);
u_free(buffer);
packets_sent++;
}
printf("All %d packets queued to normalizer\n", TOTAL_PACKETS);
}
// Check received packets from pn->output (where unpacker puts assembled packets)
static void check_received_packets(void) {
void* data;
while ((data = queue_data_get(pn->output)) != NULL) {
struct ll_entry* entry = (struct ll_entry*)data;
fragments_received++;
if (entry->len >= 4) {
int seq = entry->dgram[0] | (entry->dgram[1] << 8);
if (verify_packet_data(entry->dgram, entry->len, seq)) {
packets_received++;
} else {
printf("ERROR: Packet verification failed, seq=%d, len=%d\n", seq, entry->len);
}
} else {
printf("ERROR: Packet too small, len=%d\n", entry->len);
}
queue_dgram_free(entry);
queue_entry_free(data);
}
}
// Monitor function - only checks received packets, callbacks handle data flow
static void monitor(void* arg) {
(void)arg;
if (test_completed) return;
// Check received packets - unpacker callback puts them in pn->output
check_received_packets();
// Check if all packets received
if (packets_received >= TOTAL_PACKETS) {
clock_gettime(CLOCK_MONOTONIC, &end_time);
double duration = (end_time.tv_sec - start_time.tv_sec) * 1000.0 +
(end_time.tv_nsec - start_time.tv_nsec) / 1000000.0;
test_completed = 1;
printf("\n=== SUCCESS: All packets received! ===\n");
printf("Sent: %d, Received: %d\n", packets_sent, packets_received);
printf("Fragments: sent=%d, received=%d\n", fragments_sent, fragments_received);
printf("Duration: %.2f ms\n", duration);
return;
}
// Schedule next check
uasync_set_timeout(mock_instance.ua, 10, NULL, monitor);
}
// Timeout handler
static void test_timeout(void* arg) {
(void)arg;
if (!test_completed) {
printf("\n=== TIMEOUT ===\n");
printf("Sent: %d/%d, Received: %d/%d\n", packets_sent, TOTAL_PACKETS, packets_received, TOTAL_PACKETS);
printf("Fragments: sent=%d, received=%d\n", fragments_sent, fragments_received);
printf("Input queue count: %d, Output queue count: %d\n",
queue_entry_count(mock_etcp.input_queue),
queue_entry_count(mock_etcp.output_queue));
test_completed = 2;
}
}
// Initialize mock ETCP
static int init_mock_etcp(void) {
memset(&mock_etcp, 0, sizeof(mock_etcp));
mock_etcp.mtu = MTU_SIZE;
mock_etcp.instance = (struct UTUN_INSTANCE*)&mock_instance;
// Create io_pool for ETCP_FRAGMENT allocation
mock_etcp.io_pool = memory_pool_init(sizeof(struct ETCP_FRAGMENT));
if (!mock_etcp.io_pool) {
printf("Failed to create io_pool\n");
return -1;
}
// Create queues
mock_etcp.input_queue = queue_new(mock_instance.ua, 0,"q1");
mock_etcp.output_queue = queue_new(mock_instance.ua, 0,"q2");
if (!mock_etcp.input_queue || !mock_etcp.output_queue) {
printf("Failed to create queues\n");
return -1;
}
// Set up loopback callback on input queue
queue_set_callback(mock_etcp.input_queue, loopback_callback, NULL);
return 0;
}
// Cleanup
static void cleanup(void) {
if (pn) {
pn_deinit(pn);
}
if (mock_etcp.input_queue) {
// Drain queue
struct ETCP_FRAGMENT* frag;
while ((frag = (struct ETCP_FRAGMENT*)queue_data_get(mock_etcp.input_queue)) != NULL) {
if (frag->ll.dgram) memory_pool_free(mock_instance.data_pool, frag->ll.dgram);
queue_entry_free((struct ll_entry*)frag);
}
queue_free(mock_etcp.input_queue);
}
if (mock_etcp.output_queue) {
// Drain queue
struct ETCP_FRAGMENT* frag;
while ((frag = (struct ETCP_FRAGMENT*)queue_data_get(mock_etcp.output_queue)) != NULL) {
if (frag->ll.dgram) memory_pool_free(mock_instance.data_pool, frag->ll.dgram);
queue_entry_free((struct ll_entry*)frag);
}
queue_free(mock_etcp.output_queue);
}
if (mock_instance.data_pool) {
memory_pool_destroy(mock_instance.data_pool);
}
if (mock_etcp.io_pool) {
memory_pool_destroy(mock_etcp.io_pool);
}
if (mock_instance.ua) {
uasync_destroy(mock_instance.ua, 0);
}
}
int main() {
printf("=== PKT Normalizer Standalone Test (Loopback) ===\n");
printf("MTU: %d, Frag size: %d\n", MTU_SIZE, MTU_SIZE - 100);
printf("Testing with %d packets of random sizes (%d-%d bytes)\n\n",
TOTAL_PACKETS, MIN_PACKET_SIZE, MAX_PACKET_SIZE);
// Generate random packet sizes
srand((unsigned)time(NULL));
int total_bytes = 0;
for (int i = 0; i < TOTAL_PACKETS; i++) {
packet_sizes[i] = MIN_PACKET_SIZE + rand() % (MAX_PACKET_SIZE - MIN_PACKET_SIZE + 1);
total_bytes += packet_sizes[i];
}
printf("Total data to transfer: %d bytes (%.2f KB average per packet)\n\n",
total_bytes, (float)total_bytes / TOTAL_PACKETS / 1024);
// Enable debug output
debug_set_level(DEBUG_LEVEL_INFO);
debug_enable_category(DEBUG_CATEGORY_ETCP);
// Initialize UASYNC (includes Winsock init on Windows)
mock_instance.ua = uasync_create();
if (!mock_instance.ua) {
printf("Failed to create UASYNC\n");
return 1;
}
// Initialize memory pool
mock_instance.data_pool = memory_pool_init(MTU_SIZE);
if (!mock_instance.data_pool) {
printf("Failed to create memory pool\n");
cleanup();
return 1;
}
// Initialize mock ETCP
if (init_mock_etcp() < 0) {
cleanup();
return 1;
}
// Initialize normalizer
pn = pn_init(&mock_etcp);
if (!pn) {
printf("Failed to create normalizer\n");
cleanup();
return 1;
}
// Reset callback for test manual polling
queue_set_callback(pn->output, NULL, NULL);
printf("Normalizer created (frag_size=%d)\n\n", pn->frag_size);
// Set up monitoring and timeout FIRST (before sending)
uasync_set_timeout(mock_instance.ua, 10, NULL, monitor);
uasync_set_timeout(mock_instance.ua, TEST_TIMEOUT_MS, NULL, test_timeout);
// Give uasync a chance to process any pending callbacks
uasync_poll(mock_instance.ua, 1);
// Send packets
send_packets();
// Wait for flush timer to send last fragment
// The flush timer is set to pn->tx_wait_time (10ms) after last packet
// Poll uasync to process the flush timer
for (int i = 0; i < 20; i++) {
uasync_poll(mock_instance.ua, 5);
}
while (!test_completed) {
uasync_poll(mock_instance.ua, 100);
}
printf("\nCleaning up...\n");
cleanup();
if (test_completed == 1) {
printf("\n=== TEST PASSED ===\n");
return 0;
} else {
printf("\n=== TEST FAILED ===\n");
printf("Sent: %d/%d, Received: %d/%d\n", packets_sent, TOTAL_PACKETS, packets_received, TOTAL_PACKETS);
printf("Fragments: sent=%d, received=%d\n", fragments_sent, fragments_received);
return 1;
}
}