diff --git a/tests/Makefile.am b/tests/Makefile.am index 63c8b7ae..8ea3a3ef 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -646,7 +646,7 @@ test_memory_pool_and_config_SOURCES = test_memory_pool_and_config.c test_memory_pool_and_config_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_memory_pool_and_config_LDADD = $(COMMON_LIBS) -test_packet_dump_SOURCES = test_packet_dump.c +test_packet_dump_SOURCES = test_packet_dump.c ../src/transport_layer/packet_dump.c test_packet_dump_CFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/lib test_packet_dump_LDADD = $(COMMON_LIBS) diff --git a/tests/test_packet_dump.c b/tests/test_packet_dump.c index 139cd0d1..9987c64d 100644 --- a/tests/test_packet_dump.c +++ b/tests/test_packet_dump.c @@ -1,26 +1,182 @@ +/* Проверки ограничений входа и диагностического вывода настоящего IPv4-форматтера. */ #include -#include +#include +#include #include "../lib/debug_config.h" +#include "../lib/mem.h" +#include "../src/transport_layer/packet_dump.h" -// Simple test packet dump -void test_packet_dump() { - uint8_t test_data[] = {0x02, 0x11, 0x22, 0x33, 0x44, 0x55}; - - // Simulate single-line packet dump - char hex_buf[32]; - snprintf(hex_buf, sizeof(hex_buf), "%02x%02x%02x%02x%02x%02x", - test_data[0], test_data[1], test_data[2], test_data[3], test_data[4], test_data[5]); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TEST_SEND: len=%zu hex=%s", sizeof(test_data), hex_buf); - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TEST_COMPACT: link=NULL type=0x%02x ts=0 len=%zu", test_data[0], sizeof(test_data)); +#define CHECK(expr) do { \ + if (!(expr)) { DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet dump test line=%d: %s", __LINE__, #expr); exit(1); } \ +} while (0) + +static void expect_contains(const char* text, const char* part) { + if (!strstr(text, part)) { + DEBUG_ERROR(DEBUG_CATEGORY_SYS, "packet dump: missing '%s' in '%s'", part, text); + exit(1); + } } -int main() { - debug_config_init(); - debug_set_level(DEBUG_LEVEL_TRACE); - debug_set_categories(DEBUG_CATEGORY_ALL); - - DEBUG_INFO(DEBUG_CATEGORY_ETCP, "=== Testing single-line packet dump ==="); - test_packet_dump(); +static void init_packet(uint8_t* packet, size_t total, unsigned ihl, uint8_t protocol) { + memset(packet, 0, total); + packet[0] = 0x40 | (ihl / 4); + packet[2] = (uint8_t)(total >> 8); packet[3] = (uint8_t)total; + packet[9] = protocol; + packet[12] = 192; packet[13] = 168; packet[14] = 1; packet[15] = 2; + packet[16] = 10; packet[17] = 20; packet[18] = 30; packet[19] = 40; +} + +static void test_protocols(void) { + uint8_t packet[60]; + init_packet(packet, 40, 20, 6); + packet[22] = 0xff; packet[23] = 0xff; + packet[24] = 0x89; packet[25] = 0xab; packet[26] = 0xcd; packet[27] = 0xef; + packet[28] = 0xff; packet[29] = 0xff; packet[30] = 0xff; packet[31] = 0xff; + packet[32] = 0x50; packet[33] = 0xff; packet[34] = 0x80; packet[35] = 0x01; + const char* out = dump_ip_packet_to_buffer(packet, 40); + CHECK(strcmp(out, "IPv4 TCP 192.168.1.2:0 -> 10.20.30.40:65535 (40 bytes) " + "[FIN,SYN,RST,PSH,ACK,URG,ECE,CWR] seq=2309737967 ack=4294967295 win=32769 " + "data: 0000ffff89abcdef ffffffff50ff8001 00000000") == 0); + DEBUG_INFO(DEBUG_CATEGORY_SYS, "%s", out); + packet[33] = 0; + expect_contains(dump_ip_packet_to_buffer(packet, 40), "[none]"); + + init_packet(packet, 28, 20, 17); + packet[22] = 0x01; packet[23] = 0xbb; packet[25] = 8; + CHECK(strcmp(dump_ip_packet_to_buffer(packet, 28), + "IPv4 UDP 192.168.1.2:0 -> 10.20.30.40:443 (28 bytes) len=8 data: 000001bb00080000") == 0); + + init_packet(packet, 28, 20, 1); + packet[20] = 8; packet[24] = 0x12; packet[25] = 0x34; packet[26] = 0xab; packet[27] = 0xcd; + expect_contains(dump_ip_packet_to_buffer(packet, 28), "echo_request seq=43981 id=4660 data: 080000001234abcd"); + packet[20] = 0; + expect_contains(dump_ip_packet_to_buffer(packet, 28), "echo_reply seq=43981 id=4660"); + packet[20] = 3; packet[21] = 1; + expect_contains(dump_ip_packet_to_buffer(packet, 28), "type=3 code=1"); + + init_packet(packet, 20, 20, 99); + CHECK(strcmp(dump_ip_packet_to_buffer(packet, 20), + "IPv4 UNKNOWN 192.168.1.2 -> 10.20.30.40 (20 bytes) data: ") == 0); + DEBUG_INFO(DEBUG_CATEGORY_SYS, "PASS protocols: TCP flags/none, zero/max ports, seq/ack/window, UDP, ICMP, unknown/empty"); +} + +static void test_input_diagnostics(void) { + uint8_t packet[60]; + expect_contains(dump_ip_packet_to_buffer(NULL, 0), "Missing IP packet"); + expect_contains(dump_ip_packet_to_buffer(NULL, 100), "Missing IP packet"); + init_packet(packet, 20, 20, 99); + packet[0] = 0x65; + expect_contains(dump_ip_packet_to_buffer(packet, 20), "Non-IPv4 packet: version=6"); + for (unsigned ihl = 0; ihl < 5; ihl++) { + packet[0] = 0x40 | ihl; + expect_contains(dump_ip_packet_to_buffer(packet, 20), "Invalid IP header length"); + } + init_packet(packet, 20, 20, 99); + packet[3] = 19; + expect_contains(dump_ip_packet_to_buffer(packet, 20), "Invalid IP total length"); + packet[3] = 0; + expect_contains(dump_ip_packet_to_buffer(packet, 20), "Invalid IP total length"); + packet[0] = 0x4f; + expect_contains(dump_ip_packet_to_buffer(packet, 20), "Truncated IP header"); + DEBUG_INFO(DEBUG_CATEGORY_SYS, "PASS input diagnostics: NULL, version, invalid IHL, total length, missing options"); +} + +static void test_lengths_and_alignment(void) { + uint8_t packet[76]; + init_packet(packet, sizeof(packet), 60, 17); + packet[65] = 16; + debug_set_category_level(DEBUG_CATEGORY_DUMP, DEBUG_LEVEL_DISABLED); + for (size_t len = 0; len <= sizeof(packet); len++) { + uint8_t* exact = u_malloc(len + 1); + CHECK(exact != NULL); + memcpy(exact + 1, packet, len); + const char* out = dump_ip_packet_to_buffer(exact + 1, len); + if (len < 20) expect_contains(out, "Packet too small"); + else if (len < 60) expect_contains(out, "Truncated IP header"); + else if (len < sizeof(packet)) expect_contains(out, "Truncated IPv4 packet"); + else expect_contains(out, "UDP 192.168.1.2:0 -> 10.20.30.40:0 (76 bytes) len=16"); + CHECK(memcmp(exact + 1, packet, len) == 0); + u_free(exact); + } + debug_set_category_level(DEBUG_CATEGORY_DUMP, DEBUG_LEVEL_WARN); + + init_packet(packet, 28, 20, 17); + packet[25] = 8; + memset(packet + 28, 0xab, sizeof(packet) - 28); + const char* out = dump_ip_packet_to_buffer(packet, sizeof(packet)); + expect_contains(out, "(28 bytes) len=8 data: 0000000000080000"); + CHECK(!strstr(out, "abab")); + DEBUG_INFO(DEBUG_CATEGORY_SYS, "PASS lengths: 77 capture lengths, IP options, unaligned input, ignored capture suffix, unchanged input"); +} + +static void test_fragments(void) { + uint8_t packet[40]; + const uint8_t protocols[] = {6, 17, 1}; + for (size_t i = 0; i < sizeof(protocols); i++) { + init_packet(packet, sizeof(packet), 20, protocols[i]); + packet[7] = 1; + const char* out = dump_ip_packet_to_buffer(packet, sizeof(packet)); + expect_contains(out, "192.168.1.2 -> 10.20.30.40 (40 bytes) fragment offset=8 MF=0"); + CHECK(!strstr(out, "seq=") && !strstr(out, "len=") && !strstr(out, "type=") && !strstr(out, "header:")); + packet[6] = 0x3f; packet[7] = 0xff; + expect_contains(dump_ip_packet_to_buffer(packet, sizeof(packet)), "fragment offset=65528 MF=1"); + } + init_packet(packet, sizeof(packet), 20, 6); + packet[6] = 0x20; packet[32] = 0x50; + expect_contains(dump_ip_packet_to_buffer(packet, sizeof(packet)), "[none] seq=0 ack=0 win=0 fragment offset=0 MF=1"); + packet[3] = 28; + expect_contains(dump_ip_packet_to_buffer(packet, 28), "partial header: available=8 required=20 fragment offset=0 MF=1"); + packet[6] = 0; + expect_contains(dump_ip_packet_to_buffer(packet, 28), "truncated header: available=8 required=20"); + DEBUG_INFO(DEBUG_CATEGORY_SYS, "PASS fragments: subsequent/first, MF, maximum offset, partial transport header"); +} + +static void test_hex_bounds(void) { + const uint8_t protocols[] = {6, 17, 1, 99}; + size_t cases = 0; + debug_set_category_level(DEBUG_CATEGORY_DUMP, DEBUG_LEVEL_DISABLED); + for (size_t p = 0; p < sizeof(protocols); p++) { + for (unsigned ihl = 20; ihl <= 60; ihl += 4) { + for (size_t count = 0; count <= 65; count++) { + size_t total = ihl + count; + uint8_t* exact = u_malloc(total + 1); + CHECK(exact != NULL); + uint8_t* packet = exact + 1; + init_packet(packet, total, ihl, protocols[p]); + memset(packet + ihl, 0xa5, count); + const char* out = dump_ip_packet_to_buffer(packet, total); + const char* hex = strstr(out, " data: "); + CHECK(hex != NULL && strlen(out) < 1024); + hex += strlen(" data: "); + size_t displayed = count < 32 ? count : 32; + CHECK(strlen(hex) == displayed * 2 + (displayed ? (displayed - 1) / 8 : 0)); + for (size_t i = 0; i < displayed; i++) { + if (i && i % 8 == 0) CHECK(*hex++ == ' '); + CHECK(*hex++ == 'a'); CHECK(*hex++ == '5'); + } + CHECK(*hex == '\0'); + u_free(exact); + cases++; + } + } + } + debug_set_category_level(DEBUG_CATEGORY_DUMP, DEBUG_LEVEL_WARN); + DEBUG_INFO(DEBUG_CATEGORY_SYS, "PASS hex bounds: %zu cases, four protocols, 11 IHL values, 0..65 payload bytes", cases); +} + +int main(void) { + debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); + debug_set_category_level(DEBUG_CATEGORY_DUMP, DEBUG_LEVEL_WARN); + test_protocols(); + test_input_diagnostics(); + test_lengths_and_alignment(); + test_fragments(); + test_hex_bounds(); + uint8_t packet[20]; + init_packet(packet, sizeof(packet), 20, 99); + char* first = dump_ip_packet_to_buffer(packet, sizeof(packet)); + CHECK(first == dump_ip_packet_to_buffer(NULL, 0)); + expect_contains(first, "Missing IP packet"); + DEBUG_INFO(DEBUG_CATEGORY_SYS, "PASS packet dump: 5 groups and static output lifetime"); return 0; }