Browse Source
- Fixed pkt_normalizer.c to send packets immediately instead of buffering - Added queue_resume_callback() call in etcp.c after adding to output_queue - Updated test to use simple checksum verification instead of pattern-based - Added strict sequence order checking in test - Reduced MAX_TEST_PACKET_SIZE to 1400 to fit in normalizer fragment - Reduced TOTAL_PACKETS to 10 and TEST_TIMEOUT_MS to 5s for faster testingnodeinfo-routing-update
33 changed files with 3441 additions and 1436 deletions
@ -1,556 +1 @@
|
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_etcp_two_instances»: Нет такого файла или каталога |
||||
cc1: fatal error: test_ll_queue_fixed.c: Нет такого файла или каталога |
||||
compilation terminated. |
||||
Using built-in specs. |
||||
COLLECT_GCC=gcc |
||||
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper |
||||
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa |
||||
OFFLOAD_TARGET_DEFAULT=1 |
||||
Target: x86_64-linux-gnu |
||||
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 13.3.0-6ubuntu2~24.04' --with-bugurl=file:///usr/share/doc/gcc-13/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-13 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/libexec --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-libstdcxx-backtrace --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-13-fG75Ri/gcc-13-13.3.0/debian/tmp-gcn/usr --enable-offload-defaulted --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2 |
||||
Thread model: posix |
||||
Supported LTO compression algorithms: zlib zstd |
||||
gcc version 13.3.0 (Ubuntu 13.3.0-6ubuntu2~24.04) |
||||
COLLECT_GCC_OPTIONS='-I' '/home/vnc1/proj/utun3/src' '-I' '/home/vnc1/proj/utun3/lib' '-I' '/home/vnc1/proj/utun3/tinycrypt/lib/include' '-I' '/home/vnc1/proj/utun3/tinycrypt/lib/source' '-g' '-O2' '-v' '-o' 'test_ll_queue_fixed' '-mtune=generic' '-march=x86-64' '-dumpdir' 'test_ll_queue_fixed-' |
||||
/usr/libexec/gcc/x86_64-linux-gnu/13/cc1 -quiet -v -I /home/vnc1/proj/utun3/src -I /home/vnc1/proj/utun3/lib -I /home/vnc1/proj/utun3/tinycrypt/lib/include -I /home/vnc1/proj/utun3/tinycrypt/lib/source -imultiarch x86_64-linux-gnu test_ll_queue_fixed.c -D_FORTIFY_SOURCE=3 -quiet -dumpdir test_ll_queue_fixed- -dumpbase test_ll_queue_fixed.c -dumpbase-ext .c -mtune=generic -march=x86-64 -g -O2 -version -fasynchronous-unwind-tables -fstack-protector-strong -Wformat -Wformat-security -fstack-clash-protection -fcf-protection -o /tmp/ccwoLaK6.s |
||||
GNU C17 (Ubuntu 13.3.0-6ubuntu2~24.04) version 13.3.0 (x86_64-linux-gnu) |
||||
compiled by GNU C version 13.3.0, GMP version 6.3.0, MPFR version 4.2.1, MPC version 1.3.1, isl version isl-0.26-GMP |
||||
|
||||
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 |
||||
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu" |
||||
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed/x86_64-linux-gnu" |
||||
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/include-fixed" |
||||
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include" |
||||
#include "..." search starts here: |
||||
#include <...> search starts here: |
||||
/home/vnc1/proj/utun3/src |
||||
/home/vnc1/proj/utun3/lib |
||||
/home/vnc1/proj/utun3/tinycrypt/lib/include |
||||
/home/vnc1/proj/utun3/tinycrypt/lib/source |
||||
/usr/lib/gcc/x86_64-linux-gnu/13/include |
||||
/usr/local/include |
||||
/usr/include/x86_64-linux-gnu |
||||
/usr/include |
||||
End of search list. |
||||
cc1: fatal error: test_ll_queue_fixed.c: Нет такого файла или каталога |
||||
compilation terminated. |
||||
malloc(): corrupted top size |
||||
timeout: отслеживаемая команда завершилась созданием дампа |
||||
timeout: не удалось выполнить команду «./test_ll_queue_updated»: Нет такого файла или каталога |
||||
cc1: fatal error: test_ll_queue_updated.c: Нет такого файла или каталога |
||||
compilation terminated. |
||||
cc1: fatal error: test_ll_queue_updated.c: Нет такого файла или каталога |
||||
compilation terminated. |
||||
cc1: fatal error: test_ll_queue_updated.c: Нет такого файла или каталога |
||||
compilation terminated. |
||||
malloc(): corrupted top size |
||||
timeout: отслеживаемая команда завершилась созданием дампа |
||||
malloc(): corrupted top size |
||||
timeout: отслеживаемая команда завершилась созданием дампа |
||||
timeout: отслеживаемая команда завершилась созданием дампа |
||||
../build-aux/test-driver: строка 112: 1176142 Ошибка сегментирования (образ памяти сброшен на диск) "$@" >> "$log_file" 2>&1 |
||||
make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 |
||||
make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 |
||||
make[1]: *** [Makefile:1495: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
free(): double free detected in tcache 2 |
||||
timeout: отслеживаемая команда завершилась созданием дампа |
||||
ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U») |
||||
utun_instance.c: In function ‘utun_instance_create’: |
||||
utun_instance.c:109:53: warning: ‘/32’ directive output may be truncated writing 3 bytes into a region of size between 1 and 64 [-Wformat-truncation=] |
||||
109 | snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); |
||||
| ^~~ |
||||
In file included from /usr/include/stdio.h:980, |
||||
from utun_instance.h:6, |
||||
from utun_instance.c:2: |
||||
In function ‘snprintf’, |
||||
inlined from ‘utun_instance_create’ at utun_instance.c:109:9: |
||||
/usr/include/x86_64-linux-gnu/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 4 and 67 bytes into a destination of size 64 |
||||
54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, |
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
55 | __glibc_objsize (__s), __fmt, |
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
56 | __va_arg_pack ()); |
||||
| ~~~~~~~~~~~~~~~~~ |
||||
In file included from config_parser.c:8: |
||||
config_parser.c: In function ‘parse_debug_categories’: |
||||
../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
config_parser.c:38:29: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
38 | if (!value_copy) return DEBUG_CATEGORY_ALL; // Default to all on error |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
config_parser.c:66:27: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
66 | categories |= DEBUG_CATEGORY_ALL; |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
etcp.c: In function ‘etcp_connection_create’: |
||||
etcp.c:99:43: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] |
||||
99 | queue_set_callback(etcp->input_queue, input_queue_cb, etcp); |
||||
| ^~~~~~~~~~~~~~ |
||||
| | |
||||
| void (*)(struct ll_queue *, void *) |
||||
In file included from etcp.h:7, |
||||
from etcp.c:3: |
||||
../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ |
||||
83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
||||
| ~~~~~~~~~~~~~~~~~~^~~~~~ |
||||
etcp.c:100:44: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] |
||||
100 | queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); |
||||
| ^~~~~~~~~~~~~~~ |
||||
| | |
||||
| void (*)(struct ll_queue *, void *) |
||||
../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ |
||||
83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
||||
| ~~~~~~~~~~~~~~~~~~^~~~~~ |
||||
pkt_normalizer.c: In function ‘pkt_normalizer_send_service’: |
||||
pkt_normalizer.c:347:22: warning: initialization of ‘uint8_t *’ {aka ‘unsigned char *’} from incompatible pointer type ‘struct ll_entry *’ [-Wincompatible-pointer-types] |
||||
347 | uint8_t* d = (entry); |
||||
| ^ |
||||
make[2]: *** Нет правила для сборки цели «test_ll_queue_working.c», требуемой для «test_ll_queue-test_ll_queue_working.o». Останов. |
||||
make[1]: *** [Makefile:1495: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
make[2]: *** Нет правила для сборки цели «test_ll_queue_working.c», требуемой для «test_ll_queue-test_ll_queue_working.o». Останов. |
||||
make[1]: *** [Makefile:1495: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
make[2]: *** Нет правила для сборки цели «test_ll_queue_working.c», требуемой для «test_ll_queue-test_ll_queue_working.o». Останов. |
||||
make[1]: *** [Makefile:1495: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
test_u_async_comprehensive.c: In function ‘test_concurrent_operations’: |
||||
test_u_async_comprehensive.c:428:13: warning: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Wunused-result] |
||||
428 | write(sockets[0], &data, 1); |
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
In file included from test_debug_categories.c:8: |
||||
test_debug_categories.c: In function ‘main’: |
||||
../lib/debug_config.h:42:36: warning: overflow in conversion from ‘long unsigned int’ to ‘int’ changes value from ‘18446744073709551615’ to ‘-1’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
test_debug_categories.c:64:32: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
64 | debug_categories = DEBUG_CATEGORY_ALL; |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 |
||||
make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 |
||||
make[1]: *** [Makefile:1495: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 |
||||
make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 |
||||
make[1]: *** [Makefile:1495: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U») |
||||
utun_instance.c: In function ‘utun_instance_create’: |
||||
utun_instance.c:109:53: warning: ‘/32’ directive output may be truncated writing 3 bytes into a region of size between 1 and 64 [-Wformat-truncation=] |
||||
109 | snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); |
||||
| ^~~ |
||||
In file included from /usr/include/stdio.h:980, |
||||
from utun_instance.h:6, |
||||
from utun_instance.c:2: |
||||
In function ‘snprintf’, |
||||
inlined from ‘utun_instance_create’ at utun_instance.c:109:9: |
||||
/usr/include/x86_64-linux-gnu/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 4 and 67 bytes into a destination of size 64 |
||||
54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, |
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
55 | __glibc_objsize (__s), __fmt, |
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
56 | __va_arg_pack ()); |
||||
| ~~~~~~~~~~~~~~~~~ |
||||
In file included from config_parser.c:8: |
||||
config_parser.c: In function ‘parse_debug_categories’: |
||||
../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
config_parser.c:38:29: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
38 | if (!value_copy) return DEBUG_CATEGORY_ALL; // Default to all on error |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
config_parser.c:66:27: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
66 | categories |= DEBUG_CATEGORY_ALL; |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
etcp.c: In function ‘etcp_connection_create’: |
||||
etcp.c:99:43: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] |
||||
99 | queue_set_callback(etcp->input_queue, input_queue_cb, etcp); |
||||
| ^~~~~~~~~~~~~~ |
||||
| | |
||||
| void (*)(struct ll_queue *, void *) |
||||
In file included from etcp.h:7, |
||||
from etcp.c:3: |
||||
../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ |
||||
83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
||||
| ~~~~~~~~~~~~~~~~~~^~~~~~ |
||||
etcp.c:100:44: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] |
||||
100 | queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); |
||||
| ^~~~~~~~~~~~~~~ |
||||
| | |
||||
| void (*)(struct ll_queue *, void *) |
||||
../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ |
||||
83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
||||
| ~~~~~~~~~~~~~~~~~~^~~~~~ |
||||
pkt_normalizer.c: In function ‘pkt_normalizer_send_service’: |
||||
pkt_normalizer.c:347:22: warning: initialization of ‘uint8_t *’ {aka ‘unsigned char *’} from incompatible pointer type ‘struct ll_entry *’ [-Wincompatible-pointer-types] |
||||
347 | uint8_t* d = (entry); |
||||
| ^ |
||||
make[1]: *** [Makefile:375: all-recursive] Ошибка 1 |
||||
make: *** [Makefile:316: all] Обрыв канала |
||||
make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
make[2]: *** Нет правила для сборки цели «test_ll_queue_new.c», требуемой для «test_ll_queue-test_ll_queue_new.o». Останов. |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U») |
||||
utun_instance.c: In function ‘utun_instance_create’: |
||||
utun_instance.c:109:53: warning: ‘/32’ directive output may be truncated writing 3 bytes into a region of size between 1 and 64 [-Wformat-truncation=] |
||||
109 | snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); |
||||
| ^~~ |
||||
In file included from /usr/include/stdio.h:980, |
||||
from utun_instance.h:6, |
||||
from utun_instance.c:2: |
||||
In function ‘snprintf’, |
||||
inlined from ‘utun_instance_create’ at utun_instance.c:109:9: |
||||
/usr/include/x86_64-linux-gnu/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 4 and 67 bytes into a destination of size 64 |
||||
54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, |
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
55 | __glibc_objsize (__s), __fmt, |
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
56 | __va_arg_pack ()); |
||||
| ~~~~~~~~~~~~~~~~~ |
||||
In file included from config_parser.c:8: |
||||
config_parser.c: In function ‘parse_debug_categories’: |
||||
../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
config_parser.c:38:29: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
38 | if (!value_copy) return DEBUG_CATEGORY_ALL; // Default to all on error |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
config_parser.c:66:27: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
66 | categories |= DEBUG_CATEGORY_ALL; |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
etcp.c: In function ‘etcp_connection_create’: |
||||
etcp.c:99:43: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] |
||||
99 | queue_set_callback(etcp->input_queue, input_queue_cb, etcp); |
||||
| ^~~~~~~~~~~~~~ |
||||
| | |
||||
| void (*)(struct ll_queue *, void *) |
||||
In file included from etcp.h:7, |
||||
from etcp.c:3: |
||||
../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ |
||||
83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
||||
| ~~~~~~~~~~~~~~~~~~^~~~~~ |
||||
etcp.c:100:44: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] |
||||
100 | queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); |
||||
| ^~~~~~~~~~~~~~~ |
||||
| | |
||||
| void (*)(struct ll_queue *, void *) |
||||
../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ |
||||
83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
||||
| ~~~~~~~~~~~~~~~~~~^~~~~~ |
||||
pkt_normalizer.c: In function ‘pkt_normalizer_send_service’: |
||||
pkt_normalizer.c:347:22: warning: initialization of ‘uint8_t *’ {aka ‘unsigned char *’} from incompatible pointer type ‘struct ll_entry *’ [-Wincompatible-pointer-types] |
||||
347 | uint8_t* d = (entry); |
||||
| ^ |
||||
test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined |
||||
20 | #define DEBUG_CATEGORY_LL_QUEUE 1 |
||||
| |
||||
In file included from test_ll_queue.c:16: |
||||
../lib/debug_config.h:32: note: this is the location of the previous definition |
||||
32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module |
||||
| |
||||
test_u_async_comprehensive.c: In function ‘test_concurrent_operations’: |
||||
test_u_async_comprehensive.c:428:13: warning: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Wunused-result] |
||||
428 | write(sockets[0], &data, 1); |
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
In file included from test_debug_categories.c:8: |
||||
test_debug_categories.c: In function ‘main’: |
||||
../lib/debug_config.h:42:36: warning: overflow in conversion from ‘long unsigned int’ to ‘int’ changes value from ‘18446744073709551615’ to ‘-1’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
test_debug_categories.c:64:32: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
64 | debug_categories = DEBUG_CATEGORY_ALL; |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
../build-aux/test-driver: строка 112: 1197495 Ошибка сегментирования (образ памяти сброшен на диск) "$@" >> "$log_file" 2>&1 |
||||
make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 |
||||
make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 |
||||
make[1]: *** [Makefile:1495: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U») |
||||
utun_instance.c: In function ‘utun_instance_create’: |
||||
utun_instance.c:109:53: warning: ‘/32’ directive output may be truncated writing 3 bytes into a region of size between 1 and 64 [-Wformat-truncation=] |
||||
109 | snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); |
||||
| ^~~ |
||||
In file included from /usr/include/stdio.h:980, |
||||
from utun_instance.h:6, |
||||
from utun_instance.c:2: |
||||
In function ‘snprintf’, |
||||
inlined from ‘utun_instance_create’ at utun_instance.c:109:9: |
||||
/usr/include/x86_64-linux-gnu/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 4 and 67 bytes into a destination of size 64 |
||||
54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, |
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
55 | __glibc_objsize (__s), __fmt, |
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
56 | __va_arg_pack ()); |
||||
| ~~~~~~~~~~~~~~~~~ |
||||
In file included from config_parser.c:8: |
||||
config_parser.c: In function ‘parse_debug_categories’: |
||||
../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
config_parser.c:38:29: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
38 | if (!value_copy) return DEBUG_CATEGORY_ALL; // Default to all on error |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
config_parser.c:66:27: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
66 | categories |= DEBUG_CATEGORY_ALL; |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
etcp.c: In function ‘etcp_connection_create’: |
||||
etcp.c:99:43: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] |
||||
99 | queue_set_callback(etcp->input_queue, input_queue_cb, etcp); |
||||
| ^~~~~~~~~~~~~~ |
||||
| | |
||||
| void (*)(struct ll_queue *, void *) |
||||
In file included from etcp.h:7, |
||||
from etcp.c:3: |
||||
../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ |
||||
83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
||||
| ~~~~~~~~~~~~~~~~~~^~~~~~ |
||||
etcp.c:100:44: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] |
||||
100 | queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); |
||||
| ^~~~~~~~~~~~~~~ |
||||
| | |
||||
| void (*)(struct ll_queue *, void *) |
||||
../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ |
||||
83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
||||
| ~~~~~~~~~~~~~~~~~~^~~~~~ |
||||
pkt_normalizer.c: In function ‘pkt_normalizer_send_service’: |
||||
pkt_normalizer.c:347:22: warning: initialization of ‘uint8_t *’ {aka ‘unsigned char *’} from incompatible pointer type ‘struct ll_entry *’ [-Wincompatible-pointer-types] |
||||
347 | uint8_t* d = (entry); |
||||
| ^ |
||||
test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined |
||||
20 | #define DEBUG_CATEGORY_LL_QUEUE 1 |
||||
| |
||||
In file included from test_ll_queue.c:16: |
||||
../lib/debug_config.h:32: note: this is the location of the previous definition |
||||
32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module |
||||
| |
||||
test_ll_queue.c: In function ‘test_memory_pool_integration’: |
||||
test_ll_queue.c:670:21: error: ‘entry_data3’ undeclared (first use in this function) |
||||
670 | queue_data_free(entry_data3); |
||||
| ^~~~~~~~~~~ |
||||
test_ll_queue.c:670:21: note: each undeclared identifier is reported only once for each function it appears in |
||||
test_ll_queue.c: In function ‘test_edge_cases’: |
||||
test_ll_queue.c:719:21: error: ‘entry_retrieved’ undeclared (first use in this function) |
||||
719 | queue_data_free(entry_retrieved); |
||||
| ^~~~~~~~~~~~~~~ |
||||
test_ll_queue.c: In function ‘test_memory_pool_vs_malloc_performance’: |
||||
test_ll_queue.c:1005:31: error: ‘entry_data’ undeclared (first use in this function) |
||||
1005 | queue_data_put(q, entry_data, data->id); |
||||
| ^~~~~~~~~~ |
||||
make[2]: *** [Makefile:1086: test_ll_queue-test_ll_queue.o] Ошибка 1 |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined |
||||
20 | #define DEBUG_CATEGORY_LL_QUEUE 1 |
||||
| |
||||
In file included from test_ll_queue.c:16: |
||||
../lib/debug_config.h:32: note: this is the location of the previous definition |
||||
32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module |
||||
| |
||||
test_ll_queue.c: In function ‘test_memory_pool_integration’: |
||||
test_ll_queue.c:670:21: error: ‘entry_data3’ undeclared (first use in this function) |
||||
670 | queue_data_free(entry_data3); |
||||
| ^~~~~~~~~~~ |
||||
test_ll_queue.c:670:21: note: each undeclared identifier is reported only once for each function it appears in |
||||
test_ll_queue.c: In function ‘test_edge_cases’: |
||||
test_ll_queue.c:719:21: error: ‘entry_retrieved’ undeclared (first use in this function) |
||||
719 | queue_data_free(entry_retrieved); |
||||
| ^~~~~~~~~~~~~~~ |
||||
test_ll_queue.c: In function ‘test_memory_pool_vs_malloc_performance’: |
||||
test_ll_queue.c:1005:31: error: ‘entry_data’ undeclared (first use in this function) |
||||
1005 | queue_data_put(q, entry_data, data->id); |
||||
| ^~~~~~~~~~ |
||||
make[2]: *** [Makefile:1086: test_ll_queue-test_ll_queue.o] Ошибка 1 |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined |
||||
20 | #define DEBUG_CATEGORY_LL_QUEUE 1 |
||||
| |
||||
In file included from test_ll_queue.c:16: |
||||
../lib/debug_config.h:32: note: this is the location of the previous definition |
||||
32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module |
||||
| |
||||
test_ll_queue.c: In function ‘test_memory_pool_integration’: |
||||
test_ll_queue.c:670:21: error: ‘entry_data3’ undeclared (first use in this function) |
||||
670 | queue_data_free(entry_data3); |
||||
| ^~~~~~~~~~~ |
||||
test_ll_queue.c:670:21: note: each undeclared identifier is reported only once for each function it appears in |
||||
test_ll_queue.c: In function ‘test_edge_cases’: |
||||
test_ll_queue.c:719:21: error: ‘entry_retrieved’ undeclared (first use in this function) |
||||
719 | queue_data_free(entry_retrieved); |
||||
| ^~~~~~~~~~~~~~~ |
||||
test_ll_queue.c: In function ‘test_memory_pool_vs_malloc_performance’: |
||||
test_ll_queue.c:1005:31: error: ‘entry_data’ undeclared (first use in this function) |
||||
1005 | queue_data_put(q, entry_data, data->id); |
||||
| ^~~~~~~~~~ |
||||
make[2]: *** [Makefile:1086: test_ll_queue-test_ll_queue.o] Ошибка 1 |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U») |
||||
utun_instance.c: In function ‘utun_instance_create’: |
||||
utun_instance.c:109:53: warning: ‘/32’ directive output may be truncated writing 3 bytes into a region of size between 1 and 64 [-Wformat-truncation=] |
||||
109 | snprintf(tun_ip_str, sizeof(tun_ip_str), "%s/32", ip_buffer); |
||||
| ^~~ |
||||
In file included from /usr/include/stdio.h:980, |
||||
from utun_instance.h:6, |
||||
from utun_instance.c:2: |
||||
In function ‘snprintf’, |
||||
inlined from ‘utun_instance_create’ at utun_instance.c:109:9: |
||||
/usr/include/x86_64-linux-gnu/bits/stdio2.h:54:10: note: ‘__builtin___snprintf_chk’ output between 4 and 67 bytes into a destination of size 64 |
||||
54 | return __builtin___snprintf_chk (__s, __n, __USE_FORTIFY_LEVEL - 1, |
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
55 | __glibc_objsize (__s), __fmt, |
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
56 | __va_arg_pack ()); |
||||
| ~~~~~~~~~~~~~~~~~ |
||||
In file included from config_parser.c:8: |
||||
config_parser.c: In function ‘parse_debug_categories’: |
||||
../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
config_parser.c:38:29: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
38 | if (!value_copy) return DEBUG_CATEGORY_ALL; // Default to all on error |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
../lib/debug_config.h:42:36: warning: conversion from ‘long unsigned int’ to ‘uint32_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
config_parser.c:66:27: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
66 | categories |= DEBUG_CATEGORY_ALL; |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
etcp.c: In function ‘etcp_connection_create’: |
||||
etcp.c:99:43: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] |
||||
99 | queue_set_callback(etcp->input_queue, input_queue_cb, etcp); |
||||
| ^~~~~~~~~~~~~~ |
||||
| | |
||||
| void (*)(struct ll_queue *, void *) |
||||
In file included from etcp.h:7, |
||||
from etcp.c:3: |
||||
../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ |
||||
83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
||||
| ~~~~~~~~~~~~~~~~~~^~~~~~ |
||||
etcp.c:100:44: warning: passing argument 2 of ‘queue_set_callback’ from incompatible pointer type [-Wincompatible-pointer-types] |
||||
100 | queue_set_callback(etcp->input_send_q, input_send_q_cb, etcp); |
||||
| ^~~~~~~~~~~~~~~ |
||||
| | |
||||
| void (*)(struct ll_queue *, void *) |
||||
../lib/ll_queue.h:83:63: note: expected ‘queue_callback_fn’ {aka ‘void (*)(struct ll_queue *, void *, void *)’} but argument is of type ‘void (*)(struct ll_queue *, void *)’ |
||||
83 | void queue_set_callback(struct ll_queue* q, queue_callback_fn cbk_fn, void* arg); |
||||
| ~~~~~~~~~~~~~~~~~~^~~~~~ |
||||
pkt_normalizer.c: In function ‘pkt_normalizer_send_service’: |
||||
pkt_normalizer.c:347:22: warning: initialization of ‘uint8_t *’ {aka ‘unsigned char *’} from incompatible pointer type ‘struct ll_entry *’ [-Wincompatible-pointer-types] |
||||
347 | uint8_t* d = (entry); |
||||
| ^ |
||||
test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined |
||||
20 | #define DEBUG_CATEGORY_LL_QUEUE 1 |
||||
| |
||||
In file included from test_ll_queue.c:16: |
||||
../lib/debug_config.h:32: note: this is the location of the previous definition |
||||
32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module |
||||
| |
||||
test_ll_queue.c: In function ‘test_memory_pool_integration’: |
||||
test_ll_queue.c:670:21: error: ‘entry_data3’ undeclared (first use in this function) |
||||
670 | queue_data_free(entry_data3); |
||||
| ^~~~~~~~~~~ |
||||
test_ll_queue.c:670:21: note: each undeclared identifier is reported only once for each function it appears in |
||||
test_ll_queue.c: In function ‘test_edge_cases’: |
||||
test_ll_queue.c:719:21: error: ‘entry_retrieved’ undeclared (first use in this function) |
||||
719 | queue_data_free(entry_retrieved); |
||||
| ^~~~~~~~~~~~~~~ |
||||
test_ll_queue.c: In function ‘test_memory_pool_vs_malloc_performance’: |
||||
test_ll_queue.c:1005:31: error: ‘entry_data’ undeclared (first use in this function) |
||||
1005 | queue_data_put(q, entry_data, data->id); |
||||
| ^~~~~~~~~~ |
||||
make[2]: *** [Makefile:1086: test_ll_queue-test_ll_queue.o] Ошибка 1 |
||||
make[1]: *** [Makefile:1494: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined |
||||
20 | #define DEBUG_CATEGORY_LL_QUEUE 1 |
||||
| |
||||
In file included from test_ll_queue.c:16: |
||||
../lib/debug_config.h:32: note: this is the location of the previous definition |
||||
32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module |
||||
| |
||||
test_u_async_comprehensive.c: In function ‘test_concurrent_operations’: |
||||
test_u_async_comprehensive.c:428:13: warning: ignoring return value of ‘write’ declared with attribute ‘warn_unused_result’ [-Wunused-result] |
||||
428 | write(sockets[0], &data, 1); |
||||
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~ |
||||
In file included from test_debug_categories.c:8: |
||||
test_debug_categories.c: In function ‘main’: |
||||
../lib/debug_config.h:42:36: warning: overflow in conversion from ‘long unsigned int’ to ‘int’ changes value from ‘18446744073709551615’ to ‘-1’ [-Woverflow] |
||||
42 | #define DEBUG_CATEGORY_ALL (~((debug_category_t)0)) |
||||
| ^ |
||||
test_debug_categories.c:64:32: note: in expansion of macro ‘DEBUG_CATEGORY_ALL’ |
||||
64 | debug_categories = DEBUG_CATEGORY_ALL; |
||||
| ^~~~~~~~~~~~~~~~~~ |
||||
../build-aux/test-driver: строка 112: 1203433 Ошибка сегментирования (образ памяти сброшен на диск) "$@" >> "$log_file" 2>&1 |
||||
make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 |
||||
make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 |
||||
make[1]: *** [Makefile:1495: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
test_ll_queue.c:20: warning: "DEBUG_CATEGORY_LL_QUEUE" redefined |
||||
20 | #define DEBUG_CATEGORY_LL_QUEUE 1 |
||||
| |
||||
In file included from test_ll_queue.c:16: |
||||
../lib/debug_config.h:32: note: this is the location of the previous definition |
||||
32 | #define DEBUG_CATEGORY_LL_QUEUE ((debug_category_t)1 << 1) // ll_queue module |
||||
| |
||||
../build-aux/test-driver: строка 112: 1203954 Ошибка сегментирования (образ памяти сброшен на диск) "$@" >> "$log_file" 2>&1 |
||||
make[3]: *** [Makefile:1223: test-suite.log] Ошибка 1 |
||||
make[2]: *** [Makefile:1331: check-TESTS] Ошибка 2 |
||||
make[1]: *** [Makefile:1495: check-am] Ошибка 2 |
||||
make: *** [Makefile:375: check-recursive] Ошибка 1 |
||||
make: *** Нет правила для сборки цели «test_ll_queue». Останов. |
||||
make: *** Нет правила для сборки цели «test_ll_queue». Останов. |
||||
make: *** Нет правила для сборки цели «test_ll_queue». Останов. |
||||
/usr/bin/ld: невозможно найти ../lib/libuasync.a: Нет такого файла или каталога |
||||
collect2: error: ld returned 1 exit status |
||||
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o: в функции «_start»: |
||||
(.text+0x1b): undefined reference to `main' |
||||
collect2: error: ld returned 1 exit status |
||||
timeout: не удалось выполнить команду «./test_ll_queue»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_ll_queue»: Нет такого файла или каталога |
||||
timeout: не удалось выполнить команду «./test_ll_queue»: Нет такого файла или каталога |
||||
/usr/bin/ld: невозможно найти ../lib/libuasync.a: Нет такого файла или каталога |
||||
collect2: error: ld returned 1 exit status |
||||
Из gdb backtrace видно, что падение происходит в u_async.c:247 в функции process_timeouts при вызове node->callback(node->arg). Это проблема с таймерами uasync, а не с памятью. |
||||
@ -0,0 +1,155 @@
|
||||
// timeout_heap.c |
||||
|
||||
#include "timeout_heap.h" |
||||
#include "debug_config.h" |
||||
#include <stdlib.h> |
||||
#include <stdio.h> // For potential error printing, optional |
||||
|
||||
// Helper macros for 1-based indices |
||||
#define PARENT(i) ((i) / 2) |
||||
#define LEFT_CHILD(i) (2 * (i)) |
||||
#define RIGHT_CHILD(i) (2 * (i) + 1) |
||||
|
||||
TimeoutHeap *timeout_heap_create(size_t initial_capacity) { |
||||
TimeoutHeap *h = malloc(sizeof(TimeoutHeap)); |
||||
if (!h) return NULL; |
||||
h->heap = malloc(sizeof(TimeoutEntry) * initial_capacity); |
||||
if (!h->heap) { |
||||
free(h); |
||||
return NULL; |
||||
} |
||||
h->size = 0; |
||||
h->capacity = initial_capacity; |
||||
h->freed_count = 0; |
||||
h->user_data = NULL; |
||||
h->free_callback = NULL; |
||||
return h; |
||||
} |
||||
|
||||
|
||||
void timeout_heap_destroy(TimeoutHeap *h) { |
||||
if (!h) return; |
||||
|
||||
// Safely destroy all timer data atomically |
||||
// This prevents double-free by handling destruction consistently |
||||
while (h->size > 0) { |
||||
TimeoutEntry entry; |
||||
if (timeout_heap_pop(h, &entry) == 0) { |
||||
// Don't free data here - let the caller handle it consistently |
||||
// Just remove from heap to prevent double references |
||||
} |
||||
} |
||||
|
||||
free(h->heap); |
||||
free(h); |
||||
} |
||||
static void bubble_up(TimeoutHeap *h, size_t i) { |
||||
// i is 1-based |
||||
while (i > 1 && h->heap[PARENT(i) - 1].expiration > h->heap[i - 1].expiration) { |
||||
// Swap with parent |
||||
TimeoutEntry temp = h->heap[PARENT(i) - 1]; |
||||
h->heap[PARENT(i) - 1] = h->heap[i - 1]; |
||||
h->heap[i - 1] = temp; |
||||
i = PARENT(i); |
||||
} |
||||
} |
||||
|
||||
int timeout_heap_push(TimeoutHeap *h, TimeoutTime expiration, void *data) { |
||||
if (h->size == h->capacity) { |
||||
size_t new_cap = h->capacity ? h->capacity * 2 : 1; |
||||
TimeoutEntry *new_heap = realloc(h->heap, sizeof(TimeoutEntry) * new_cap); |
||||
if (!new_heap) return -1; // Allocation failed |
||||
h->heap = new_heap; |
||||
h->capacity = new_cap; |
||||
} |
||||
|
||||
// Insert at end (0-based) |
||||
size_t idx = h->size++; |
||||
h->heap[idx].expiration = expiration; |
||||
h->heap[idx].data = data; |
||||
h->heap[idx].deleted = 0; |
||||
|
||||
// Bubble up (1-based) |
||||
bubble_up(h, idx + 1); |
||||
return 0; |
||||
} |
||||
|
||||
static void heapify_down(TimeoutHeap *h, size_t i) { |
||||
// i is 1-based |
||||
while (1) { |
||||
size_t smallest = i; |
||||
size_t left = LEFT_CHILD(i); |
||||
size_t right = RIGHT_CHILD(i); |
||||
|
||||
if (left <= h->size && h->heap[left - 1].expiration < h->heap[smallest - 1].expiration) { |
||||
smallest = left; |
||||
} |
||||
if (right <= h->size && h->heap[right - 1].expiration < h->heap[smallest - 1].expiration) { |
||||
smallest = right; |
||||
} |
||||
if (smallest == i) break; |
||||
|
||||
// Swap |
||||
TimeoutEntry temp = h->heap[smallest - 1]; |
||||
h->heap[smallest - 1] = h->heap[i - 1]; |
||||
h->heap[i - 1] = temp; |
||||
i = smallest; |
||||
} |
||||
} |
||||
|
||||
static void remove_root(TimeoutHeap *h) { |
||||
if (h->size == 0) return; |
||||
|
||||
// Move last to root |
||||
h->heap[0] = h->heap[--h->size]; |
||||
|
||||
// Heapify down (1-based) |
||||
if (h->size > 0) { |
||||
heapify_down(h, 1); |
||||
} |
||||
} |
||||
|
||||
int timeout_heap_peek(TimeoutHeap *h, TimeoutEntry *out) { |
||||
if (h->size == 0) return -1; |
||||
|
||||
// Skip deleted |
||||
size_t i = 0; |
||||
while (i < h->size && h->heap[0].deleted) { |
||||
remove_root(h); |
||||
} |
||||
if (h->size == 0) return -1; |
||||
|
||||
*out = h->heap[0]; |
||||
return 0; |
||||
} |
||||
|
||||
int timeout_heap_pop(TimeoutHeap *h, TimeoutEntry *out) { |
||||
if (h->size == 0) return -1; |
||||
|
||||
// Skip deleted and free their data |
||||
while (h->size > 0 && h->heap[0].deleted) { |
||||
// Just remove from heap, do not free data (to avoid double-free during destruction) |
||||
remove_root(h); |
||||
} |
||||
if (h->size == 0) return -1; |
||||
|
||||
*out = h->heap[0]; |
||||
remove_root(h); |
||||
return 0; |
||||
} |
||||
|
||||
int timeout_heap_cancel(TimeoutHeap *h, TimeoutTime expiration, void *data) { |
||||
for (size_t i = 0; i < h->size; ++i) { |
||||
if (h->heap[i].expiration == expiration && h->heap[i].data == data) { |
||||
h->heap[i].deleted = 1; |
||||
return 0; |
||||
} |
||||
} |
||||
return -1; // Not found |
||||
} |
||||
|
||||
void timeout_heap_set_free_callback(TimeoutHeap *h, void* user_data, void (*callback)(void* user_data, void* data)) { |
||||
if (!h) return; |
||||
h->user_data = user_data; |
||||
h->free_callback = callback; |
||||
} |
||||
@ -0,0 +1,793 @@
|
||||
// uasync.c |
||||
|
||||
#include "u_async.h" |
||||
#include "debug_config.h" |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include <unistd.h> |
||||
#include <errno.h> |
||||
#include <poll.h> |
||||
#include <limits.h> |
||||
#include <fcntl.h> |
||||
|
||||
|
||||
|
||||
// Timeout node with safe cancellation |
||||
struct timeout_node { |
||||
void* arg; |
||||
timeout_callback_t callback; |
||||
uint64_t expiration_ms; // absolute expiration time in milliseconds |
||||
struct UASYNC* ua; // Pointer back to uasync instance for counter updates |
||||
int cancelled; // Cancellation flag |
||||
}; |
||||
|
||||
// Socket node with array-based storage |
||||
struct socket_node { |
||||
int fd; |
||||
socket_callback_t read_cbk; |
||||
socket_callback_t write_cbk; |
||||
socket_callback_t except_cbk; |
||||
void* user_data; |
||||
int active; // 1 if socket is active, 0 if freed (for reuse) |
||||
}; |
||||
|
||||
// Array-based socket management for O(1) operations |
||||
struct socket_array { |
||||
struct socket_node* sockets; // Dynamic array of socket nodes |
||||
int* fd_to_index; // FD to array index mapping |
||||
int* index_to_fd; // Array index to FD mapping |
||||
int capacity; // Total allocated capacity |
||||
int count; // Number of active sockets |
||||
int max_fd; // Maximum FD for bounds checking |
||||
}; |
||||
|
||||
static struct socket_array* socket_array_create(int initial_capacity); |
||||
static void socket_array_destroy(struct socket_array* sa); |
||||
static int socket_array_add(struct socket_array* sa, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data); |
||||
static int socket_array_remove(struct socket_array* sa, int fd); |
||||
static struct socket_node* socket_array_get(struct socket_array* sa, int fd); |
||||
|
||||
// No global instance - each module must use its own struct UASYNC instance |
||||
|
||||
// Array-based socket management implementation |
||||
static struct socket_array* socket_array_create(int initial_capacity) { |
||||
if (initial_capacity < 4) initial_capacity = 4; // Minimum capacity |
||||
|
||||
struct socket_array* sa = malloc(sizeof(struct socket_array)); |
||||
if (!sa) return NULL; |
||||
|
||||
sa->sockets = calloc(initial_capacity, sizeof(struct socket_node)); |
||||
sa->fd_to_index = calloc(initial_capacity, sizeof(int)); |
||||
sa->index_to_fd = calloc(initial_capacity, sizeof(int)); |
||||
|
||||
if (!sa->sockets || !sa->fd_to_index || !sa->index_to_fd) { |
||||
free(sa->sockets); |
||||
free(sa->fd_to_index); |
||||
free(sa->index_to_fd); |
||||
free(sa); |
||||
return NULL; |
||||
} |
||||
|
||||
// Initialize mapping arrays to -1 (invalid) |
||||
for (int i = 0; i < initial_capacity; i++) { |
||||
sa->fd_to_index[i] = -1; |
||||
sa->index_to_fd[i] = -1; |
||||
sa->sockets[i].fd = -1; |
||||
sa->sockets[i].active = 0; |
||||
} |
||||
|
||||
sa->capacity = initial_capacity; |
||||
sa->count = 0; |
||||
sa->max_fd = -1; |
||||
|
||||
return sa; |
||||
} |
||||
|
||||
static void socket_array_destroy(struct socket_array* sa) { |
||||
if (!sa) return; |
||||
|
||||
free(sa->sockets); |
||||
free(sa->fd_to_index); |
||||
free(sa->index_to_fd); |
||||
free(sa); |
||||
} |
||||
|
||||
static int socket_array_add(struct socket_array* sa, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) { |
||||
if (!sa || fd < 0 || fd >= FD_SETSIZE) return -1; |
||||
if (fd >= sa->capacity) { |
||||
// Need to resize - double the capacity |
||||
int new_capacity = sa->capacity * 2; |
||||
if (fd >= new_capacity) new_capacity = fd + 16; // Ensure enough space |
||||
|
||||
struct socket_node* new_sockets = realloc(sa->sockets, new_capacity * sizeof(struct socket_node)); |
||||
int* new_fd_to_index = realloc(sa->fd_to_index, new_capacity * sizeof(int)); |
||||
int* new_index_to_fd = realloc(sa->index_to_fd, new_capacity * sizeof(int)); |
||||
|
||||
if (!new_sockets || !new_fd_to_index || !new_index_to_fd) { |
||||
// Allocation failed |
||||
free(new_sockets); |
||||
free(new_fd_to_index); |
||||
free(new_index_to_fd); |
||||
return -1; |
||||
} |
||||
|
||||
// Initialize new elements |
||||
for (int i = sa->capacity; i < new_capacity; i++) { |
||||
new_fd_to_index[i] = -1; |
||||
new_index_to_fd[i] = -1; |
||||
new_sockets[i].fd = -1; |
||||
new_sockets[i].active = 0; |
||||
} |
||||
|
||||
sa->sockets = new_sockets; |
||||
sa->fd_to_index = new_fd_to_index; |
||||
sa->index_to_fd = new_index_to_fd; |
||||
sa->capacity = new_capacity; |
||||
} |
||||
|
||||
// Check if FD already exists |
||||
if (sa->fd_to_index[fd] != -1) return -1; // FD already exists |
||||
|
||||
// Find first free slot |
||||
int index = -1; |
||||
for (int i = 0; i < sa->capacity; i++) { |
||||
if (!sa->sockets[i].active) { |
||||
index = i; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (index == -1) return -1; // No free slots (shouldn't happen) |
||||
|
||||
// Add the socket |
||||
sa->sockets[index].fd = fd; |
||||
sa->sockets[index].read_cbk = read_cbk; |
||||
sa->sockets[index].write_cbk = write_cbk; |
||||
sa->sockets[index].except_cbk = except_cbk; |
||||
sa->sockets[index].user_data = user_data; |
||||
sa->sockets[index].active = 1; |
||||
|
||||
sa->fd_to_index[fd] = index; |
||||
sa->index_to_fd[index] = fd; |
||||
sa->count++; |
||||
|
||||
if (fd > sa->max_fd) sa->max_fd = fd; |
||||
|
||||
return index; |
||||
} |
||||
|
||||
static int socket_array_remove(struct socket_array* sa, int fd) { |
||||
if (!sa || fd < 0 || fd >= sa->capacity) return -1; |
||||
|
||||
int index = sa->fd_to_index[fd]; |
||||
if (index == -1 || !sa->sockets[index].active) return -1; // FD not found |
||||
|
||||
// Mark as inactive |
||||
sa->sockets[index].active = 0; |
||||
sa->sockets[index].fd = -1; |
||||
sa->fd_to_index[fd] = -1; |
||||
sa->index_to_fd[index] = -1; |
||||
sa->count--; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static struct socket_node* socket_array_get(struct socket_array* sa, int fd) { |
||||
if (!sa || fd < 0 || fd >= sa->capacity) return NULL; |
||||
|
||||
int index = sa->fd_to_index[fd]; |
||||
if (index == -1 || !sa->sockets[index].active) return NULL; |
||||
|
||||
return &sa->sockets[index]; |
||||
} |
||||
|
||||
// Callback to free timeout node and update counters |
||||
static void timeout_node_free_callback(void* user_data, void* data) { |
||||
struct UASYNC* ua = (struct UASYNC*)user_data; |
||||
struct timeout_node* node = (struct timeout_node*)data; |
||||
(void)node; // Not used directly, but keep for consistency |
||||
ua->timer_free_count++; |
||||
free(data); |
||||
} |
||||
|
||||
// Helper to get current time |
||||
static void get_current_time(struct timeval* tv) { |
||||
gettimeofday(tv, NULL); |
||||
} |
||||
|
||||
|
||||
|
||||
// Drain wakeup pipe - read all available bytes |
||||
static void drain_wakeup_pipe(struct UASYNC* ua) { |
||||
if (!ua || !ua->wakeup_initialized) return; |
||||
|
||||
char buf[64]; |
||||
while (1) { |
||||
ssize_t n = read(ua->wakeup_pipe[0], buf, sizeof(buf)); |
||||
if (n <= 0) break; |
||||
} |
||||
} |
||||
|
||||
// Helper to add timeval: tv += dt (timebase units) |
||||
static void timeval_add_tb(struct timeval* tv, int dt) { |
||||
tv->tv_usec += (dt % 10000) * 100; |
||||
tv->tv_sec += dt / 10000 + tv->tv_usec / 1000000; |
||||
tv->tv_usec %= 1000000; |
||||
} |
||||
|
||||
// Convert timeval to milliseconds (uint64_t) |
||||
static uint64_t timeval_to_ms(const struct timeval* tv) { |
||||
return (uint64_t)tv->tv_sec * 1000ULL + (uint64_t)tv->tv_usec / 1000ULL; |
||||
} |
||||
|
||||
|
||||
|
||||
// Simplified timeout handling without reference counting |
||||
|
||||
// Process expired timeouts with safe cancellation |
||||
static void process_timeouts(struct UASYNC* ua) { |
||||
if (!ua || !ua->timeout_heap) return; |
||||
|
||||
struct timeval now_tv; |
||||
get_current_time(&now_tv); |
||||
uint64_t now_ms = timeval_to_ms(&now_tv); |
||||
|
||||
while (1) { |
||||
TimeoutEntry entry; |
||||
if (timeout_heap_peek(ua->timeout_heap, &entry) != 0) break; |
||||
if (entry.expiration > now_ms) break; |
||||
|
||||
// Pop the expired timeout |
||||
timeout_heap_pop(ua->timeout_heap, &entry); |
||||
struct timeout_node* node = (struct timeout_node*)entry.data; |
||||
|
||||
if (node && node->callback && !node->cancelled) { |
||||
// Execute callback only if not cancelled |
||||
node->callback(node->arg); |
||||
} |
||||
|
||||
// Always free the node after processing |
||||
if (node && node->ua) { |
||||
node->ua->timer_free_count++; |
||||
} |
||||
free(node); |
||||
} |
||||
} |
||||
|
||||
// Compute time to next timeout |
||||
static void get_next_timeout(struct UASYNC* ua, struct timeval* tv) { |
||||
if (!ua || !ua->timeout_heap) { |
||||
tv->tv_sec = 0; |
||||
tv->tv_usec = 0; |
||||
return; |
||||
} |
||||
|
||||
TimeoutEntry entry; |
||||
if (timeout_heap_peek(ua->timeout_heap, &entry) != 0) { |
||||
tv->tv_sec = 0; |
||||
tv->tv_usec = 0; |
||||
return; |
||||
} |
||||
|
||||
struct timeval now_tv; |
||||
get_current_time(&now_tv); |
||||
uint64_t now_ms = timeval_to_ms(&now_tv); |
||||
|
||||
if (entry.expiration <= now_ms) { |
||||
tv->tv_sec = 0; |
||||
tv->tv_usec = 0; |
||||
return; |
||||
} |
||||
|
||||
uint64_t delta_ms = entry.expiration - now_ms; |
||||
tv->tv_sec = delta_ms / 1000; |
||||
tv->tv_usec = (delta_ms % 1000) * 1000; |
||||
} |
||||
|
||||
|
||||
|
||||
// Instance version |
||||
void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* arg, timeout_callback_t callback) { |
||||
if (!ua || timeout_tb < 0 || !callback) return NULL; |
||||
if (!ua->timeout_heap) return NULL; |
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: timeout=%d.%d ms, arg=%p, callback=%p", timeout_tb/10, timeout_tb%10, arg, callback); |
||||
|
||||
struct timeout_node* node = malloc(sizeof(struct timeout_node)); |
||||
if (!node) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to allocate node"); |
||||
return NULL; |
||||
} |
||||
ua->timer_alloc_count++; |
||||
|
||||
node->arg = arg; |
||||
node->callback = callback; |
||||
node->ua = ua; |
||||
node->cancelled = 0; |
||||
|
||||
// Calculate expiration time in milliseconds |
||||
struct timeval now; |
||||
get_current_time(&now); |
||||
timeval_add_tb(&now, timeout_tb); |
||||
node->expiration_ms = timeval_to_ms(&now); |
||||
|
||||
// Add to heap |
||||
if (timeout_heap_push(ua->timeout_heap, node->expiration_ms, node) != 0) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_set_timeout: failed to push to heap"); |
||||
free(node); |
||||
ua->timer_free_count++; // Balance the alloc counter |
||||
return NULL; |
||||
} |
||||
|
||||
return node; |
||||
} |
||||
|
||||
|
||||
|
||||
// Instance version |
||||
err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id) { |
||||
if (!ua || !t_id || !ua->timeout_heap) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: invalid parameters ua=%p, t_id=%p, heap=%p", |
||||
ua, t_id, ua ? ua->timeout_heap : NULL); |
||||
return ERR_FAIL; |
||||
} |
||||
|
||||
struct timeout_node* node = (struct timeout_node*)t_id; |
||||
|
||||
// Try to cancel from heap first |
||||
if (timeout_heap_cancel(ua->timeout_heap, node->expiration_ms, node) == 0) { |
||||
// Successfully marked as deleted - free will happen lazily in heap |
||||
node->cancelled = 1; |
||||
node->callback = NULL; |
||||
DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: successfully cancelled timer %p from heap", node); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_cancel_timeout: not found in heap: ua=%p, t_id=%p, node=%p, expires=%llu ms", |
||||
ua, t_id, node, (unsigned long long)node->expiration_ms); |
||||
|
||||
// If not found in heap, it may have already expired or been invalid |
||||
return ERR_FAIL; |
||||
} |
||||
|
||||
|
||||
// Instance version |
||||
void* uasync_add_socket(struct UASYNC* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) { |
||||
if (!ua || fd < 0 || fd >= FD_SETSIZE) return NULL; // Bounds check |
||||
|
||||
int index = socket_array_add(ua->sockets, fd, read_cbk, write_cbk, except_cbk, user_data); |
||||
if (index < 0) return NULL; |
||||
|
||||
ua->socket_alloc_count++; |
||||
|
||||
// Return pointer to the socket node (same as before for API compatibility) |
||||
return &ua->sockets->sockets[index]; |
||||
} |
||||
|
||||
|
||||
|
||||
// Instance version |
||||
err_t uasync_remove_socket(struct UASYNC* ua, void* s_id) { |
||||
if (!ua || !s_id) return ERR_FAIL; |
||||
|
||||
struct socket_node* node = (struct socket_node*)s_id; |
||||
if (node->fd < 0) return ERR_FAIL; // Invalid node |
||||
|
||||
int result = socket_array_remove(ua->sockets, node->fd); |
||||
if (result != 0) return ERR_FAIL; |
||||
|
||||
ua->socket_free_count++; |
||||
return ERR_OK; |
||||
} |
||||
|
||||
|
||||
|
||||
void uasync_mainloop(struct UASYNC* ua) { |
||||
while (1) { |
||||
uasync_poll(ua, -1); /* infinite timeout */ |
||||
} |
||||
} |
||||
|
||||
// Instance version |
||||
void uasync_poll(struct UASYNC* ua, int timeout_tb) { |
||||
if (!ua) return; |
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "async: mainloop: event waiting"); |
||||
|
||||
/* Process expired timeouts */ |
||||
process_timeouts(ua); |
||||
|
||||
/* Compute timeout for poll in milliseconds */ |
||||
int timeout_ms = -1; // infinite by default |
||||
|
||||
// Get next timeout from heap |
||||
struct timeval tv; |
||||
get_next_timeout(ua, &tv); |
||||
|
||||
if (tv.tv_sec > 0 || tv.tv_usec > 0 || (ua->timeout_heap && ua->timeout_heap->size > 0)) { |
||||
// Convert timeval to milliseconds, cap at INT_MAX |
||||
uint64_t ms = (uint64_t)tv.tv_sec * 1000ULL + (uint64_t)tv.tv_usec / 1000ULL; |
||||
if (ms > INT_MAX) ms = INT_MAX; |
||||
timeout_ms = (int)ms; |
||||
} |
||||
|
||||
/* If timeout_tb >= 0, compute timeout as min(timeout_tb, existing timer) */ |
||||
if (timeout_tb >= 0) { |
||||
// Convert timebase (0.1 ms) to milliseconds |
||||
int user_timeout_ms = timeout_tb / 10; |
||||
if (timeout_tb % 10 != 0) user_timeout_ms++; // round up |
||||
|
||||
if (timeout_ms < 0 || user_timeout_ms < timeout_ms) { |
||||
timeout_ms = user_timeout_ms; |
||||
} |
||||
} |
||||
|
||||
/* Build pollfd array from socket array - O(1) per socket */ |
||||
int socket_count = ua->sockets ? ua->sockets->count : 0; |
||||
int wakeup_fd_present = ua->wakeup_initialized ? 1 : 0; |
||||
int total_fds = socket_count + wakeup_fd_present; |
||||
|
||||
if (total_fds == 0) { |
||||
/* No sockets and no wakeup fd, just wait for timeout */ |
||||
if (timeout_ms >= 0) { |
||||
/* usleep would be better but we just call poll with empty set */ |
||||
poll(NULL, 0, timeout_ms); |
||||
} else { |
||||
/* Infinite timeout with no sockets - should not happen in practice */ |
||||
return; |
||||
} |
||||
/* Check timeouts again after sleep */ |
||||
process_timeouts(ua); |
||||
return; |
||||
} |
||||
|
||||
struct pollfd* fds = malloc(total_fds * sizeof(struct pollfd)); |
||||
struct socket_node** nodes = NULL; |
||||
if (socket_count > 0) { |
||||
nodes = malloc(socket_count * sizeof(struct socket_node*)); |
||||
} |
||||
if (!fds || (socket_count > 0 && !nodes)) { |
||||
free(fds); |
||||
free(nodes); |
||||
return; /* out of memory */ |
||||
} |
||||
|
||||
/* Fill arrays */ |
||||
int idx = 0; |
||||
|
||||
/* Add wakeup fd first if present */ |
||||
if (wakeup_fd_present) { |
||||
fds[idx].fd = ua->wakeup_pipe[0]; |
||||
fds[idx].events = POLLIN; |
||||
fds[idx].revents = 0; |
||||
idx++; |
||||
} |
||||
|
||||
/* Add socket fds using efficient array traversal */ |
||||
int node_idx = 0; |
||||
for (int i = 0; i < ua->sockets->capacity && node_idx < socket_count; i++) { |
||||
if (ua->sockets->sockets[i].active) { |
||||
struct socket_node* cur = &ua->sockets->sockets[i]; |
||||
fds[idx].fd = cur->fd; |
||||
fds[idx].events = 0; |
||||
fds[idx].revents = 0; |
||||
|
||||
if (cur->read_cbk) fds[idx].events |= POLLIN; |
||||
if (cur->write_cbk) fds[idx].events |= POLLOUT; |
||||
if (cur->except_cbk) fds[idx].events |= POLLPRI; |
||||
|
||||
if (nodes) { |
||||
nodes[node_idx] = cur; |
||||
} |
||||
idx++; |
||||
node_idx++; |
||||
} |
||||
} |
||||
|
||||
/* Call poll */ |
||||
int ret = poll(fds, total_fds, timeout_ms); |
||||
if (ret < 0) { |
||||
if (errno == EINTR) { |
||||
free(fds); |
||||
free(nodes); |
||||
return; |
||||
} |
||||
perror("poll"); |
||||
free(fds); |
||||
free(nodes); |
||||
return; |
||||
} |
||||
|
||||
/* Process timeouts that may have expired during poll */ |
||||
process_timeouts(ua); |
||||
|
||||
/* Process socket events */ |
||||
if (ret > 0) { |
||||
for (int i = 0; i < total_fds; i++) { |
||||
if (fds[i].revents == 0) continue; |
||||
|
||||
/* Handle wakeup fd separately */ |
||||
if (wakeup_fd_present && i == 0) { |
||||
if (fds[i].revents & POLLIN) { |
||||
drain_wakeup_pipe(ua); |
||||
} |
||||
continue; |
||||
} |
||||
|
||||
/* Socket event */ |
||||
int socket_idx = i - wakeup_fd_present; |
||||
struct socket_node* node = nodes[socket_idx]; |
||||
|
||||
/* Check for error conditions first */ |
||||
if (fds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { |
||||
/* Treat as exceptional condition */ |
||||
if (node->except_cbk) { |
||||
node->except_cbk(node->fd, node->user_data); |
||||
} |
||||
} |
||||
|
||||
/* Exceptional data (out-of-band) */ |
||||
if (fds[i].revents & POLLPRI) { |
||||
if (node->except_cbk) { |
||||
node->except_cbk(node->fd, node->user_data); |
||||
} |
||||
} |
||||
|
||||
/* Read readiness */ |
||||
if (fds[i].revents & POLLIN) { |
||||
if (node->read_cbk) { |
||||
node->read_cbk(node->fd, node->user_data); |
||||
} |
||||
} |
||||
|
||||
/* Write readiness */ |
||||
if (fds[i].revents & POLLOUT) { |
||||
if (node->write_cbk) { |
||||
node->write_cbk(node->fd, node->user_data); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
free(fds); |
||||
free(nodes); |
||||
} |
||||
|
||||
|
||||
|
||||
// ========== Instance management functions ========== |
||||
|
||||
struct UASYNC* uasync_create(void) { |
||||
|
||||
struct UASYNC* ua = malloc(sizeof(struct UASYNC)); |
||||
if (!ua) return NULL; |
||||
|
||||
memset(ua, 0, sizeof(struct UASYNC)); |
||||
ua->wakeup_pipe[0] = -1; |
||||
ua->wakeup_pipe[1] = -1; |
||||
ua->wakeup_initialized = 0; |
||||
|
||||
// Create wakeup pipe |
||||
if (pipe(ua->wakeup_pipe) < 0) { |
||||
DEBUG_WARN(DEBUG_CATEGORY_UASYNC, "Failed to create wakeup pipe: %s", strerror(errno)); |
||||
// Continue without wakeup mechanism |
||||
ua->wakeup_pipe[0] = -1; |
||||
ua->wakeup_pipe[1] = -1; |
||||
} else { |
||||
ua->wakeup_initialized = 1; |
||||
// Set non-blocking on read end to avoid blocking if pipe is full |
||||
int flags = fcntl(ua->wakeup_pipe[0], F_GETFL, 0); |
||||
if (flags >= 0) { |
||||
fcntl(ua->wakeup_pipe[0], F_SETFL, flags | O_NONBLOCK); |
||||
} |
||||
} |
||||
|
||||
ua->sockets = socket_array_create(16); |
||||
if (!ua->sockets) { |
||||
if (ua->wakeup_initialized) { |
||||
close(ua->wakeup_pipe[0]); |
||||
close(ua->wakeup_pipe[1]); |
||||
} |
||||
free(ua); |
||||
return NULL; |
||||
} |
||||
|
||||
ua->timeout_heap = timeout_heap_create(16); |
||||
if (!ua->timeout_heap) { |
||||
socket_array_destroy(ua->sockets); |
||||
if (ua->wakeup_initialized) { |
||||
close(ua->wakeup_pipe[0]); |
||||
close(ua->wakeup_pipe[1]); |
||||
} |
||||
free(ua); |
||||
return NULL; |
||||
} |
||||
|
||||
// Set callback to free timeout nodes and update counters |
||||
timeout_heap_set_free_callback(ua->timeout_heap, ua, timeout_node_free_callback); |
||||
|
||||
return ua; |
||||
} |
||||
|
||||
// Print all resources for debugging |
||||
void uasync_print_resources(struct UASYNC* ua, const char* prefix) { |
||||
if (!ua) { |
||||
printf("%s: NULL uasync instance\n", prefix); |
||||
return; |
||||
} |
||||
|
||||
printf("\n🔍 %s: UASYNC Resource Report for %p\n", prefix, ua); |
||||
printf(" Timer Statistics: allocated=%zu, freed=%zu, active=%zd\n", |
||||
ua->timer_alloc_count, ua->timer_free_count, |
||||
(ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); |
||||
printf(" Socket Statistics: allocated=%zu, freed=%zu, active=%zd\n", |
||||
ua->socket_alloc_count, ua->socket_free_count, |
||||
(ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); |
||||
|
||||
// Показать активные таймеры |
||||
if (ua->timeout_heap) { |
||||
size_t active_timers = 0; |
||||
// Безопасное чтение без извлечения - просто итерируем по массиву |
||||
for (size_t i = 0; i < ua->timeout_heap->size; i++) { |
||||
if (!ua->timeout_heap->heap[i].deleted) { |
||||
active_timers++; |
||||
struct timeout_node* node = (struct timeout_node*)ua->timeout_heap->heap[i].data; |
||||
printf(" Timer: node=%p, expires=%llu ms, cancelled=%d\n", |
||||
node, (unsigned long long)ua->timeout_heap->heap[i].expiration, node->cancelled); |
||||
} |
||||
} |
||||
printf(" Active timers in heap: %zu\n", active_timers); |
||||
} |
||||
|
||||
// Показать активные сокеты |
||||
if (ua->sockets) { |
||||
int active_sockets = 0; |
||||
printf(" Socket array capacity: %d, active: %d\n", |
||||
ua->sockets->capacity, ua->sockets->count); |
||||
for (int i = 0; i < ua->sockets->capacity; i++) { |
||||
if (ua->sockets->sockets[i].active) { |
||||
active_sockets++; |
||||
printf(" Socket: fd=%d, active=%d\n", |
||||
ua->sockets->sockets[i].fd, |
||||
ua->sockets->sockets[i].active); |
||||
} |
||||
} |
||||
printf(" Total active sockets: %d\n", active_sockets); |
||||
} |
||||
|
||||
printf("🔚 %s: End of resource report\n\n", prefix); |
||||
} |
||||
|
||||
void uasync_destroy(struct UASYNC* ua, int close_fds) { |
||||
if (!ua) return; |
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: starting cleanup for ua=%p", ua); |
||||
|
||||
// Диагностика ресурсов перед очисткой |
||||
uasync_print_resources(ua, "BEFORE_DESTROY"); |
||||
|
||||
// Check for potential memory leaks |
||||
if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected before cleanup: timers %zu/%zu, sockets %zu/%zu", |
||||
ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); |
||||
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Timer leak: allocated=%zu, freed=%zu, diff=%zd", |
||||
ua->timer_alloc_count, ua->timer_free_count, |
||||
(ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); |
||||
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "Socket leak: allocated=%zu, freed=%zu, diff=%zd", |
||||
ua->socket_alloc_count, ua->socket_free_count, |
||||
(ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); |
||||
// Continue cleanup, will abort after if leaks remain |
||||
} |
||||
|
||||
// Free all remaining timeouts |
||||
if (ua->timeout_heap) { |
||||
size_t freed_count = 0; |
||||
while (1) { |
||||
TimeoutEntry entry; |
||||
if (timeout_heap_pop(ua->timeout_heap, &entry) != 0) break; |
||||
struct timeout_node* node = (struct timeout_node*)entry.data; |
||||
|
||||
// Free all timer nodes (avoid double-free bug) |
||||
if (node) { |
||||
ua->timer_free_count++; |
||||
free(node); |
||||
} |
||||
} |
||||
timeout_heap_destroy(ua->timeout_heap); |
||||
} |
||||
|
||||
// Free all socket nodes using array approach |
||||
if (ua->sockets) { |
||||
// Count and free all active sockets |
||||
int freed_count = 0; |
||||
for (int i = 0; i < ua->sockets->capacity; i++) { |
||||
if (ua->sockets->sockets[i].active) { |
||||
if (close_fds && ua->sockets->sockets[i].fd >= 0) { |
||||
close(ua->sockets->sockets[i].fd); |
||||
} |
||||
ua->socket_free_count++; |
||||
freed_count++; |
||||
} |
||||
} |
||||
DEBUG_DEBUG(DEBUG_CATEGORY_MEMORY, "Freed %d socket nodes in destroy", freed_count); |
||||
socket_array_destroy(ua->sockets); |
||||
} |
||||
|
||||
// Close wakeup pipe |
||||
if (ua->wakeup_initialized) { |
||||
close(ua->wakeup_pipe[0]); |
||||
close(ua->wakeup_pipe[1]); |
||||
} |
||||
|
||||
// Final leak check |
||||
if (ua->timer_alloc_count != ua->timer_free_count || ua->socket_alloc_count != ua->socket_free_count) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Memory leaks detected after cleanup: timers %zu/%zu, sockets %zu/%zu", |
||||
ua->timer_alloc_count, ua->timer_free_count, ua->socket_alloc_count, ua->socket_free_count); |
||||
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Timer leak: allocated=%zu, freed=%zu, diff=%zd", |
||||
ua->timer_alloc_count, ua->timer_free_count, |
||||
(ssize_t)(ua->timer_alloc_count - ua->timer_free_count)); |
||||
DEBUG_ERROR(DEBUG_CATEGORY_TIMERS, "FINAL Socket leak: allocated=%zu, freed=%zu, diff=%zd", |
||||
ua->socket_alloc_count, ua->socket_free_count, |
||||
(ssize_t)(ua->socket_alloc_count - ua->socket_free_count)); |
||||
abort(); |
||||
} |
||||
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_TIMERS, "uasync_destroy: completed successfully for ua=%p", ua); |
||||
free(ua); |
||||
} |
||||
|
||||
void uasync_init_instance(struct UASYNC* ua) { |
||||
if (!ua) return; |
||||
|
||||
// Initialize socket array if not present |
||||
if (!ua->sockets) { |
||||
ua->sockets = socket_array_create(16); |
||||
} |
||||
|
||||
if (!ua->timeout_heap) { |
||||
ua->timeout_heap = timeout_heap_create(16); |
||||
if (ua->timeout_heap) { |
||||
timeout_heap_set_free_callback(ua->timeout_heap, ua, timeout_node_free_callback); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Debug statistics |
||||
void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free) { |
||||
if (!ua) return; |
||||
if (timer_alloc) *timer_alloc = ua->timer_alloc_count; |
||||
if (timer_free) *timer_free = ua->timer_free_count; |
||||
if (socket_alloc) *socket_alloc = ua->socket_alloc_count; |
||||
if (socket_free) *socket_free = ua->socket_free_count; |
||||
} |
||||
|
||||
// Get global instance for backward compatibility |
||||
|
||||
// Wakeup mechanism |
||||
int uasync_wakeup(struct UASYNC* ua) { |
||||
if (!ua || !ua->wakeup_initialized) return -1; |
||||
|
||||
char byte = 0; |
||||
ssize_t ret = write(ua->wakeup_pipe[1], &byte, 1); |
||||
if (ret != 1) { |
||||
// Don't print error from signal handler |
||||
return -1; |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int uasync_get_wakeup_fd(struct UASYNC* ua) { |
||||
if (!ua || !ua->wakeup_initialized) return -1; |
||||
return ua->wakeup_pipe[1]; |
||||
} |
||||
|
||||
/* Lookup socket by file descriptor - returns current pointer even after realloc */ |
||||
int uasync_lookup_socket(struct UASYNC* ua, int fd, void** socket_id) { |
||||
if (!ua || !ua->sockets || !socket_id || fd < 0 || fd >= FD_SETSIZE) { |
||||
return -1; |
||||
} |
||||
|
||||
*socket_id = socket_array_get(ua->sockets, fd); |
||||
return (*socket_id != NULL) ? 0 : -1; |
||||
} |
||||
@ -0,0 +1,282 @@
|
||||
// pkt_normalizer.c - Implementation of packet normalizer for ETCP |
||||
#include "pkt_normalizer.h" |
||||
#include "etcp.h" // For ETCP_CONN and related structures |
||||
#include "ll_queue.h" // For queue operations |
||||
#include "u_async.h" // For UASYNC |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdio.h> // For debugging (can be removed if not needed) |
||||
#include "debug_config.h" // Assuming this for DEBUG_ERROR |
||||
|
||||
// Forward declarations |
||||
static void packer_cb(struct ll_queue* q, void* arg); |
||||
static void pn_flush_cb(void* arg); |
||||
static void etcp_input_ready_cb(struct ll_queue* q, void* arg); |
||||
static void pn_unpacker_cb(struct ll_queue* q, void* arg); |
||||
static void pn_send_to_etcp(struct PKTNORM* pn); |
||||
|
||||
// Initialization |
||||
struct PKTNORM* pn_init(struct ETCP_CONN* etcp) { |
||||
if (!etcp) return NULL; |
||||
|
||||
struct PKTNORM* pn = calloc(1, sizeof(struct PKTNORM)); |
||||
if (!pn) return NULL; |
||||
|
||||
pn->etcp = etcp; |
||||
pn->ua = etcp->instance->ua; |
||||
pn->frag_size = etcp->mtu - 100; // Use MTU as fixed packet size (adjust if headers need subtraction) |
||||
pn->tx_wait_time = 10; |
||||
|
||||
pn->input = queue_new(pn->ua, 0); // No hash needed |
||||
pn->output = queue_new(pn->ua, 0); // No hash needed |
||||
|
||||
if (!pn->input || !pn->output) { |
||||
pn_pair_deinit(pn); |
||||
return NULL; |
||||
} |
||||
|
||||
queue_set_callback(pn->input, packer_cb, pn); |
||||
queue_set_callback(etcp->output_queue, pn_unpacker_cb, pn); |
||||
|
||||
pn->data = NULL; |
||||
pn->recvpart = NULL; |
||||
pn->flush_timer = NULL; |
||||
|
||||
return pn; |
||||
} |
||||
|
||||
// Deinitialization |
||||
void pn_pair_deinit(struct PKTNORM* pn) { |
||||
if (!pn) return; |
||||
|
||||
// Drain and free queues |
||||
if (pn->input) { |
||||
struct ll_entry* entry; |
||||
while ((entry = queue_data_get(pn->input)) != NULL) { |
||||
if (entry->dgram) { |
||||
free(entry->dgram); |
||||
} |
||||
queue_entry_free(entry); |
||||
} |
||||
queue_free(pn->input); |
||||
} |
||||
if (pn->output) { |
||||
struct ll_entry* entry; |
||||
while ((entry = queue_data_get(pn->output)) != NULL) { |
||||
if (entry->dgram) { |
||||
free(entry->dgram); |
||||
} |
||||
queue_entry_free(entry); |
||||
} |
||||
queue_free(pn->output); |
||||
} |
||||
|
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
} |
||||
|
||||
if (pn->data) { |
||||
memory_pool_free(pn->etcp->instance->data_pool, pn->data); |
||||
} |
||||
if (pn->recvpart) { |
||||
queue_dgram_free(pn->recvpart); |
||||
queue_entry_free(pn->recvpart); |
||||
} |
||||
|
||||
free(pn); |
||||
} |
||||
|
||||
// Reset unpacker state |
||||
void pn_unpacker_reset_state(struct PKTNORM* pn) { |
||||
if (!pn) return; |
||||
if (pn->recvpart) { |
||||
queue_dgram_free(pn->recvpart); |
||||
queue_entry_free(pn->recvpart); |
||||
pn->recvpart = NULL; |
||||
} |
||||
} |
||||
|
||||
// Send data to packer (copies and adds to input queue or pending, triggering callback) |
||||
void pn_packer_send(struct PKTNORM* pn, uint8_t* data, uint16_t len) { |
||||
if (!pn || !data || len == 0) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer_send: pn=%p, len=%d", pn, len); |
||||
|
||||
struct ll_entry* entry = ll_alloc_lldgram(len); |
||||
if (!entry) return; |
||||
memcpy(entry->dgram, data, len); |
||||
entry->len = len; |
||||
entry->dgram_pool = NULL; |
||||
|
||||
int ret = queue_data_put(pn->input, entry, 0); |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer_send: queue_data_put returned %d, input count=%d", ret, queue_entry_count(pn->input)); |
||||
|
||||
// Cancel flush timer if active |
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
pn->flush_timer = NULL; |
||||
} |
||||
} |
||||
|
||||
// Internal: Packer callback |
||||
static void packer_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: packer_cb"); |
||||
|
||||
queue_wait_threshold(pn->etcp->input_queue, 0, 0, etcp_input_ready_cb, pn); |
||||
} |
||||
|
||||
// Helper to send block to ETCP as ETCP_FRAGMENT |
||||
static void pn_send_to_etcp(struct PKTNORM* pn) { |
||||
if (!pn || !pn->data || pn->data_ptr == 0) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: pn_send_to_etcp"); |
||||
// Allocate ETCP_FRAGMENT from io_pool |
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(pn->etcp->io_pool); |
||||
if (!frag) {// drop data |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_packer: send to etcp alloc error"); |
||||
pn->alloc_errors++; |
||||
pn->data_ptr = 0; |
||||
return; |
||||
} |
||||
|
||||
frag->seq = 0; |
||||
frag->timestamp = 0; |
||||
frag->ll.dgram = pn->data; |
||||
frag->ll.dgram_pool = pn->etcp->instance->data_pool; |
||||
frag->ll.len = pn->data_ptr; |
||||
frag->ll.memlen = pn->etcp->instance->data_pool->object_size; |
||||
|
||||
queue_data_put(pn->etcp->input_queue, (struct ll_entry*)frag, 0); |
||||
// Сбросить структуру (dgram передан во фрагмент, не освобождаем) |
||||
pn->data = NULL; |
||||
} |
||||
|
||||
// Internal: Renew sndpart buffer |
||||
static void pn_buf_renew(struct PKTNORM* pn) { |
||||
if (pn->data) { |
||||
int remain = pn->data_size - pn->data_ptr; |
||||
if (remain < 3) pn_send_to_etcp(pn); |
||||
} |
||||
if (!pn->data) { |
||||
pn->data = memory_pool_alloc(pn->etcp->instance->data_pool); |
||||
int size=pn->etcp->instance->data_pool->object_size; |
||||
if (size>pn->frag_size) size=pn->frag_size; |
||||
pn->data_size = size; |
||||
pn->data_ptr=0; |
||||
} |
||||
} |
||||
|
||||
// Internal: Process input when etcp->input_queue is ready (empty) |
||||
static void etcp_input_ready_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: etcp_input_ready_cb"); |
||||
|
||||
struct ll_entry* in_dgram = queue_data_get(pn->input); |
||||
if (!in_dgram) { queue_resume_callback(pn->input); return; } |
||||
|
||||
uint16_t in_ptr = 0;// |
||||
while (in_ptr < in_dgram->len) { |
||||
pn_buf_renew(pn); |
||||
if (!pn->data) break; // Allocation failed |
||||
|
||||
int remain = pn->data_size - pn->data_ptr; |
||||
if (remain<3) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_packer: fatal logic error"); |
||||
pn->logic_errors++; |
||||
break; |
||||
} |
||||
|
||||
if (in_ptr == 0) { |
||||
pn->data[pn->data_ptr++] = in_dgram->len & 0xFF; |
||||
pn->data[pn->data_ptr++] = (in_dgram->len >> 8) & 0xFF; |
||||
remain -= 2; |
||||
} |
||||
|
||||
memcpy(pn->data + pn->data_ptr, in_dgram->dgram + in_ptr, remain); |
||||
pn->data_ptr += remain; |
||||
in_ptr += remain; |
||||
|
||||
} |
||||
|
||||
queue_dgram_free(in_dgram); |
||||
queue_entry_free(in_dgram); |
||||
|
||||
// Cancel flush timer if active |
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
pn->flush_timer = NULL; |
||||
} |
||||
|
||||
// Set flush timer if no more input |
||||
if (queue_entry_count(pn->input) == 0) { |
||||
pn->flush_timer = uasync_set_timeout(pn->ua, pn->tx_wait_time, pn, pn_flush_cb); |
||||
} |
||||
|
||||
queue_resume_callback(pn->input); |
||||
} |
||||
|
||||
// Internal: Flush callback on timeout |
||||
static void pn_flush_cb(void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
pn->flush_timer = NULL; |
||||
pn_send_to_etcp(pn); |
||||
} |
||||
|
||||
// Internal: Unpacker callback (assembles fragments into original packets) |
||||
static void pn_unpacker_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
while (1) { |
||||
void* data = queue_data_get(pn->etcp->output_queue); |
||||
if (!data) break; |
||||
|
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)data; // Since data is ll_entry* |
||||
uint8_t* payload = frag->ll.dgram; |
||||
uint16_t len = frag->ll.len; |
||||
uint16_t ptr = 0; |
||||
|
||||
while (ptr < len) { |
||||
if (!pn->recvpart) { |
||||
// Need length header for new packet |
||||
if (len - ptr < 2) { |
||||
// Incomplete header, reset |
||||
pn_unpacker_reset_state(pn); |
||||
break; |
||||
} |
||||
uint16_t pkt_len = payload[ptr] | (payload[ptr + 1] << 8); |
||||
ptr += 2; |
||||
|
||||
pn->recvpart = ll_alloc_lldgram(pkt_len); |
||||
if (!pn->recvpart) { |
||||
break; |
||||
} |
||||
pn->recvpart->len = 0; |
||||
} |
||||
|
||||
uint16_t rem = pn->recvpart->memlen - pn->recvpart->len; |
||||
uint16_t avail = len - ptr; |
||||
uint16_t cp = (rem < avail) ? rem : avail; |
||||
memcpy(pn->recvpart->dgram + pn->recvpart->len, payload + ptr, cp); |
||||
pn->recvpart->len += cp; |
||||
ptr += cp; |
||||
|
||||
if (pn->recvpart->len == pn->recvpart->memlen) { |
||||
queue_data_put(pn->output, pn->recvpart, 0); |
||||
pn->recvpart = NULL; |
||||
} |
||||
} |
||||
|
||||
// Free the fragment using ll_queue API |
||||
queue_dgram_free((struct ll_entry*)frag); |
||||
queue_entry_free((struct ll_entry*)frag); |
||||
} |
||||
|
||||
queue_resume_callback(q); |
||||
} |
||||
@ -0,0 +1,313 @@
|
||||
// pkt_normalizer.c - Implementation of packet normalizer for ETCP |
||||
#include "pkt_normalizer.h" |
||||
#include "etcp.h" // For ETCP_CONN and related structures |
||||
#include "ll_queue.h" // For queue operations |
||||
#include "u_async.h" // For UASYNC |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdio.h> // For debugging (can be removed if not needed) |
||||
#include "debug_config.h" // Assuming this for DEBUG_ERROR |
||||
|
||||
// Forward declarations |
||||
static void packer_cb(struct ll_queue* q, void* arg); |
||||
static void pn_flush_cb(void* arg); |
||||
static void etcp_input_ready_cb(struct ll_queue* q, void* arg); |
||||
static void pn_unpacker_cb(struct ll_queue* q, void* arg); |
||||
static void pn_send_to_etcp(struct PKTNORM* pn); |
||||
|
||||
// Initialization |
||||
struct PKTNORM* pn_init(struct ETCP_CONN* etcp) { |
||||
if (!etcp) return NULL; |
||||
|
||||
struct PKTNORM* pn = calloc(1, sizeof(struct PKTNORM)); |
||||
if (!pn) return NULL; |
||||
|
||||
pn->etcp = etcp; |
||||
pn->ua = etcp->instance->ua; |
||||
pn->frag_size = etcp->mtu - 100; // Use MTU as fixed packet size (adjust if headers need subtraction) |
||||
pn->tx_wait_time = 10; |
||||
|
||||
pn->input = queue_new(pn->ua, 0); // No hash needed |
||||
pn->output = queue_new(pn->ua, 0); // No hash needed |
||||
|
||||
if (!pn->input || !pn->output) { |
||||
pn_pair_deinit(pn); |
||||
return NULL; |
||||
} |
||||
|
||||
queue_set_callback(pn->input, packer_cb, pn); |
||||
queue_set_callback(etcp->output_queue, pn_unpacker_cb, pn); |
||||
|
||||
pn->data = NULL; |
||||
pn->recvpart = NULL; |
||||
pn->recvpart_rem = 0; |
||||
pn->flush_timer = NULL; |
||||
|
||||
return pn; |
||||
} |
||||
|
||||
// Deinitialization |
||||
void pn_pair_deinit(struct PKTNORM* pn) { |
||||
if (!pn) return; |
||||
|
||||
// Drain and free queues |
||||
if (pn->input) { |
||||
struct ll_entry* entry; |
||||
while ((entry = queue_data_get(pn->input)) != NULL) { |
||||
if (entry->dgram) { |
||||
free(entry->dgram); |
||||
} |
||||
queue_entry_free(entry); |
||||
} |
||||
queue_free(pn->input); |
||||
} |
||||
if (pn->output) { |
||||
struct ll_entry* entry; |
||||
while ((entry = queue_data_get(pn->output)) != NULL) { |
||||
if (entry->dgram) { |
||||
free(entry->dgram); |
||||
} |
||||
queue_entry_free(entry); |
||||
} |
||||
queue_free(pn->output); |
||||
} |
||||
|
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
} |
||||
|
||||
if (pn->data) { |
||||
memory_pool_free(pn->etcp->instance->data_pool, pn->data); |
||||
} |
||||
if (pn->recvpart) { |
||||
queue_dgram_free(pn->recvpart); |
||||
queue_entry_free(pn->recvpart); |
||||
} |
||||
|
||||
free(pn); |
||||
} |
||||
|
||||
// Reset unpacker state |
||||
void pn_unpacker_reset_state(struct PKTNORM* pn) { |
||||
if (!pn) return; |
||||
if (pn->recvpart) { |
||||
queue_dgram_free(pn->recvpart); |
||||
queue_entry_free(pn->recvpart); |
||||
pn->recvpart = NULL; |
||||
} |
||||
pn->recvpart_rem = 0; |
||||
} |
||||
|
||||
// Send data to packer (copies and adds to input queue or pending, triggering callback) |
||||
void pn_packer_send(struct PKTNORM* pn, uint8_t* data, uint16_t len) { |
||||
if (!pn || !data || len == 0) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer_send: pn=%p, len=%d", pn, len); |
||||
|
||||
struct ll_entry* entry = ll_alloc_lldgram(len); |
||||
if (!entry) return; |
||||
memcpy(entry->dgram, data, len); |
||||
entry->len = len; |
||||
entry->dgram_pool = NULL; |
||||
|
||||
int ret = queue_data_put(pn->input, entry, 0); |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer_send: queue_data_put returned %d, input count=%d", ret, queue_entry_count(pn->input)); |
||||
|
||||
// Cancel flush timer if active |
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
pn->flush_timer = NULL; |
||||
} |
||||
} |
||||
|
||||
// Internal: Packer callback |
||||
static void packer_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: packer_cb"); |
||||
|
||||
queue_wait_threshold(pn->etcp->input_queue, 0, 0, etcp_input_ready_cb, pn); |
||||
} |
||||
|
||||
// Helper to send block to ETCP as ETCP_FRAGMENT |
||||
static void pn_send_to_etcp(struct PKTNORM* pn) { |
||||
if (!pn || !pn->data || pn->data_ptr == 0) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: pn_send_to_etcp, len=%d", pn->data_ptr); |
||||
// Allocate ETCP_FRAGMENT from io_pool |
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(pn->etcp->io_pool); |
||||
if (!frag) {// drop data |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_packer: send to etcp alloc error"); |
||||
pn->alloc_errors++; |
||||
pn->data_ptr = 0; |
||||
return; |
||||
} |
||||
|
||||
frag->seq = 0; |
||||
frag->timestamp = 0; |
||||
frag->ll.dgram = pn->data; |
||||
frag->ll.dgram_pool = pn->etcp->instance->data_pool; |
||||
frag->ll.len = pn->data_ptr; |
||||
frag->ll.memlen = pn->etcp->instance->data_pool->object_size; |
||||
|
||||
queue_data_put(pn->etcp->input_queue, (struct ll_entry*)frag, 0); |
||||
// Сбросить структуру (dgram передан во фрагмент, не освобождаем) |
||||
pn->data = NULL; |
||||
} |
||||
|
||||
// Internal: Renew sndpart buffer |
||||
static void pn_buf_renew(struct PKTNORM* pn) { |
||||
if (pn->data) { |
||||
int remain = pn->data_size - pn->data_ptr; |
||||
if (remain < 3) pn_send_to_etcp(pn); |
||||
} |
||||
if (!pn->data) { |
||||
pn->data = memory_pool_alloc(pn->etcp->instance->data_pool); |
||||
int size=pn->etcp->instance->data_pool->object_size; |
||||
if (size>pn->frag_size) size=pn->frag_size; |
||||
pn->data_size = size; |
||||
pn->data_ptr=0; |
||||
} |
||||
} |
||||
|
||||
// Internal: Process input when etcp->input_queue is ready (empty) |
||||
static void etcp_input_ready_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: etcp_input_ready_cb"); |
||||
|
||||
struct ll_entry* in_dgram = queue_data_get(pn->input); |
||||
if (!in_dgram) { queue_resume_callback(pn->input); return; } |
||||
|
||||
uint16_t in_ptr = 0;// |
||||
while (in_ptr < in_dgram->len) { |
||||
pn_buf_renew(pn); |
||||
if (!pn->data) break; // Allocation failed |
||||
|
||||
int remain = pn->data_size - pn->data_ptr; |
||||
if (remain<3) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_packer: fatal logic error"); |
||||
pn->logic_errors++; |
||||
break; |
||||
} |
||||
|
||||
if (in_ptr == 0) { |
||||
pn->data[pn->data_ptr++] = in_dgram->len & 0xFF; |
||||
pn->data[pn->data_ptr++] = (in_dgram->len >> 8) & 0xFF; |
||||
remain -= 2; |
||||
} |
||||
|
||||
memcpy(pn->data + pn->data_ptr, in_dgram->dgram + in_ptr, remain); |
||||
pn->data_ptr += remain; |
||||
in_ptr += remain; |
||||
|
||||
} |
||||
|
||||
queue_dgram_free(in_dgram); |
||||
queue_entry_free(in_dgram); |
||||
|
||||
// Cancel flush timer if active |
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
pn->flush_timer = NULL; |
||||
} |
||||
|
||||
// Set flush timer if no more input |
||||
if (queue_entry_count(pn->input) == 0) { |
||||
pn->flush_timer = uasync_set_timeout(pn->ua, pn->tx_wait_time, pn, pn_flush_cb); |
||||
} |
||||
|
||||
queue_resume_callback(pn->input); |
||||
} |
||||
|
||||
// Internal: Flush callback on timeout |
||||
static void pn_flush_cb(void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
pn->flush_timer = NULL; |
||||
pn_send_to_etcp(pn); |
||||
} |
||||
|
||||
// Internal: Unpacker callback (assembles fragments into original packets) |
||||
static void pn_unpacker_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
while (1) { |
||||
void* data = queue_data_get(pn->etcp->output_queue); |
||||
if (!data) break; |
||||
|
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)data; |
||||
uint8_t* payload = frag->ll.dgram; |
||||
uint16_t len = frag->ll.len; |
||||
uint16_t ptr = 0; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_unpacker: frag len=%d, recvpart=%p, recvpart_rem=%d", |
||||
len, (void*)pn->recvpart, pn->recvpart_rem); |
||||
|
||||
while (ptr < len) { |
||||
// Если ждем заголовок нового пакета (recvpart == NULL и recvpart_rem == 0) |
||||
if (!pn->recvpart && pn->recvpart_rem == 0) { |
||||
// Читаем длину пакета из первых 2 байт |
||||
if (len - ptr < 2) { |
||||
// Неполный заголовок - сбрасываем |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_unpacker: incomplete header, len=%d, ptr=%d", len, ptr); |
||||
pn_unpacker_reset_state(pn); |
||||
break; |
||||
} |
||||
uint16_t pkt_len = payload[ptr] | (payload[ptr + 1] << 8); |
||||
ptr += 2; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_unpacker: new packet, pkt_len=%d", pkt_len); |
||||
|
||||
if (pkt_len == 0) { |
||||
// Пустой пакет - пропускаем |
||||
continue; |
||||
} |
||||
|
||||
// Проверка на максимальный размер пакета (10KB + 2 байта заголовка) |
||||
if (pkt_len > 10002) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_unpacker: invalid pkt_len=%d, resetting", pkt_len); |
||||
pn_unpacker_reset_state(pn); |
||||
break; |
||||
} |
||||
|
||||
pn->recvpart = ll_alloc_lldgram(pkt_len); |
||||
if (!pn->recvpart) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_unpacker: failed to alloc recvpart"); |
||||
break; |
||||
} |
||||
pn->recvpart->len = 0; |
||||
pn->recvpart_rem = pkt_len; // Сколько байт осталось собрать |
||||
} |
||||
|
||||
// Копируем данные в recvpart |
||||
uint16_t rem = pn->recvpart_rem; |
||||
uint16_t avail = len - ptr; |
||||
uint16_t cp = (rem < avail) ? rem : avail; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_unpacker: copying cp=%d bytes (rem=%d, avail=%d)", cp, rem, avail); |
||||
|
||||
memcpy(pn->recvpart->dgram + pn->recvpart->len, payload + ptr, cp); |
||||
pn->recvpart->len += cp; |
||||
pn->recvpart_rem -= cp; |
||||
ptr += cp; |
||||
|
||||
// Если пакет полностью собран |
||||
if (pn->recvpart_rem == 0) { |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_unpacker: packet complete, len=%d", pn->recvpart->len); |
||||
queue_data_put(pn->output, pn->recvpart, 0); |
||||
pn->recvpart = NULL; |
||||
// Следующий фрагмент будет начинаться с заголовка нового пакета |
||||
} |
||||
} |
||||
|
||||
// Free the fragment using ll_queue API |
||||
queue_dgram_free((struct ll_entry*)frag); |
||||
queue_entry_free((struct ll_entry*)frag); |
||||
} |
||||
|
||||
queue_resume_callback(q); |
||||
} |
||||
@ -0,0 +1,288 @@
|
||||
// pkt_normalizer.c - Implementation of packet normalizer for ETCP |
||||
#include "pkt_normalizer.h" |
||||
#include "etcp.h" // For ETCP_CONN and related structures |
||||
#include "ll_queue.h" // For queue operations |
||||
#include "u_async.h" // For UASYNC |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdio.h> // For debugging (can be removed if not needed) |
||||
#include "debug_config.h" // Assuming this for DEBUG_ERROR |
||||
|
||||
// Forward declarations |
||||
static void packer_cb(struct ll_queue* q, void* arg); |
||||
static void pn_flush_cb(void* arg); |
||||
static void etcp_input_ready_cb(struct ll_queue* q, void* arg); |
||||
static void pn_unpacker_cb(struct ll_queue* q, void* arg); |
||||
static void pn_send_to_etcp(struct PKTNORM* pn); |
||||
|
||||
// Initialization |
||||
struct PKTNORM* pn_init(struct ETCP_CONN* etcp) { |
||||
if (!etcp) return NULL; |
||||
|
||||
struct PKTNORM* pn = calloc(1, sizeof(struct PKTNORM)); |
||||
if (!pn) return NULL; |
||||
|
||||
pn->etcp = etcp; |
||||
pn->ua = etcp->instance->ua; |
||||
pn->frag_size = etcp->mtu - 100; // Use MTU as fixed packet size (adjust if headers need subtraction) |
||||
pn->tx_wait_time = 10; |
||||
|
||||
pn->input = queue_new(pn->ua, 0); // No hash needed |
||||
pn->output = queue_new(pn->ua, 0); // No hash needed |
||||
|
||||
if (!pn->input || !pn->output) { |
||||
pn_pair_deinit(pn); |
||||
return NULL; |
||||
} |
||||
|
||||
queue_set_callback(pn->input, packer_cb, pn); |
||||
queue_set_callback(etcp->output_queue, pn_unpacker_cb, pn); |
||||
|
||||
pn->data = NULL; |
||||
pn->recvpart = NULL; |
||||
pn->flush_timer = NULL; |
||||
|
||||
return pn; |
||||
} |
||||
|
||||
// Deinitialization |
||||
void pn_pair_deinit(struct PKTNORM* pn) { |
||||
if (!pn) return; |
||||
|
||||
// Drain and free queues |
||||
if (pn->input) { |
||||
struct ll_entry* entry; |
||||
while ((entry = queue_data_get(pn->input)) != NULL) { |
||||
if (entry->dgram) { |
||||
free(entry->dgram); |
||||
} |
||||
queue_entry_free(entry); |
||||
} |
||||
queue_free(pn->input); |
||||
} |
||||
if (pn->output) { |
||||
struct ll_entry* entry; |
||||
while ((entry = queue_data_get(pn->output)) != NULL) { |
||||
if (entry->dgram) { |
||||
free(entry->dgram); |
||||
} |
||||
queue_entry_free(entry); |
||||
} |
||||
queue_free(pn->output); |
||||
} |
||||
|
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
} |
||||
|
||||
if (pn->data) { |
||||
memory_pool_free(pn->etcp->instance->data_pool, pn->data); |
||||
} |
||||
if (pn->recvpart) { |
||||
queue_dgram_free(pn->recvpart); |
||||
queue_entry_free(pn->recvpart); |
||||
} |
||||
|
||||
free(pn); |
||||
} |
||||
|
||||
// Reset unpacker state |
||||
void pn_unpacker_reset_state(struct PKTNORM* pn) { |
||||
if (!pn) return; |
||||
if (pn->recvpart) { |
||||
queue_dgram_free(pn->recvpart); |
||||
queue_entry_free(pn->recvpart); |
||||
pn->recvpart = NULL; |
||||
} |
||||
} |
||||
|
||||
// Send data to packer (copies and adds to input queue or pending, triggering callback) |
||||
void pn_packer_send(struct PKTNORM* pn, uint8_t* data, uint16_t len) { |
||||
if (!pn || !data || len == 0) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer_send: pn=%p, len=%d", pn, len); |
||||
|
||||
struct ll_entry* entry = ll_alloc_lldgram(len); |
||||
if (!entry) return; |
||||
memcpy(entry->dgram, data, len); |
||||
entry->len = len; |
||||
entry->dgram_pool = NULL; |
||||
|
||||
int ret = queue_data_put(pn->input, entry, 0); |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer_send: queue_data_put returned %d, input count=%d", ret, queue_entry_count(pn->input)); |
||||
|
||||
// Cancel flush timer if active |
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
pn->flush_timer = NULL; |
||||
} |
||||
} |
||||
|
||||
// Internal: Packer callback |
||||
static void packer_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: packer_cb"); |
||||
|
||||
queue_wait_threshold(pn->etcp->input_queue, 0, 0, etcp_input_ready_cb, pn); |
||||
} |
||||
|
||||
// Helper to send block to ETCP as ETCP_FRAGMENT |
||||
static void pn_send_to_etcp(struct PKTNORM* pn) { |
||||
if (!pn || !pn->data || pn->data_ptr == 0) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: pn_send_to_etcp"); |
||||
// Allocate ETCP_FRAGMENT from io_pool |
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(pn->etcp->io_pool); |
||||
if (!frag) {// drop data |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_packer: send to etcp alloc error"); |
||||
pn->alloc_errors++; |
||||
pn->data_ptr = 0; |
||||
return; |
||||
} |
||||
|
||||
frag->seq = 0; |
||||
frag->timestamp = 0; |
||||
frag->ll.dgram = pn->data; |
||||
frag->ll.len = pn->data_ptr; |
||||
frag->ll.memlen = pn->etcp->instance->data_pool->object_size; |
||||
|
||||
queue_data_put(pn->etcp->input_queue, (struct ll_entry*)frag, 0); |
||||
// Сбросить структуру (dgram передан во фрагмент, не освобождаем) |
||||
pn->data = NULL; |
||||
} |
||||
|
||||
// Internal: Renew sndpart buffer |
||||
static void pn_buf_renew(struct PKTNORM* pn) { |
||||
if (pn->data) { |
||||
int remain = pn->data_size - pn->data_ptr; |
||||
if (remain < 3) pn_send_to_etcp(pn); |
||||
} |
||||
if (!pn->data) { |
||||
pn->data = memory_pool_alloc(pn->etcp->instance->data_pool); |
||||
int size=pn->etcp->instance->data_pool->object_size; |
||||
if (size>pn->frag_size) size=pn->frag_size; |
||||
pn->data_size = size; |
||||
pn->data_ptr=0; |
||||
} |
||||
} |
||||
|
||||
// Internal: Process input when etcp->input_queue is ready (empty) |
||||
static void etcp_input_ready_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: etcp_input_ready_cb"); |
||||
|
||||
struct ll_entry* in_dgram = queue_data_get(pn->input); |
||||
if (!in_dgram) { queue_resume_callback(pn->input); return; } |
||||
|
||||
uint16_t in_ptr = 0;// |
||||
while (in_ptr < in_dgram->len) { |
||||
pn_buf_renew(pn); |
||||
if (!pn->data) break; // Allocation failed |
||||
|
||||
int remain = pn->data_size - pn->data_ptr; |
||||
if (remain<3) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_packer: fatal logic error"); |
||||
pn->logic_errors++; |
||||
break; |
||||
} |
||||
|
||||
if (in_ptr == 0) { |
||||
pn->data[pn->data_ptr++] = in_dgram->len & 0xFF; |
||||
pn->data[pn->data_ptr++] = (in_dgram->len >> 8) & 0xFF; |
||||
remain -= 2; |
||||
} |
||||
|
||||
memcpy(pn->data + pn->data_ptr, in_dgram->dgram + in_ptr, remain); |
||||
pn->data_ptr += remain; |
||||
in_ptr += remain; |
||||
|
||||
} |
||||
|
||||
queue_dgram_free(in_dgram); |
||||
queue_entry_free(in_dgram); |
||||
|
||||
// Cancel flush timer if active |
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
pn->flush_timer = NULL; |
||||
} |
||||
|
||||
// Set flush timer if no more input |
||||
if (queue_entry_count(pn->input) == 0) { |
||||
pn->flush_timer = uasync_set_timeout(pn->ua, pn->tx_wait_time, pn, pn_flush_cb); |
||||
} |
||||
|
||||
queue_resume_callback(pn->input); |
||||
} |
||||
|
||||
// Internal: Flush callback on timeout |
||||
static void pn_flush_cb(void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
pn->flush_timer = NULL; |
||||
pn_send_to_etcp(pn); |
||||
} |
||||
|
||||
// Internal: Unpacker callback (assembles fragments into original packets) |
||||
static void pn_unpacker_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
while (1) { |
||||
void* data = queue_data_get(pn->etcp->output_queue); |
||||
if (!data) break; |
||||
|
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)data; // Since data is ll_entry* |
||||
uint8_t* payload = frag->ll.dgram; |
||||
uint16_t len = frag->ll.len; |
||||
uint16_t ptr = 0; |
||||
|
||||
while (ptr < len) { |
||||
if (!pn->recvpart) { |
||||
// Need length header for new packet |
||||
if (len - ptr < 2) { |
||||
// Incomplete header, reset |
||||
pn_unpacker_reset_state(pn); |
||||
break; |
||||
} |
||||
uint16_t pkt_len = payload[ptr] | (payload[ptr + 1] << 8); |
||||
ptr += 2; |
||||
|
||||
pn->recvpart = ll_alloc_lldgram(pkt_len); |
||||
if (!pn->recvpart) { |
||||
break; |
||||
} |
||||
pn->recvpart->len = 0; |
||||
} else { |
||||
// We are in the middle of assembling a packet |
||||
// Skip the 2-byte length header at the start of subsequent fragments |
||||
if (ptr == 0) { |
||||
ptr += 2; |
||||
if (ptr >= len) break; // Fragment contains only the length header |
||||
} |
||||
} |
||||
|
||||
uint16_t rem = pn->recvpart->memlen - pn->recvpart->len; |
||||
uint16_t avail = len - ptr; |
||||
uint16_t cp = (rem < avail) ? rem : avail; |
||||
memcpy(pn->recvpart->dgram + pn->recvpart->len, payload + ptr, cp); |
||||
pn->recvpart->len += cp; |
||||
ptr += cp; |
||||
|
||||
if (pn->recvpart->len == pn->recvpart->memlen) { |
||||
queue_data_put(pn->output, pn->recvpart, 0); |
||||
pn->recvpart = NULL; |
||||
} |
||||
} |
||||
|
||||
// Free the fragment - dgram was malloc'd in pn_send_to_etcp |
||||
memory_pool_free(pn->etcp->instance->data_pool, frag->ll.dgram); |
||||
memory_pool_free(pn->etcp->io_pool, frag); |
||||
} |
||||
|
||||
queue_resume_callback(q); |
||||
} |
||||
@ -0,0 +1,295 @@
|
||||
// pkt_normalizer.c - Implementation of packet normalizer for ETCP |
||||
#include "pkt_normalizer.h" |
||||
#include "etcp.h" // For ETCP_CONN and related structures |
||||
#include "ll_queue.h" // For queue operations |
||||
#include "u_async.h" // For UASYNC |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdio.h> // For debugging (can be removed if not needed) |
||||
#include "debug_config.h" // Assuming this for DEBUG_ERROR |
||||
|
||||
// Forward declarations |
||||
static void packer_cb(struct ll_queue* q, void* arg); |
||||
static void pn_flush_cb(void* arg); |
||||
static void etcp_input_ready_cb(struct ll_queue* q, void* arg); |
||||
static void pn_unpacker_cb(struct ll_queue* q, void* arg); |
||||
static void pn_send_to_etcp(struct PKTNORM* pn); |
||||
|
||||
// Initialization |
||||
struct PKTNORM* pn_init(struct ETCP_CONN* etcp) { |
||||
if (!etcp) return NULL; |
||||
|
||||
struct PKTNORM* pn = calloc(1, sizeof(struct PKTNORM)); |
||||
if (!pn) return NULL; |
||||
|
||||
pn->etcp = etcp; |
||||
pn->ua = etcp->instance->ua; |
||||
pn->frag_size = etcp->mtu - 100; // Use MTU as fixed packet size (adjust if needed) |
||||
pn->tx_wait_time = 10; |
||||
|
||||
pn->input = queue_new(pn->ua, 0); // No hash needed |
||||
pn->output = queue_new(pn->ua, 0); // No hash needed |
||||
|
||||
if (!pn->input || !pn->output) { |
||||
pn_pair_deinit(pn); |
||||
return NULL; |
||||
} |
||||
|
||||
queue_set_callback(pn->input, packer_cb, pn); |
||||
queue_set_callback(etcp->output_queue, pn_unpacker_cb, pn); |
||||
|
||||
pn->data = NULL; |
||||
pn->recvpart = NULL; |
||||
pn->recvpart_rem = 0; |
||||
pn->flush_timer = NULL; |
||||
|
||||
return pn; |
||||
} |
||||
|
||||
// Deinitialization |
||||
void pn_pair_deinit(struct PKTNORM* pn) { |
||||
if (!pn) return; |
||||
|
||||
// Drain and free queues |
||||
if (pn->input) { |
||||
struct ll_entry* entry; |
||||
while ((entry = queue_data_get(pn->input)) != NULL) { |
||||
if (entry->dgram) { |
||||
free(entry->dgram); |
||||
} |
||||
queue_entry_free(entry); |
||||
} |
||||
queue_free(pn->input); |
||||
} |
||||
if (pn->output) { |
||||
struct ll_entry* entry; |
||||
while ((entry = queue_data_get(pn->output)) != NULL) { |
||||
if (entry->dgram) { |
||||
free(entry->dgram); |
||||
} |
||||
queue_entry_free(entry); |
||||
} |
||||
queue_free(pn->output); |
||||
} |
||||
|
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
} |
||||
|
||||
if (pn->data) { |
||||
memory_pool_free(pn->etcp->instance->data_pool, pn->data); |
||||
} |
||||
if (pn->recvpart) { |
||||
queue_dgram_free(pn->recvpart); |
||||
queue_entry_free(pn->recvpart); |
||||
} |
||||
|
||||
free(pn); |
||||
} |
||||
|
||||
// Reset unpacker state |
||||
void pn_unpacker_reset_state(struct PKTNORM* pn) { |
||||
if (!pn) return; |
||||
if (pn->recvpart) { |
||||
queue_dgram_free(pn->recvpart); |
||||
queue_entry_free(pn->recvpart); |
||||
pn->recvpart = NULL; |
||||
} |
||||
pn->recvpart_rem = 0; |
||||
} |
||||
|
||||
// Send data to packer (copies and adds to input queue or pending, triggering callback) |
||||
void pn_packer_send(struct PKTNORM* pn, uint8_t* data, uint16_t len) { |
||||
if (!pn || !data || len == 0) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer_send: pn=%p, len=%d", pn, len); |
||||
|
||||
struct ll_entry* entry = ll_alloc_lldgram(len); |
||||
if (!entry) return; |
||||
memcpy(entry->dgram, data, len); |
||||
entry->len = len; |
||||
entry->dgram_pool = NULL; |
||||
|
||||
int ret = queue_data_put(pn->input, entry, 0); |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer_send: queue_data_put returned %d, input count=%d", ret, queue_entry_count(pn->input)); |
||||
|
||||
// Cancel flush timer if active |
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
pn->flush_timer = NULL; |
||||
} |
||||
} |
||||
|
||||
// Internal: Packer callback |
||||
static void packer_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: packer_cb"); |
||||
|
||||
queue_wait_threshold(pn->etcp->input_queue, 0, 0, etcp_input_ready_cb, pn); |
||||
} |
||||
|
||||
// Helper to send block to ETCP as ETCP_FRAGMENT |
||||
static void pn_send_to_etcp(struct PKTNORM* pn) { |
||||
if (!pn || !pn->data || pn->data_ptr == 0) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: pn_send_to_etcp"); |
||||
// Allocate ETCP_FRAGMENT from io_pool |
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)queue_entry_new_from_pool(pn->etcp->io_pool); |
||||
if (!frag) {// drop data |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_packer: send to etcp alloc error"); |
||||
pn->alloc_errors++; |
||||
pn->data_ptr = 0; |
||||
return; |
||||
} |
||||
|
||||
frag->seq = 0; |
||||
frag->timestamp = 0; |
||||
frag->ll.dgram = pn->data; |
||||
frag->ll.dgram_pool = pn->etcp->instance->data_pool; |
||||
frag->ll.len = pn->data_ptr; |
||||
frag->ll.memlen = pn->etcp->instance->data_pool->object_size; |
||||
|
||||
queue_data_put(pn->etcp->input_queue, (struct ll_entry*)frag, 0); |
||||
// Сбросить структуру (dgram передан во фрагмент, не освобождаем) |
||||
pn->data = NULL; |
||||
} |
||||
|
||||
// Internal: Renew buffer for packer |
||||
static void pn_buf_renew(struct PKTNORM* pn) { |
||||
if (pn->data && pn->data_ptr > 0) { |
||||
pn_send_to_etcp(pn); |
||||
} |
||||
if (!pn->data) { |
||||
pn->data = memory_pool_alloc(pn->etcp->instance->data_pool); |
||||
size_t size = pn->etcp->instance->data_pool->object_size; |
||||
if (size > pn->frag_size) size = pn->frag_size; |
||||
pn->data_size = size; |
||||
pn->data_ptr = 0; |
||||
} |
||||
} |
||||
|
||||
// Internal: Process input when etcp->input_queue is ready (empty) |
||||
static void etcp_input_ready_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_packer: etcp_input_ready_cb"); |
||||
|
||||
struct ll_entry* in_dgram = queue_data_get(pn->input); |
||||
if (!in_dgram) { queue_resume_callback(pn->input); return; } |
||||
|
||||
uint16_t in_ptr = 0; |
||||
while (in_ptr < in_dgram->len) { |
||||
pn_buf_renew(pn); |
||||
if (!pn->data) break; // Allocation failed |
||||
|
||||
int remain = pn->data_size - pn->data_ptr; |
||||
if (remain < 3) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_packer: fatal logic error"); |
||||
pn->logic_errors++; |
||||
break; |
||||
} |
||||
|
||||
if (in_ptr == 0) { |
||||
pn->data[pn->data_ptr++] = in_dgram->len & 0xFF; |
||||
pn->data[pn->data_ptr++] = (in_dgram->len >> 8) & 0xFF; |
||||
remain -= 2; |
||||
} |
||||
|
||||
memcpy(pn->data + pn->data_ptr, in_dgram->dgram + in_ptr, remain); |
||||
pn->data_ptr += remain; |
||||
in_ptr += remain; |
||||
} |
||||
|
||||
queue_dgram_free(in_dgram); |
||||
queue_entry_free(in_dgram); |
||||
|
||||
// Cancel flush timer if active |
||||
if (pn->flush_timer) { |
||||
uasync_cancel_timeout(pn->ua, pn->flush_timer); |
||||
pn->flush_timer = NULL; |
||||
} |
||||
|
||||
// Set flush timer if no more input |
||||
if (queue_entry_count(pn->input) == 0) { |
||||
pn->flush_timer = uasync_set_timeout(pn->ua, pn->tx_wait_time, pn, pn_flush_cb); |
||||
} |
||||
|
||||
queue_resume_callback(pn->input); |
||||
} |
||||
|
||||
// Internal: Flush callback on timeout |
||||
static void pn_flush_cb(void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
pn->flush_timer = NULL; |
||||
pn_send_to_etcp(pn); |
||||
} |
||||
|
||||
// Internal: Unpacker callback (assembles fragments into original packets) |
||||
static void pn_unpacker_cb(struct ll_queue* q, void* arg) { |
||||
struct PKTNORM* pn = (struct PKTNORM*)arg; |
||||
if (!pn) return; |
||||
|
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)queue_data_get(pn->etcp->output_queue); |
||||
if (!frag) break; |
||||
uint8_t* payload = frag->ll.dgram; |
||||
uint16_t len = frag->ll.len; |
||||
uint16_t ptr = 0; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_unpacker: frag len=%d, recvpart=%p, recvpart_rem=%d", |
||||
len, (void*)pn->recvpart, pn->recvpart_rem); |
||||
|
||||
while (ptr < len) { |
||||
|
||||
if (pn->recvpart_rem==0) { |
||||
if (len - ptr < 2) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_unpacker: incomplete header, len=%d, ptr=%d", len, ptr); |
||||
pn_unpacker_reset_state(pn); |
||||
break; |
||||
} |
||||
uint16_t pkt_len = payload[ptr] | (payload[ptr + 1] << 8); |
||||
ptr += 2; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_unpacker: new packet, pkt_len=%d", pkt_len); |
||||
|
||||
if (pkt_len == 0) { |
||||
// Пустой пакет - пропускаем |
||||
continue; |
||||
} |
||||
|
||||
pn->recvpart = ll_alloc_lldgram(pkt_len); |
||||
if (!pn->recvpart) { |
||||
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "pn_unpacker: failed to alloc recvpart"); |
||||
} |
||||
pn->recvpart->len = 0; |
||||
pn->recvpart_rem = pkt_len; // Сколько байт осталось собрать |
||||
} |
||||
|
||||
// Копируем данные в recvpart |
||||
uint16_t rem = pn->recvpart_rem; |
||||
uint16_t avail = len - ptr; |
||||
uint16_t cp = (rem < avail) ? rem : avail; |
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_unpacker: copying cp=%d bytes (rem=%d, avail=%d)", cp, rem, avail); |
||||
|
||||
if (pn->recvpart) memcpy(pn->recvpart->dgram + pn->recvpart->len, payload + ptr, cp); |
||||
pn->recvpart->len += cp; |
||||
pn->recvpart_rem -= cp; |
||||
ptr += cp; |
||||
|
||||
// Если пакет полностью собран |
||||
if (pn->recvpart_rem == 0) { |
||||
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "pn_unpacker: packet complete, len=%d", pn->recvpart->len); |
||||
if (pn->recvpart) queue_data_put(pn->output, pn->recvpart, 0); |
||||
} |
||||
} |
||||
|
||||
// Free the fragment using ll_queue API |
||||
queue_dgram_free(&frag->ll); |
||||
queue_entry_free(&frag->ll); |
||||
|
||||
queue_resume_callback(q); |
||||
} |
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,326 @@
|
||||
// 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/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" |
||||
|
||||
// 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; |
||||
|
||||
// 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 one fragment from input_queue to output_queue
|
||||
static void loopback_callback(struct ll_queue* q, void* arg) { |
||||
(void)arg; |
||||
|
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)queue_data_get(q); |
||||
if (frag) { |
||||
// 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 = 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); |
||||
free(buffer); |
||||
packets_sent++; |
||||
} |
||||
|
||||
// Force flush any pending data in packer buffer
|
||||
pn_flush(pn); |
||||
|
||||
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; |
||||
|
||||
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
|
||||
static void monitor(void* arg) { |
||||
(void)arg; |
||||
|
||||
if (test_completed) return; |
||||
|
||||
// Trigger loopback processing manually
|
||||
// In real ETCP, this would be triggered by the queue callback
|
||||
if (mock_etcp.input_queue) { |
||||
void* frag_data; |
||||
while ((frag_data = queue_data_get(mock_etcp.input_queue)) != NULL) { |
||||
struct ETCP_FRAGMENT* frag = (struct ETCP_FRAGMENT*)frag_data; |
||||
// Move fragment from input to output (loopback)
|
||||
if (mock_etcp.output_queue) { |
||||
queue_data_put(mock_etcp.output_queue, (struct ll_entry*)frag, 0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Check received packets
|
||||
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("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); |
||||
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); |
||||
mock_etcp.output_queue = queue_new(mock_instance.ua, 0); |
||||
|
||||
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_pair_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); |
||||
|
||||
// Initialize UASYNC
|
||||
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; |
||||
} |
||||
|
||||
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); |
||||
usleep(5000); |
||||
} |
||||
|
||||
// Main loop
|
||||
int elapsed = 0; |
||||
while (!test_completed && elapsed < TEST_TIMEOUT_MS + 1000) { |
||||
uasync_poll(mock_instance.ua, 5); |
||||
usleep(5000); |
||||
elapsed += 5; |
||||
} |
||||
|
||||
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); |
||||
return 1; |
||||
} |
||||
} |
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue