Browse Source

Сохранить текущее состояние перед исправлением компиляционных ошибок

v2_dev
Evgeny 8 months ago
parent
commit
3f7fd9f8ad
  1. 5
      1
  2. 30
      AGENTS.md
  3. 2
      build.sh
  4. 153
      build/config.h
  5. 323
      build/config.log
  6. 1200
      build/config.status
  7. 1
      build/lib/tinycrypt
  8. 1
      build/stamp-h1
  9. 293
      build_errors.txt
  10. 3
      configure.ac
  11. 6
      lib/Makefile.am
  12. 46
      lib/ll_queue.c
  13. 15
      lib/ll_queue.h
  14. 87
      lib/memory_pool.c
  15. 23
      lib/memory_pool.h
  16. 55
      lib/u_async.c
  17. 40
      lib/u_async.h
  18. 53
      log
  19. 2
      src/Makefile.am
  20. 442
      src/config_parser.c
  21. 78
      src/config_parser.h
  22. 17
      src/config_updater.c
  23. 5
      src/config_updater.h
  24. 470
      src/etcp.c
  25. 105
      src/etcp.h
  26. 5
      src/etcp.h.demo
  27. 1024
      src/etcp_connections.c
  28. 100
      src/etcp_connections.h
  29. 16
      src/etcp_protocol.txt
  30. 31
      src/etcp_sockets.h
  31. 62
      src/memory_pool.c
  32. 36
      src/memory_pool.h
  33. 76
      src/packet_buffer.h
  34. 85
      src/packet_pool.c
  35. 35
      src/packet_pool.h
  36. 45
      src/secure_channel.c
  37. 28
      src/secure_channel.h
  38. 79
      src/tun_if.c
  39. 2
      src/tun_if.h
  40. 28
      src/utun.c
  41. 52
      src/utun_fixes.c
  42. 208
      src/utun_instance.c
  43. 22
      src/utun_instance.h
  44. 19
      test_invalid_keys.conf
  45. 16
      test_no_keys.conf
  46. BIN
      tests/test_etcp_crypto
  47. 213
      tests/test_etcp_crypto.c
  48. BIN
      tests/test_etcp_crypto_fix
  49. 219
      tests/test_etcp_crypto_fix.c
  50. 42
      utun_test_backup.conf

5
1

@ -0,0 +1,5 @@
In file included from src/etcp_connections.c:1:
src/etcp_connections.h:6:10: fatal error: etcp_sockets.h: Нет такого файла или каталога
6 | #include "etcp_sockets.h"
| ^~~~~~~~~~~~~~~~
compilation terminated.

30
AGENTS.md

@ -1,30 +0,0 @@
# AGENTS.md - uTun Development Guide
This document provides essential information for agentic coding assistants working on the uTun VPN tunnel project.
Совместимость со станым при доработках сохранять не надо. при переделке старые хвосты надо удалять.
Если не работает - добавляй отладочную информацию чтобы быстрее найти проблемное место и не рушить работающий код.
если отладочная информация будет флудить сделай ее отключаемой или подумай как ограничить ее вывод по возможности сохраняя информативность.
Добавляй в код структуры для статистики (например суммируй число ошибок, вызовов и других потенциально нужных для анализа при ошибках метрик).
добавляй в тест подсчет времени сколько каждый этап длился для лучшего понимания оптимизации
перед запуском тестов не забывай их пересобирать
веди changelog.txt: дата время: что поменялось
задача протокола - обеспечивать минимальную задержку при большом трафике. т.е. держать необходимый минимум данных в очередях.
дизайн - однопоточный асинхронный.
все ожидающие операции - через сокеты(дескрипторы) и таймауты -> u_async
все очереди через ll_queue
главный модуль обслуживания подключения - connection
unit-tests: для каждого теста в начале файла теста описывай что и как он тестирует - кратко но указыая на нюансы если они есть.
Если код не собирается и в этом месте написано #указание <текст> - то это указание надо выполнить в приоритетном порядке.
не используй shell для редактирования - много ошибок допускаешь. есть встроенные инструменты.
## Code Style Guidelines
комменты в коде на русском
используй по возможности структуры а не typedef, пример: struct STRUCT_NAME {...}; - прототипы структур ЗАГЛАВНЫМИ БУКВАМИ
Приоритет - простое (логически понятное, минималистичное api, но функциональное). Всегда надо думать как сделать проще и красивее. не надо нагромождать лишнего - сперва подумай действительно ли оно нужно.
рассуждай на английском, остальное лучше на русском (отчёты, дальшейшие шаги и ответы на запросы всегда на русском)

2
build.sh

@ -0,0 +1,2 @@
#!/bin/bash
make 2> build_errors.txt

153
build/config.h

@ -0,0 +1,153 @@
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the <arpa/inet.h> header file. */
#define HAVE_ARPA_INET_H 1
/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define to 1 if you have the `gettimeofday' function. */
#define HAVE_GETTIMEOFDAY 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `crypto' library (-lcrypto). */
#define HAVE_LIBCRYPTO 1
/* Define to 1 if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* Define to 1 if your system has a GNU libc compatible `malloc' function, and
to 0 otherwise. */
#define HAVE_MALLOC 1
/* Define to 1 if you have the `memset' function. */
#define HAVE_MEMSET 1
/* Define to 1 if you have the <netinet/in.h> header file. */
#define HAVE_NETINET_IN_H 1
/* Define to 1 if you have the `socket' function. */
#define HAVE_SOCKET 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the `strchr' function. */
#define HAVE_STRCHR 1
/* Define to 1 if you have the `strdup' function. */
#define HAVE_STRDUP 1
/* Define to 1 if you have the `strerror' function. */
#define HAVE_STRERROR 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the `strstr' function. */
#define HAVE_STRSTR 1
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#define HAVE_SYS_IOCTL_H 1
/* Define to 1 if you have the <sys/socket.h> header file. */
#define HAVE_SYS_SOCKET_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define if TUN/TAP is available */
/* #undef HAVE_TUN_TAP */
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Name of package */
#define PACKAGE "utun"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "https://github.com/anomalyco/utun3/issues"
/* Define to the full name of this package. */
#define PACKAGE_NAME "utun"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "utun 2.0.0"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "utun"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "2.0.0"
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "2.0.0"
/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
/* #undef _UINT32_T */
/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
/* #undef _UINT64_T */
/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
<pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
/* #undef _UINT8_T */
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
/* #undef inline */
#endif
/* Define to rpl_malloc if the replacement function should be used. */
/* #undef malloc */
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */
/* Define to `int' if <sys/types.h> does not define. */
/* #undef ssize_t */
/* Define to the type of an unsigned integer type of width exactly 16 bits if
such a type exists and the standard includes do not define it. */
/* #undef uint16_t */
/* Define to the type of an unsigned integer type of width exactly 32 bits if
such a type exists and the standard includes do not define it. */
/* #undef uint32_t */
/* Define to the type of an unsigned integer type of width exactly 64 bits if
such a type exists and the standard includes do not define it. */
/* #undef uint64_t */
/* Define to the type of an unsigned integer type of width exactly 8 bits if
such a type exists and the standard includes do not define it. */
/* #undef uint8_t */

323
build/config.log

@ -0,0 +1,323 @@
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by utun configure 2.0.0, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ ../configure --no-create --no-recursion
## --------- ##
## Platform. ##
## --------- ##
hostname = user-Standard-PC-i440FX-PIIX-1996
uname -m = x86_64
uname -r = 6.14.0-37-generic
uname -s = Linux
uname -v = #37~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 20 10:25:38 UTC 2
/usr/bin/uname -p = x86_64
/bin/uname -X = unknown
/bin/arch = x86_64
/usr/bin/arch -k = unknown
/usr/convex/getsysinfo = unknown
/usr/bin/hostinfo = unknown
/bin/machine = unknown
/usr/bin/oslevel = unknown
/bin/universe = unknown
PATH: /home/vnc1/.cargo/bin/
PATH: /usr/local/sbin/
PATH: /usr/local/bin/
PATH: /usr/sbin/
PATH: /usr/bin/
PATH: /sbin/
PATH: /bin/
PATH: /usr/games/
PATH: /usr/local/games/
PATH: /snap/bin/
PATH: /snap/bin/
## ----------- ##
## Core tests. ##
## ----------- ##
configure:2453: looking for aux files: config.guess config.sub missing install-sh ar-lib compile
configure:2466: trying ../build-aux/
configure:2495: ../build-aux/config.guess found
configure:2495: ../build-aux/config.sub found
configure:2495: ../build-aux/missing found
configure:2477: ../build-aux/install-sh found
configure:2495: ../build-aux/ar-lib found
configure:2495: ../build-aux/compile found
configure:2675: checking for gcc
configure:2696: found /usr/bin/gcc
configure:2707: result: gcc
configure:3060: checking for C compiler version
configure:3069: gcc --version >&5
gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
Copyright (C) 2023 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
configure:3080: $? = 0
configure:3069: gcc -v >&5
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)
... rest of stderr output deleted ...
configure:3080: $? = 0
configure:3069: gcc -V >&5
gcc: error: unrecognized command-line option '-V'
gcc: fatal error: no input files
compilation terminated.
configure:3080: $? = 1
configure:3069: gcc -qversion >&5
gcc: error: unrecognized command-line option '-qversion'; did you mean '--version'?
gcc: fatal error: no input files
compilation terminated.
configure:3080: $? = 1
configure:3069: gcc -version >&5
gcc: error: unrecognized command-line option '-version'
gcc: fatal error: no input files
compilation terminated.
configure:3080: $? = 1
configure:3100: checking whether the C compiler works
configure:3122: gcc conftest.c >&5
configure:3126: $? = 0
configure:3176: result: yes
configure:3179: checking for C compiler default output file name
configure:3181: result: a.out
configure:3187: checking for suffix of executables
configure:3194: gcc -o conftest conftest.c >&5
configure:3198: $? = 0
configure:3221: result:
configure:3243: checking whether we are cross compiling
configure:3251: gcc -o conftest conftest.c >&5
configure:3255: $? = 0
configure:3262: ./conftest
configure:3266: $? = 0
configure:3281: result: no
configure:3286: checking for suffix of object files
configure:3309: gcc -c conftest.c >&5
configure:3313: $? = 0
configure:3335: result: o
configure:3339: checking whether the compiler supports GNU C
configure:3359: gcc -c conftest.c >&5
configure:3359: $? = 0
configure:3369: result: yes
configure:3380: checking whether gcc accepts -g
configure:3401: gcc -c -g conftest.c >&5
configure:3401: $? = 0
configure:3445: result: yes
configure:3465: checking for gcc option to enable C11 features
configure:3480: gcc -c -g -O2 conftest.c >&5
configure:3480: $? = 0
configure:3498: result: none needed
configure:3619: checking whether gcc understands -c and -o together
configure:3642: gcc -c conftest.c -o conftest2.o
configure:3645: $? = 0
configure:3642: gcc -c conftest.c -o conftest2.o
configure:3645: $? = 0
configure:3657: result: yes
configure:3722: checking for ranlib
configure:3743: found /usr/bin/ranlib
configure:3754: result: ranlib
configure:3832: checking for ar
configure:3853: found /usr/bin/ar
configure:3864: result: ar
configure:3890: checking the archiver (ar) interface
configure:3907: gcc -c -g -O2 conftest.c >&5
configure:3907: $? = 0
configure:3910: ar cru libconftest.a conftest.o >&5
ar: `u' modifier ignored since `D' is the default (see `U')
configure:3913: $? = 0
configure:3941: result: ar
configure:3978: checking for a BSD-compatible install
configure:4051: result: /usr/bin/install -c
configure:4062: checking whether build environment is sane
configure:4117: result: yes
configure:4272: checking for a race-free mkdir -p
configure:4316: result: /usr/bin/mkdir -p
configure:4323: checking for gawk
configure:4358: result: no
configure:4323: checking for mawk
configure:4344: found /usr/bin/mawk
configure:4355: result: mawk
configure:4366: checking whether make sets $(MAKE)
configure:4389: result: yes
configure:4411: checking whether make supports the include directive
configure:4426: make -f confmf.GNU && cat confinc.out
make[1]: Entering directory '/home/vnc1/proj/utun3/build'
make[1]: Leaving directory '/home/vnc1/proj/utun3/build'
this is the am__doit target
configure:4429: $? = 0
configure:4448: result: yes (GNU style)
configure:4483: checking whether make supports nested variables
configure:4501: result: yes
configure:4518: error: source directory already configured; run "make distclean" there first
## ---------------- ##
## Cache variables. ##
## ---------------- ##
ac_cv_c_compiler_gnu=yes
ac_cv_env_CC_set=
ac_cv_env_CC_value=
ac_cv_env_CFLAGS_set=
ac_cv_env_CFLAGS_value=
ac_cv_env_CPPFLAGS_set=
ac_cv_env_CPPFLAGS_value=
ac_cv_env_LDFLAGS_set=
ac_cv_env_LDFLAGS_value=
ac_cv_env_LIBS_set=
ac_cv_env_LIBS_value=
ac_cv_env_build_alias_set=
ac_cv_env_build_alias_value=
ac_cv_env_host_alias_set=
ac_cv_env_host_alias_value=
ac_cv_env_target_alias_set=
ac_cv_env_target_alias_value=
ac_cv_objext=o
ac_cv_path_install='/usr/bin/install -c'
ac_cv_path_mkdir=/usr/bin/mkdir
ac_cv_prog_AWK=mawk
ac_cv_prog_ac_ct_AR=ar
ac_cv_prog_ac_ct_CC=gcc
ac_cv_prog_ac_ct_RANLIB=ranlib
ac_cv_prog_cc_c11=
ac_cv_prog_cc_g=yes
ac_cv_prog_cc_stdc=
ac_cv_prog_make_make_set=yes
am_cv_ar_interface=ar
am_cv_make_support_nested_variables=yes
am_cv_prog_cc_c_o=yes
## ----------------- ##
## Output variables. ##
## ----------------- ##
ACLOCAL=''
AMDEPBACKSLASH='\'
AMDEP_FALSE='#'
AMDEP_TRUE=''
AMTAR=''
AM_BACKSLASH='\'
AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
AM_DEFAULT_VERBOSITY='1'
AM_V='$(V)'
AR='ar'
AUTOCONF=''
AUTOHEADER=''
AUTOMAKE=''
AWK='mawk'
CC='gcc'
CCDEPMODE=''
CFLAGS='-g -O2'
CPPFLAGS=''
CSCOPE=''
CTAGS=''
CYGPATH_W=''
DEFS=''
DEPDIR='.deps'
ECHO_C=''
ECHO_N='-n'
ECHO_T=''
ETAGS=''
EXEEXT=''
INSTALL_DATA='${INSTALL} -m 644'
INSTALL_PROGRAM='${INSTALL}'
INSTALL_SCRIPT='${INSTALL}'
INSTALL_STRIP_PROGRAM='$(install_sh) -c -s'
LDFLAGS=''
LIBOBJS=''
LIBS=''
LTLIBOBJS=''
MAKEINFO=''
MKDIR_P='/usr/bin/mkdir -p'
OBJEXT='o'
PACKAGE=''
PACKAGE_BUGREPORT='https://github.com/anomalyco/utun3/issues'
PACKAGE_NAME='utun'
PACKAGE_STRING='utun 2.0.0'
PACKAGE_TARNAME='utun'
PACKAGE_URL=''
PACKAGE_VERSION='2.0.0'
PATH_SEPARATOR=':'
RANLIB='ranlib'
SET_MAKE=''
SHELL='/bin/bash'
STRIP=''
VERSION=''
ac_ct_AR='ar'
ac_ct_CC='gcc'
am__EXEEXT_FALSE=''
am__EXEEXT_TRUE=''
am__fastdepCC_FALSE=''
am__fastdepCC_TRUE=''
am__include='include'
am__isrc=' -I$(srcdir)'
am__leading_dot='.'
am__nodep='_no'
am__quote=''
am__tar=''
am__untar=''
bindir='${exec_prefix}/bin'
build=''
build_alias=''
build_cpu=''
build_os=''
build_vendor=''
datadir='${datarootdir}'
datarootdir='${prefix}/share'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
dvidir='${docdir}'
exec_prefix='NONE'
host=''
host_alias=''
host_cpu=''
host_os=''
host_vendor=''
htmldir='${docdir}'
includedir='${prefix}/include'
infodir='${datarootdir}/info'
install_sh='${SHELL} /home/vnc1/proj/utun3/build-aux/install-sh'
libdir='${exec_prefix}/lib'
libexecdir='${exec_prefix}/libexec'
localedir='${datarootdir}/locale'
localstatedir='${prefix}/var'
mandir='${datarootdir}/man'
mkdir_p=''
oldincludedir='/usr/include'
pdfdir='${docdir}'
prefix='NONE'
program_transform_name='s,x,x,'
psdir='${docdir}'
runstatedir='${localstatedir}/run'
sbindir='${exec_prefix}/sbin'
sharedstatedir='${prefix}/com'
sysconfdir='${prefix}/etc'
target_alias=''
## ----------- ##
## confdefs.h. ##
## ----------- ##
/* confdefs.h */
#define PACKAGE_NAME "utun"
#define PACKAGE_TARNAME "utun"
#define PACKAGE_VERSION "2.0.0"
#define PACKAGE_STRING "utun 2.0.0"
#define PACKAGE_BUGREPORT "https://github.com/anomalyco/utun3/issues"
#define PACKAGE_URL ""
configure: exit 1

1200
build/config.status

File diff suppressed because it is too large Load Diff

1
build/lib/tinycrypt

@ -0,0 +1 @@
../tinycrypt/lib

1
build/stamp-h1

@ -0,0 +1 @@
timestamp for config.h

293
build_errors.txt

@ -0,0 +1,293 @@
etcp_connections.c: In function ‘etcp_encrypt_send’:
etcp_connections.c:250:24: error: incompatible types when initializing type ‘sc_context_t *’ {aka ‘struct secure_channel *’} using type ‘struct secure_channel’
250 | sc_context_t* sc = dgram->link->etcp->crypto_ctx;
| ^~~~~
etcp_connections.c:252:59: warning: ‘return’ with no value, in function returning non-void
252 | if (len<=0 || len>1480) { dgram->link->send_errors++; return; }
| ^~~~~~
etcp_connections.c:248:5: note: declared here
248 | int etcp_encrypt_send(struct ETCP_DGRAM* dgram) {
| ^~~~~~~~~~~~~~~~~
etcp_connections.c:255:38: warning: passing argument 4 of ‘sc_encrypt’ from incompatible pointer type [-Wincompatible-pointer-types]
255 | sc_encrypt(sc, dgram->data, len, &enc_buf, &enc_buf_len);
| ^~~~~~~~
| |
| uint8_t (*)[1600] {aka unsigned char (*)[1600]}
In file included from etcp_connections.h:4,
from etcp_connections.c:1:
secure_channel.h:61:102: note: expected ‘uint8_t *’ {aka ‘unsigned char *’} but argument is of type ‘uint8_t (*)[1600]’ {aka ‘unsigned char (*)[1600]’}
61 | sc_status_t sc_encrypt(sc_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t *ciphertext_len);
| ~~~~~~~~~^~~~~~~~~~
etcp_connections.c:256:82: warning: ‘return’ with no value, in function returning non-void
256 | if (enc_buf_len + dgram->noencrypt_len > 1480) { dgram->link->send_errors++; return; }
| ^~~~~~
etcp_connections.c:248:5: note: declared here
248 | int etcp_encrypt_send(struct ETCP_DGRAM* dgram) {
| ^~~~~~~~~~~~~~~~~
etcp_connections.c:259:35: error: incompatible types when initializing type ‘struct sockaddr_storage *’ using type ‘struct sockaddr_storage’
259 | struct sockaddr_storage* addr=dgram->link->remote_addr;
| ^~~~~
etcp_connections.c:260:27: error: ‘to_addr’ undeclared (first use in this function); did you mean ‘in_addr’?
260 | socklen_t addr_len = (to_addr->ss_family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
| ^~~~~~~
| in_addr
etcp_connections.c:260:27: note: each undeclared identifier is reported only once for each function it appears in
etcp_connections.c:261:67: warning: passing argument 5 of ‘sendto’ from incompatible pointer type [-Wincompatible-pointer-types]
261 | ssize_t sent = sendto(dgram->link->conn->fd, enc_buf, len, 0, addr, addr_len);
| ^~~~
| |
| struct sockaddr_storage *
In file included from etcp_connections.h:7:
/usr/include/x86_64-linux-gnu/sys/socket.h:153:58: note: expected ‘const struct sockaddr *’ but argument is of type ‘struct sockaddr_storage *’
153 | int __flags, __CONST_SOCKADDR_ARG __addr,
| ^
etcp_connections.c: In function ‘etcp_connections_read_callback’:
etcp_connections.c:311:33: error: ‘pkt’ undeclared (first use in this function)
311 | socklen_t addr_len = sizeof(pkt->metadata.remote_addr);
| ^~~
etcp_connections.c:314:15: error: redefinition of ‘addr_len’
314 | socklen_t addr_len=sizeof(addr);
| ^~~~~~~~
etcp_connections.c:311:15: note: previous definition of ‘addr_len’ with type ‘socklen_t’ {aka ‘unsigned int’}
311 | socklen_t addr_len = sizeof(pkt->metadata.remote_addr);
| ^~~~~~~~
etcp_connections.c:322:48: warning: passing argument 1 of ‘memory_pool_alloc’ from incompatible pointer type [-Wincompatible-pointer-types]
322 | struct ETCP_DGRAM* pkt = memory_pool_alloc(&e_sock->instance->pkt_pool);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| struct memory_pool **
In file included from utun_instance.h:7,
from etcp_connections.h:5:
../lib/memory_pool.h:18:45: note: expected ‘struct memory_pool *’ but argument is of type ‘struct memory_pool **’
18 | void* memory_pool_alloc(struct memory_pool* pool);
| ~~~~~~~~~~~~~~~~~~~~^~~~
etcp_connections.c:327:51: error: expected expression before ‘struct’
327 | struct ETCP_LINK* link=etcp_link_find_by_addr(struct ETCP_SOCKET* e_sock, &addr);
| ^~~~~~
etcp_connections.c:327:28: error: too few arguments to function ‘etcp_link_find_by_addr’
327 | struct ETCP_LINK* link=etcp_link_find_by_addr(struct ETCP_SOCKET* e_sock, &addr);
| ^~~~~~~~~~~~~~~~~~~~~~
etcp_connections.c:105:26: note: declared here
105 | static struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_SOCKET* e_sock, struct sockaddr_storage* addr) {
| ^~~~~~~~~~~~~~~~~~~~~~
etcp_connections.c:360:41: error: invalid type argument of ‘->’ (have ‘struct secure_channel’)
360 | if (memcmp(&conn->crypto_ctx->peer_public_key, &sc.peer_public_key, SC_PUBKEY_SIZE)) { errorcode=5; goto ec_fr; }// коллизия - peer id совпал а ключи разные.
| ^~
etcp_connections.c:362:48: error: ‘etcp’ undeclared (first use in this function)
362 | struct ETCP_LINK* link = etcp_link_new(etcp, e_sock, addr, 1);
| ^~~~
etcp_connections.c:362:62: error: incompatible type for argument 3 of ‘etcp_link_new’
362 | struct ETCP_LINK* link = etcp_link_new(etcp, e_sock, addr, 1);
| ^~~~
| |
| struct sockaddr_storage
etcp_connections.c:209:108: note: expected ‘struct sockaddr_storage *’ but argument is of type ‘struct sockaddr_storage’
209 | struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn, struct sockaddr_storage* remote_addr, uint8_t is_server) {
| ~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~
etcp_connections.c:376:49: error: invalid type argument of ‘->’ (have ‘struct global_config’)
376 | int mtu=e_sock->instance->config->global->mtu;
| ^~
etcp_connections.c:383:9: warning: implicit declaration of function ‘packet_pool_free’ [-Wimplicit-function-declaration]
383 | packet_pool_free(&e_sock->instance->pkt_pool, pkt);
| ^~~~~~~~~~~~~~~~
etcp_connections.c:392:5: warning: implicit declaration of function ‘etcp_input’; did you mean ‘etcp_conn_input’? [-Wimplicit-function-declaration]
392 | etcp_input(pkt, conns);
| ^~~~~~~~~~
| etcp_conn_input
etcp_connections.c:392:21: error: ‘conns’ undeclared (first use in this function); did you mean ‘confstr’?
392 | etcp_input(pkt, conns);
| ^~~~~
| confstr
etcp_connections.c: In function ‘etcp_input’:
etcp_connections.c:405:18: error: ‘e_sock’ undeclared (first use in this function)
405 | if (!pkt || !e_sock) return -1;
| ^~~~~~
etcp_connections.c:408:21: error: ‘struct ETCP_DGRAM’ has no member named ‘metadata’
408 | size_t len = pkt->metadata.data_len;
| ^~
etcp_connections.c:417:75: error: ‘struct ETCP_CONN’ has no member named ‘has_peer_key’
417 | if (header != ETCP_INIT_REQUEST && header != ETCP_CHANNEL_INIT && etcp->has_peer_key) {
| ^~
etcp_connections.c:419:28: error: incompatible types when initializing type ‘sc_context_t *’ {aka ‘struct secure_channel *’} using type ‘struct secure_channel’
419 | sc_context_t* sc = etcp->crypto_ctx;
| ^~~~
etcp_connections.c:431:83: error: ‘struct ETCP_DGRAM’ has no member named ‘metadata’
431 | struct ETCP_LINK* link = etcp_link_find_by_addr(e_sock, (struct sockaddr*)&pkt->metadata.remote_addr,
| ^~
etcp_connections.c:432:63: error: ‘struct ETCP_DGRAM’ has no member named ‘metadata’
432 | sizeof(pkt->metadata.remote_addr));
| ^~
etcp_connections.c:431:30: error: too many arguments to function ‘etcp_link_find_by_addr’
431 | struct ETCP_LINK* link = etcp_link_find_by_addr(e_sock, (struct sockaddr*)&pkt->metadata.remote_addr,
| ^~~~~~~~~~~~~~~~~~~~~~
etcp_connections.c:105:26: note: declared here
105 | static struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_SOCKET* e_sock, struct sockaddr_storage* addr) {
| ^~~~~~~~~~~~~~~~~~~~~~
etcp_connections.c:437:70: error: ‘struct ETCP_DGRAM’ has no member named ‘metadata’
437 | link = etcp_link_new(etcp, e_sock, (struct sockaddr*)&pkt->metadata.remote_addr,
| ^~
etcp_connections.c:438:44: error: ‘struct ETCP_DGRAM’ has no member named ‘metadata’
438 | sizeof(pkt->metadata.remote_addr));
| ^~
etcp_connections.c:443:13: warning: implicit declaration of function ‘etcp_connections_send’; did you mean ‘etcp_connection_create’? [-Wimplicit-function-declaration]
443 | etcp_connections_send(e_sock, reset_pkt, 1, (struct sockaddr*)&pkt->metadata.remote_addr, sizeof(pkt->metadata.remote_addr));
| ^~~~~~~~~~~~~~~~~~~~~
| etcp_connection_create
etcp_connections.c:443:79: error: ‘struct ETCP_DGRAM’ has no member named ‘metadata’
443 | etcp_connections_send(e_sock, reset_pkt, 1, (struct sockaddr*)&pkt->metadata.remote_addr, sizeof(pkt->metadata.remote_addr));
| ^~
etcp_connections.c:443:113: error: ‘struct ETCP_DGRAM’ has no member named ‘metadata’
443 | etcp_connections_send(e_sock, reset_pkt, 1, (struct sockaddr*)&pkt->metadata.remote_addr, sizeof(pkt->metadata.remote_addr));
| ^~
etcp_connections.c:459:20: error: ‘struct ETCP_CONN’ has no member named ‘peer_public_key’
459 | memcpy(etcp->peer_public_key, peer_pubkey, SC_PUBKEY_SIZE);
| ^~
etcp_connections.c:460:13: error: ‘struct ETCP_CONN’ has no member named ‘has_peer_key’
460 | etcp->has_peer_key = 1;
| ^~
etcp_connections.c:461:36: error: incompatible type for argument 1 of ‘sc_set_peer_public_key’
461 | sc_set_peer_public_key(etcp->crypto_ctx, peer_pubkey);
| ~~~~^~~~~~~~~~~~
| |
| struct secure_channel
secure_channel.h:58:50: note: expected ‘sc_context_t *’ {aka ‘struct secure_channel *’} but argument is of type ‘struct secure_channel’
58 | sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key, int mode);// mode: 0-bin 1-hex key format
| ~~~~~~~~~~~~~~^~~
etcp_connections.c:461:9: error: too few arguments to function ‘sc_set_peer_public_key’
461 | sc_set_peer_public_key(etcp->crypto_ctx, peer_pubkey);
| ^~~~~~~~~~~~~~~~~~~~~~
secure_channel.h:58:13: note: declared here
58 | sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key, int mode);// mode: 0-bin 1-hex key format
| ^~~~~~~~~~~~~~~~~~~~~~
etcp_connections.c:478:12: error: ‘struct ETCP_DGRAM’ has no member named ‘metadata’
478 | pkt->metadata.channel = link; // Or etcp_channel if separate
| ^~
etcp_connections.c:483:5: warning: implicit declaration of function ‘packet_pool_put’ [-Wimplicit-function-declaration]
483 | packet_pool_put(&etcp->packet_pool, pkt);
| ^~~~~~~~~~~~~~~
etcp_connections.c:483:26: error: ‘struct ETCP_CONN’ has no member named ‘packet_pool’
483 | packet_pool_put(&etcp->packet_pool, pkt);
| ^~
etcp_connections.c: In function ‘init_connections’:
etcp_connections.c:490:29: warning: initialization of ‘struct config *’ from incompatible pointer type ‘struct utun_config *’ [-Wincompatible-pointer-types]
490 | struct config* config = instance->config;
| ^~~~~~~~
etcp_connections.c:491:42: error: invalid use of undefined type ‘struct config’
491 | instance->connections = calloc(config->server_count, sizeof(struct ETCP_CONN));
| ^~
etcp_connections.c:494:15: error: ‘struct UTUN_INSTANCE’ has no member named ‘connection_count’; did you mean ‘connections’?
494 | instance->connection_count = 0;
| ^~~~~~~~~~~~~~~~
| connections
etcp_connections.c:496:39: error: invalid use of undefined type ‘struct config’
496 | struct CFG_SERVER* server = config->servers;
| ^~
etcp_connections.c:500:34: warning: implicit declaration of function ‘etcp_connection_add’; did you mean ‘etcp_connection_create’? [-Wimplicit-function-declaration]
500 | struct ETCP_CONN* conn = etcp_connection_add(instance, server->ip, server->netif_index, server->so_mark, server->type);
| ^~~~~~~~~~~~~~~~~~~
| etcp_connection_create
etcp_connections.c:500:34: warning: initialization of ‘struct ETCP_CONN *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
etcp_connections.c:503:26: error: ‘etcp’ undeclared (first use in this function)
503 | etcp_destroy(etcp);
| ^~~~
etcp_connections.c:508:36: error: ‘struct CFG_SERVER’ has no member named ‘addr’
508 | server->name, server->addr, e_sock->num_channels);
| ^~
etcp_connections.c:508:44: error: ‘e_sock’ undeclared (first use in this function)
508 | server->name, server->addr, e_sock->num_channels);
| ^~~~~~
etcp_connections.c:513:31: error: invalid use of undefined type ‘struct config’
513 | for (int j = 0; j < config->client_count; j++) {
| ^~
etcp_connections.c:514:47: error: invalid use of undefined type ‘struct config’
514 | struct client_config* client = &config->clients[j];
| ^~
etcp_connections.c:517:26: error: invalid use of undefined type ‘struct client_config’
517 | if (strcmp(client->from, server->name) != 0) {
| ^~
etcp_connections.c:525:31: error: ‘struct ETCP_CONN’ has no member named ‘ctx’
525 | sc_init_ctx(&etcp_conn->ctx, &instance->my_keys);
| ^~
etcp_connections.c:526:26: error: invalid use of undefined type ‘struct client_config’
526 | if (strlen(client->peer_public_key_hex) > 0) {
| ^~
etcp_connections.c:528:17: warning: implicit declaration of function ‘hex_to_binary’ [-Wimplicit-function-declaration]
528 | if (hex_to_binary(client->peer_public_key_hex, peer_key_bin, SC_PUBKEY_SIZE) == 0) {
| ^~~~~~~~~~~~~
etcp_connections.c:528:37: error: invalid use of undefined type ‘struct client_config’
528 | if (hex_to_binary(client->peer_public_key_hex, peer_key_bin, SC_PUBKEY_SIZE) == 0) {
| ^~
etcp_connections.c:529:33: error: ‘struct ETCP_CONN’ has no member named ‘peer_public_key’
529 | memcpy(etcp_conn->peer_public_key, peer_key_bin, SC_PUBKEY_SIZE);
| ^~
etcp_connections.c:530:26: error: ‘struct ETCP_CONN’ has no member named ‘has_peer_key’
530 | etcp_conn->has_peer_key = 1;
| ^~
etcp_connections.c:531:49: error: incompatible type for argument 1 of ‘sc_set_peer_public_key’
531 | sc_set_peer_public_key(etcp_conn->crypto_ctx, peer_key_bin);
| ~~~~~~~~~^~~~~~~~~~~~
| |
| struct secure_channel
secure_channel.h:58:50: note: expected ‘sc_context_t *’ {aka ‘struct secure_channel *’} but argument is of type ‘struct secure_channel’
58 | sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key, int mode);// mode: 0-bin 1-hex key format
| ~~~~~~~~~~~~~~^~~
etcp_connections.c:531:17: error: too few arguments to function ‘sc_set_peer_public_key’
531 | sc_set_peer_public_key(etcp_conn->crypto_ctx, peer_key_bin);
| ^~~~~~~~~~~~~~~~~~~~~~
secure_channel.h:58:13: note: declared here
58 | sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key, int mode);// mode: 0-bin 1-hex key format
| ^~~~~~~~~~~~~~~~~~~~~~
etcp_connections.c:537:16: error: variable ‘cl’ has initializer but incomplete type
537 | struct client_link cl=client->links;
| ^~~~~~~~~~~
etcp_connections.c:537:37: error: invalid use of undefined type ‘struct client_config’
537 | struct client_link cl=client->links;
| ^~
etcp_connections.c:537:28: error: storage size of ‘cl’ isn’t known
537 | struct client_link cl=client->links;
| ^~
etcp_connections.c:539:13: error: ‘conn’ undeclared (first use in this function)
539 | conn= (надо найти)
| ^~~~
etcp_connections.c:539:20: error: ‘надо’ undeclared (first use in this function)
539 | conn= (надо найти)
| ^~~~
etcp_connections.c:539:24: error: expected ‘)’ before ‘найти’
539 | conn= (надо найти)
| ~ ^~~~~~
| )
etcp_connections.c:539:31: error: expected ‘;’ before ‘struct’
539 | conn= (надо найти)
| ^
| ;
......
542 | struct ETCP_LINK* link = etcp_link_new(etcp, conn, cl->remote_sockaddr, sizeof(struct sockaddr_storage));
| ~~~~~~
In file included from etcp_connections.c:8:
etcp_connections.c:544:95: error: invalid use of undefined type ‘struct client_config’
544 | DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to create link for client %s", client->name);
| ^~
../lib/debug_config.h:114:92: note: in definition of macro ‘DEBUG_ERROR’
114 | debug_output(DEBUG_LEVEL_ERROR, category, __FUNCTION__, __FILE__, __LINE__, fmt, ##__VA_ARGS__); \
| ^~~~~~~~~~~
etcp_connections.c:550:51: error: invalid use of undefined type ‘struct client_config’
550 | printf(" Added client %s -> %s\n", client->name, client->to_addr);
| ^~
etcp_connections.c:550:65: error: invalid use of undefined type ‘struct client_config’
550 | printf(" Added client %s -> %s\n", client->name, client->to_addr);
| ^~
etcp_connections.c:554:19: error: ‘struct UTUN_INSTANCE’ has no member named ‘connection_count’; did you mean ‘connections’?
554 | if (instance->connection_count == 0) {
| ^~~~~~~~~~~~~~~~
| connections
etcp_connections.c:561:54: error: ‘struct UTUN_INSTANCE’ has no member named ‘connection_count’; did you mean ‘connections’?
561 | printf("Initialized %d connections\n", instance->connection_count);
| ^~~~~~~~~~~~~~~~
| connections
etcp_connections.c: At top level:
etcp_connections.c:26:12: warning: ‘etcp_link_send_init_response’ used but never defined
26 | static int etcp_link_send_init_response(struct ETCP_LINK* link, int mtu, uint16_t keepalive_interval);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
make[2]: *** [Makefile:619: utun-etcp_connections.o] Ошибка 1
make[1]: *** [Makefile:433: all-recursive] Ошибка 1
make: *** [Makefile:353: all] Ошибка 2

3
configure.ac

@ -1,4 +1,4 @@
AC_INIT([utun], [2.0.0], [https://github.com/anomalyco/utun3/issues])
AC_INIT([utun],[2.0.0],[https://github.com/anomalyco/utun3/issues])
AC_CONFIG_SRCDIR([src/utun.c])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_AUX_DIR([build-aux])
@ -6,7 +6,6 @@ AC_CONFIG_MACRO_DIR([m4])
# Required programs
AC_PROG_CC
AC_PROG_CC_C99
AC_PROG_RANLIB
AM_PROG_AR
AM_INIT_AUTOMAKE([foreign subdir-objects -Wall -Werror])

6
lib/Makefile.am

@ -3,12 +3,14 @@ noinst_LIBRARIES = libuasync.a
libuasync_a_SOURCES = \
u_async.c \
u_async.h \
$(top_srcdir)/src/ll_queue.h \
$(top_srcdir)/src/ll_queue.c \
ll_queue.h \
ll_queue.c \
debug_config.c \
debug_config.h \
timeout_heap.c \
timeout_heap.h \
memory_pool.c \
memory_pool.h \
sha256.c \
sha256.h

46
src/ll_queue.c → lib/ll_queue.c

@ -1,10 +1,10 @@
#include "ll_queue.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "../lib/u_async.h"
#include "../lib/debug_config.h"
#include "ll_queue.h"
#include "u_async.h"
#include "debug_config.h"
// Предварительное объявление для отложенного возобновления
static void queue_resume_timeout_cb(void* arg);
@ -31,8 +31,8 @@ static void check_waiters(struct ll_queue* q) {
waiter->callback(q, waiter->callback_arg);
// Удалить waiter из списка
*pprev = next;
if (q->use_pools) {
memory_pool_free(&q->waiter_pool, waiter);
if (q->pool) {
memory_pool_free(q->pool, waiter);
} else {
free(waiter);
}
@ -50,7 +50,7 @@ static void check_waiters(struct ll_queue* q) {
// ==================== Управление очередью ====================
struct ll_queue* queue_new_with_pools(struct uasync_s* ua, int use_pools) {
struct ll_queue* queue_new(struct UASYNC* ua, struct memory_pool* pool) {
struct ll_queue* q = calloc(1, sizeof(struct ll_queue));
if (!q) return NULL;
@ -65,22 +65,11 @@ struct ll_queue* queue_new_with_pools(struct uasync_s* ua, int use_pools) {
q->resume_timeout_id = NULL;
q->ua = ua;
q->waiters = NULL;
q->use_pools = use_pools;
// Инициализировать пулы памяти
if (use_pools) {
memory_pool_init(&q->waiter_pool, sizeof(struct queue_waiter));
// Для entry_pool будем использовать фиксированный размер, так как размер данных может варьироваться
// Вместо этого оптимизируем через специализированные функции
}
q->pool=pool;
return q;
}
struct ll_queue* queue_new(struct uasync_s* ua) {
return queue_new_with_pools(ua, 0); // По умолчанию без пулов для обратной совместимости
}
void queue_free(struct ll_queue* q) {
if (!q) return;
@ -104,8 +93,8 @@ void queue_free(struct ll_queue* q) {
while (waiter) {
struct queue_waiter* next = waiter->next;
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_free: freeing waiter %p (waiter %d)", waiter, waiter_count++);
if (q->use_pools) {
memory_pool_free(&q->waiter_pool, waiter);
if (q->pool) {
memory_pool_free(q->pool, waiter);
} else {
free(waiter);
}
@ -118,11 +107,6 @@ void queue_free(struct ll_queue* q) {
uasync_cancel_timeout(q->ua, q->resume_timeout_id);
}
// Очистить пулы памяти
if (q->use_pools) {
memory_pool_destroy(&q->waiter_pool);
}
DEBUG_DEBUG(DEBUG_CATEGORY_LL_QUEUE, "queue_free: freeing queue structure %p", q);
free(q);
}
@ -347,8 +331,8 @@ struct queue_waiter* queue_wait_threshold(struct ll_queue* q, int max_packets, s
// Создать новый waiter
struct queue_waiter* waiter;
if (q->use_pools) {
waiter = (struct queue_waiter*)memory_pool_alloc(&q->waiter_pool);
if (q->pool) {
waiter = (struct queue_waiter*)memory_pool_alloc(q->pool);
} else {
waiter = malloc(sizeof(struct queue_waiter));
}
@ -391,8 +375,8 @@ void queue_cancel_wait(struct ll_queue* q, struct queue_waiter* waiter) {
while (w) {
if (w == waiter) {
*pprev = w->next;
if (q->use_pools) {
memory_pool_free(&q->waiter_pool, w);
if (q->pool) {
memory_pool_free(q->pool, w);
} else {
free(w);
}
@ -406,11 +390,11 @@ void queue_cancel_wait(struct ll_queue* q, struct queue_waiter* waiter) {
// ==================== Статистика и метрики ====================
void queue_get_pool_stats(struct ll_queue* q, size_t* waiter_allocations, size_t* waiter_reuse) {
if (!q || !q->use_pools) {
if (!q || !q->pool) {
if (waiter_allocations) *waiter_allocations = 0;
if (waiter_reuse) *waiter_reuse = 0;
return;
}
memory_pool_get_stats(&q->waiter_pool, waiter_allocations, waiter_reuse);
memory_pool_get_stats(q->pool, waiter_allocations, waiter_reuse);
}

15
src/ll_queue.h → lib/ll_queue.h

@ -6,7 +6,6 @@
// Предварительные объявления
struct ll_queue;
struct uasync_s;
struct ll_entry;
struct queue_waiter;
struct memory_pool;
@ -46,25 +45,21 @@ struct ll_queue {
int callback_suspended; // 1 если коллбэки приостановлены (во время обработки)
void* resume_timeout_id; // ID таймаута uasync для отложенного возобновления
struct uasync_s* ua; // Экземпляр uasync для таймеров
struct UASYNC* ua; // Экземпляр uasync для таймеров
struct queue_waiter* waiters; // Список ожидающих коллбэков
// Пулы памяти для оптимизации аллокаций
struct memory_pool waiter_pool; // Пул для структур struct queue_waiter
int use_pools; // Флаг использования пулов (включается при частых аллокациях)
struct memory_pool* pool; // Пул для структур struct queue_waiter
};
// ==================== Управление очередью ====================
// Создать новую пустую очередь
// ua - экземпляр uasync для таймеров (обязательный параметр)
// use_pools - использовать пулы памяти для оптимизации (1) или нет (0)
// Возвращает: указатель на очередь или NULL при ошибке выделения памяти
struct ll_queue* queue_new_with_pools(struct uasync_s* ua, int use_pools);
// Создать новую пустую очередь (обратная совместимость)
struct ll_queue* queue_new(struct uasync_s* ua);
// pool - если не null то использовать этот пул памяти
// Возвращает: указатель на очередь или NULL при ошибке выделения памяти (see memory_pool.c/h)
struct ll_queue* queue_new(struct UASYNC* ua, struct memory_pool* pool);
// Освободить очередь и все её элементы
// Также отменяет отложенное возобновление если оно запланировано

87
lib/memory_pool.c

@ -0,0 +1,87 @@
// memory_pool.c
#include "memory_pool.h"
#include <stdlib.h>
#include <string.h>
// Инициализировать пул памяти
struct memory_pool* memory_pool_init(size_t object_size) {
struct memory_pool* pool = calloc(1, sizeof(struct memory_pool));
if (!pool) {
return NULL;
}
pool->free_head = NULL;
pool->object_size = object_size;
pool->free_count = 0;
pool->allocations = 0;
pool->reuse_count = 0;
return pool;
}
// Выделить объект из пула или из malloc
void* memory_pool_alloc(struct memory_pool* pool) {
if (!pool) {
return NULL;
}
if (pool->free_head) {
void* obj = pool->free_head;
pool->free_head = *(void**)obj; // Извлекаем next из начала блока
pool->free_count--;
pool->reuse_count++;
memset(obj, 0, pool->object_size); // Опционально: очищаем память для безопасности
return obj;
}
pool->allocations++;
return calloc(1, pool->object_size); // Используем calloc для инициализации нулями
}
// Освободить объект в пул или в free
void memory_pool_free(struct memory_pool* pool, void* obj) {
if (!pool || !obj) {
return;
}
// Если пул не заполнен, сохранить объект для повторного использования
if (pool->free_count < MEMORY_POOL_MAX_FREE) {
*(void**)obj = pool->free_head; // Сохраняем next в начало блока
pool->free_head = obj;
pool->free_count++;
return;
}
// Иначе освободить через free
free(obj);
}
// Получить статистику пула
void memory_pool_get_stats(struct memory_pool* pool, size_t* allocations, size_t* reuse_count) {
if (!pool) {
return;
}
if (allocations) {
*allocations = pool->allocations;
}
if (reuse_count) {
*reuse_count = pool->reuse_count;
}
}
// Очистить пул памяти
void memory_pool_destroy(struct memory_pool* pool) {
if (!pool) {
return;
}
// Освободить все объекты в пуле
void* obj = pool->free_head;
while (obj) {
void* next = *(void**)obj;
free(obj);
obj = next;
}
pool->free_head = NULL;
pool->free_count = 0;
free(pool);
}

23
lib/memory_pool.h

@ -0,0 +1,23 @@
// memory_pool.h
#ifndef MEMORY_POOL_H
#define MEMORY_POOL_H
#include <stddef.h>
#define MEMORY_POOL_MAX_FREE 64 // Максимальное количество свободных объектов в пуле (чтобы не накапливать слишком много)
struct memory_pool {
void* free_head; // Голова linked list свободных блоков (next хранится в начале каждого блока)
int free_count; // Количество свободных блоков
size_t object_size; // Размер объектов в пуле
size_t allocations; // Общее количество аллокаций (включая новые malloc)
size_t reuse_count; // Количество повторных использований из пула
};
struct memory_pool* memory_pool_init(size_t object_size);
void* memory_pool_alloc(struct memory_pool* pool);
void memory_pool_free(struct memory_pool* pool, void* obj);
void memory_pool_get_stats(struct memory_pool* pool, size_t* allocations, size_t* reuse_count);
void memory_pool_destroy(struct memory_pool* pool);
#endif // MEMORY_POOL_H

55
lib/u_async.c

@ -1,7 +1,6 @@
// uasync.c
#include "u_async.h"
#include "timeout_heap.h"
#include "debug_config.h"
#include <stdio.h>
#include <string.h>
@ -19,7 +18,7 @@ struct timeout_node {
void* arg;
timeout_callback_t callback;
uint64_t expiration_ms; // absolute expiration time in milliseconds
uasync_t* ua; // Pointer back to uasync instance for counter updates
struct UASYNC* ua; // Pointer back to uasync instance for counter updates
int cancelled; // Cancellation flag
};
@ -50,21 +49,7 @@ static int socket_array_remove(struct socket_array* sa, int fd);
static struct socket_node* socket_array_get(struct socket_array* sa, int fd);
static int socket_array_build_pollfd(struct socket_array* sa, struct pollfd* fds, int max_fds);
// Uasync instance structure
struct uasync_s {
TimeoutHeap* timeout_heap; // Heap for timeout management
struct socket_array* sockets; // Array-based socket management
// Debug counters for memory allocation tracking
size_t timer_alloc_count;
size_t timer_free_count;
size_t socket_alloc_count;
size_t socket_free_count;
// Wakeup pipe for interrupting poll
int wakeup_pipe[2]; // [0] read, [1] write
int wakeup_initialized;
};
// No global instance - each module must use its own uasync_t instance
// 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) {
@ -219,7 +204,7 @@ static int socket_array_build_pollfd(struct socket_array* sa, struct pollfd* fds
// Callback to free timeout node and update counters
static void timeout_node_free_callback(void* user_data, void* data) {
uasync_t* ua = (uasync_t*)user_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++;
@ -234,7 +219,7 @@ static void get_current_time(struct timeval* tv) {
// Drain wakeup pipe - read all available bytes
static void drain_wakeup_pipe(uasync_t* ua) {
static void drain_wakeup_pipe(struct UASYNC* ua) {
if (!ua || !ua->wakeup_initialized) return;
char buf[64];
@ -261,7 +246,7 @@ static uint64_t timeval_to_ms(const struct timeval* tv) {
// Simplified timeout handling without reference counting
// Process expired timeouts with safe cancellation
static void process_timeouts(struct uasync_s* ua) {
static void process_timeouts(struct UASYNC* ua) {
if (!ua || !ua->timeout_heap) return;
struct timeval now_tv;
@ -291,7 +276,7 @@ static void process_timeouts(struct uasync_s* ua) {
}
// Compute time to next timeout
static void get_next_timeout(struct uasync_s* ua, struct timeval* tv) {
static void get_next_timeout(struct UASYNC* ua, struct timeval* tv) {
if (!ua || !ua->timeout_heap) {
tv->tv_sec = 0;
tv->tv_usec = 0;
@ -326,7 +311,7 @@ static void get_next_timeout(struct uasync_s* ua, struct timeval* tv) {
// Instance version
void* uasync_set_timeout(uasync_t* ua, int timeout_tb, void* arg, timeout_callback_t callback) {
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;
@ -358,7 +343,7 @@ void* uasync_set_timeout(uasync_t* ua, int timeout_tb, void* arg, timeout_callba
// Instance version
err_t uasync_cancel_timeout(uasync_t* ua, void* t_id) {
err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id) {
if (!ua || !t_id || !ua->timeout_heap) return ERR_FAIL;
struct timeout_node* node = (struct timeout_node*)t_id;
@ -381,7 +366,7 @@ err_t uasync_cancel_timeout(uasync_t* ua, void* t_id) {
// Instance version
void* uasync_add_socket(uasync_t* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_data) {
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);
@ -396,7 +381,7 @@ void* uasync_add_socket(uasync_t* ua, int fd, socket_callback_t read_cbk, socket
// Instance version
err_t uasync_remove_socket(uasync_t* ua, void* s_id) {
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;
@ -411,14 +396,14 @@ err_t uasync_remove_socket(uasync_t* ua, void* s_id) {
void uasync_mainloop(uasync_t* ua) {
void uasync_mainloop(struct UASYNC* ua) {
while (1) {
uasync_poll(ua, -1); /* infinite timeout */
}
}
// Instance version
void uasync_poll(uasync_t* ua, int timeout_tb) {
void uasync_poll(struct UASYNC* ua, int timeout_tb) {
if (!ua) return;
/* Process expired timeouts */
@ -585,7 +570,7 @@ void uasync_poll(uasync_t* ua, int timeout_tb) {
// ========== Instance management functions ==========
uasync_t* uasync_create(void) {
struct UASYNC* uasync_create(void) {
// Initialize debug system on first use
static int debug_initialized = 0;
if (!debug_initialized) {
@ -593,10 +578,10 @@ uasync_t* uasync_create(void) {
debug_initialized = 1;
}
uasync_t* ua = malloc(sizeof(struct uasync_s));
struct UASYNC* ua = malloc(sizeof(struct UASYNC));
if (!ua) return NULL;
memset(ua, 0, sizeof(struct uasync_s));
memset(ua, 0, sizeof(struct UASYNC));
ua->wakeup_pipe[0] = -1;
ua->wakeup_pipe[1] = -1;
ua->wakeup_initialized = 0;
@ -643,7 +628,7 @@ uasync_t* uasync_create(void) {
return ua;
}
void uasync_destroy(uasync_t* ua) {
void uasync_destroy(struct UASYNC* ua) {
if (!ua) return;
// Check for potential memory leaks
@ -699,7 +684,7 @@ void uasync_destroy(uasync_t* ua) {
free(ua);
}
void uasync_init_instance(uasync_t* ua) {
void uasync_init_instance(struct UASYNC* ua) {
if (!ua) return;
// Initialize socket array if not present
@ -716,7 +701,7 @@ void uasync_init_instance(uasync_t* ua) {
}
// Debug statistics
void uasync_get_stats(uasync_t* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free) {
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;
@ -727,7 +712,7 @@ void uasync_get_stats(uasync_t* ua, size_t* timer_alloc, size_t* timer_free, siz
// Get global instance for backward compatibility
// Wakeup mechanism
int uasync_wakeup(uasync_t* ua) {
int uasync_wakeup(struct UASYNC* ua) {
if (!ua || !ua->wakeup_initialized) return -1;
char byte = 0;
@ -739,7 +724,7 @@ int uasync_wakeup(uasync_t* ua) {
return 0;
}
int uasync_get_wakeup_fd(uasync_t* ua) {
int uasync_get_wakeup_fd(struct UASYNC* ua) {
if (!ua || !ua->wakeup_initialized) return -1;
return ua->wakeup_pipe[1];
}

40
lib/u_async.h

@ -7,6 +7,7 @@
#include <sys/time.h>
#include <stddef.h>
#include "timeout_heap.h"
typedef void (*timeout_callback_t)(void* user_arg);// передаёт user_arg из uasync_set_timeout
typedef void (*socket_callback_t)(int fd, void* user_arg);// передаёт user_arg из uasync_add_socket
@ -18,33 +19,44 @@ typedef int err_t;
#define ERR_OK 0
#define ERR_FAIL -1
// Opaque uasync instance handle
typedef struct uasync_s uasync_t;
// Uasync instance structure
struct UASYNC {
TimeoutHeap* timeout_heap; // Heap for timeout management
struct socket_array* sockets; // Array-based socket management
// Debug counters for memory allocation tracking
size_t timer_alloc_count;
size_t timer_free_count;
size_t socket_alloc_count;
size_t socket_free_count;
// Wakeup pipe for interrupting poll
int wakeup_pipe[2]; // [0] read, [1] write
int wakeup_initialized;
};
// Instance API - основной API для работы с uasync
uasync_t* uasync_create(void);
void uasync_destroy(uasync_t* ua);
void uasync_init_instance(uasync_t* ua);
struct UASYNC* uasync_create(void);
void uasync_destroy(struct UASYNC* ua);
void uasync_init_instance(struct UASYNC* ua);
// Timeouts, timebase = 0.1 mS
void* uasync_set_timeout(uasync_t* ua, int timeout_tb, void* user_arg, timeout_callback_t callback);
err_t uasync_cancel_timeout(uasync_t* ua, void* t_id);
void* uasync_set_timeout(struct UASYNC* ua, int timeout_tb, void* user_arg, timeout_callback_t callback);
err_t uasync_cancel_timeout(struct UASYNC* ua, void* t_id);
// Sockets
void* uasync_add_socket(uasync_t* ua, int fd, socket_callback_t read_cbk, socket_callback_t write_cbk, socket_callback_t except_cbk, void* user_arg);
err_t uasync_remove_socket(uasync_t* ua, void* s_id);
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_arg);
err_t uasync_remove_socket(struct UASYNC* ua, void* s_id);
// Single iteration of event loop with timeout (timebase units)
void uasync_poll(uasync_t* ua, int timeout_tb);
void uasync_poll(struct UASYNC* ua, int timeout_tb);
// Mainloop (бесконечный цикл, __noreturn)
void uasync_mainloop(uasync_t* ua);
void uasync_mainloop(struct UASYNC* ua);
// Debug statistics
void uasync_get_stats(uasync_t* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free);
void uasync_get_stats(struct UASYNC* ua, size_t* timer_alloc, size_t* timer_free, size_t* socket_alloc, size_t* socket_free);
// Wakeup mechanism for interrupting poll
int uasync_wakeup(uasync_t* ua);
int uasync_get_wakeup_fd(uasync_t* ua); // returns write fd for wakeup pipe (for signal handlers)
int uasync_wakeup(struct UASYNC* ua);
int uasync_get_wakeup_fd(struct UASYNC* ua); // returns write fd for wakeup pipe (for signal handlers)
#endif // UASYNC_H

53
log

@ -0,0 +1,53 @@
ar: модификатор «u» игнорируется, так как по умолчанию используется «D» (смотрите «U»)
utun_instance.c: In function ‘utun_instance_destroy’:
utun_instance.c:98:17: warning: implicit declaration of function ‘conn_destroy’; did you mean ‘uasync_destroy’? [-Wimplicit-function-declaration]
98 | conn_destroy(instance->connections[i]);
| ^~~~~~~~~~~~
| uasync_destroy
utun_instance.c: In function ‘tun_read_callback’:
utun_instance.c:237:21: warning: implicit declaration of function ‘conn_send’ [-Wimplicit-function-declaration]
237 | if (conn_send(route.next_hop, buffer, nread) < 0) {
| ^~~~~~~~~
utun_instance.c: In function ‘utun_instance_unregister_sockets’:
utun_instance.c:276:57: warning: passing argument 2 of ‘uasync_remove_socket’ makes pointer from integer without a cast [-Wint-conversion]
276 | uasync_remove_socket(instance->ua, instance->tun.fd);
| ~~~~~~~~~~~~~^~~
| |
| int
In file included from utun_instance.c:8:
../lib/u_async.h:35:48: note: expected ‘void *’ but argument is of type ‘int’
35 | err_t uasync_remove_socket(uasync_t* ua, void* s_id);
| ~~~~~~^~~~
etcp_connections.c: In function ‘etcp_link_send_init’:
etcp_connections.c:223:5: warning: implicit declaration of function ‘sc_get_public_key’; did you mean ‘sc_set_peer_public_key’? [-Wimplicit-function-declaration]
223 | sc_get_public_key(sc, pubkey);
| ^~~~~~~~~~~~~~~~~
| sc_set_peer_public_key
etcp_connections.c: In function ‘init_connections’:
etcp_connections.c:432:29: warning: initialization of ‘struct config *’ from incompatible pointer type ‘struct utun_config *’ [-Wincompatible-pointer-types]
432 | struct config* config = instance->config;
| ^~~~~~~~
etcp_connections.c:433:42: error: invalid use of undefined type ‘struct config’
433 | instance->connections = calloc(config->server_count, sizeof(conn_handle_t*));
| ^~
etcp_connections.c:438:31: error: invalid use of undefined type ‘struct config’
438 | for (int i = 0; i < config->server_count; i++) {
| ^~
etcp_connections.c:439:47: error: invalid use of undefined type ‘struct config’
439 | struct server_config* server = &config->servers[i];
| ^~
etcp_connections.c:442:30: warning: implicit declaration of function ‘parse_ip_port’ [-Wimplicit-function-declaration]
442 | uint16_t bind_port = parse_ip_port(server->addr, bind_ip_str, sizeof(bind_ip_str));
| ^~~~~~~~~~~~~
etcp_connections.c:485:43: warning: assignment to ‘struct ETCP_SOCKET *’ from incompatible pointer type ‘struct ETCP_CONNECTIONS *’ [-Wincompatible-pointer-types]
485 | instance->first_listen_socket = conns;
| ^
etcp_connections.c:489:35: error: invalid use of undefined type ‘struct config’
489 | for (int j = 0; j < config->client_count; j++) {
| ^~
etcp_connections.c:490:51: error: invalid use of undefined type ‘struct config’
490 | struct client_config* client = &config->clients[j];
| ^~
make[2]: *** [Makefile:626: utun-etcp_connections.o] Ошибка 1
make[1]: *** [Makefile:433: all-recursive] Ошибка 1
make: *** [Makefile:353: all] Ошибка 2

2
src/Makefile.am

@ -12,8 +12,6 @@ utun_SOURCES = \
secure_channel.c \
crc32.c \
pkt_normalizer.c \
packet_pool.c \
memory_pool.c \
$(top_srcdir)/tinycrypt/lib/source/aes_encrypt.c \
$(top_srcdir)/tinycrypt/lib/source/aes_decrypt.c \
$(top_srcdir)/tinycrypt/lib/source/cbc_mode.c \

442
src/config_parser.c

@ -1,31 +1,25 @@
// config_parser.c - Configuration parser for utun application (simplified)
// config_parser.c - Configuration parser for utun application (updated for new structures)
#define _POSIX_C_SOURCE 200809L
#include "config_parser.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "debug_config.h"
#include "../lib/debug_config.h"
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <net/if.h>
#define MAX_LINE_LEN 1024
#define INITIAL_ARRAY_CAPACITY 8
void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex_str, size_t hex_len) {
if (!bytes || !hex_str || hex_len < len * 2 + 1) return;
for (size_t i = 0; i < len; i++) {
snprintf(hex_str + i * 2, hex_len - i * 2, "%02x", bytes[i]);
}
hex_str[len * 2] = '\0';
}
typedef enum {
SECTION_UNKNOWN,
SECTION_GLOBAL,
SECTION_SERVER,
SECTION_CLIENT,
SECTION_CONNECTION,
SECTION_ROUTING
} section_type_t;
@ -57,87 +51,215 @@ static int parse_key_value(const char *line, char *key, size_t key_len, char *va
return 0;
}
static int ensure_capacity(void **array, int *capacity, int item_size) {
if (!array || !capacity) return -1;
int new_capacity = *capacity == 0 ? INITIAL_ARRAY_CAPACITY : *capacity * 2;
void *new_array = realloc(*array, new_capacity * item_size);
if (!new_array) return -1;
*array = new_array;
*capacity = new_capacity;
return 0;
}
static int assign_string(char *dest, size_t dest_size, const char *src) {
if (!dest || !src || strlen(src) >= dest_size) return -1;
strcpy(dest, src);
return 0;
}
static int add_server(struct utun_config *cfg, const struct server_config *srv) {
if (cfg->server_count >= cfg->server_capacity) {
if (ensure_capacity((void**)&cfg->servers, &cfg->server_capacity, sizeof(struct server_config)) < 0) return -1;
static int parse_ip_with_netmask(const char *str, struct IP *ip, uint8_t *netmask) {
char ip_str[64];
strncpy(ip_str, str, sizeof(ip_str) - 1);
ip_str[sizeof(ip_str) - 1] = '\0';
char *slash = strchr(ip_str, '/');
if (slash) {
*slash = '\0';
*netmask = atoi(slash + 1);
} else {
*netmask = 32; // Default IPv4 netmask
}
// Try IPv4 first
struct in_addr addr4;
if (inet_pton(AF_INET, ip_str, &addr4) == 1) {
ip->family = AF_INET;
ip->addr.v4 = addr4;
return 0;
}
memcpy(&cfg->servers[cfg->server_count++], srv, sizeof(struct server_config));
// Try IPv6
struct in6_addr addr6;
if (inet_pton(AF_INET6, ip_str, &addr6) == 1) {
ip->family = AF_INET6;
ip->addr.v6 = addr6;
if (*netmask == 32) *netmask = 128; // Default IPv6 netmask
return 0;
}
return -1;
}
static int add_client(struct utun_config *cfg, const struct client_config *cli) {
if (cfg->client_count >= cfg->client_capacity) {
if (ensure_capacity((void**)&cfg->clients, &cfg->client_capacity, sizeof(struct client_config)) < 0) return -1;
static int parse_sockaddr(const char *addr_str, const char *port_str, struct sockaddr_storage *sockaddr) {
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
struct addrinfo *result;
if (getaddrinfo(addr_str, port_str, &hints, &result) != 0) {
return -1;
}
memcpy(&cfg->clients[cfg->client_count++], cli, sizeof(struct client_config));
memcpy(sockaddr, result->ai_addr, result->ai_addrlen);
freeaddrinfo(result);
return 0;
}
static int add_route(struct utun_config *cfg, const char *server, const char *client) {
if (!cfg->connections_v2) {
cfg->connections_v2 = calloc(1, sizeof(struct connection_config_v2));
if (!cfg->connections_v2) return -1;
cfg->connection_v2_capacity = 1;
static int parse_address_and_port(const char *str, struct sockaddr_storage *sockaddr) {
char addr_copy[MAX_ADDR_LEN];
if (strlen(str) >= sizeof(addr_copy)) return -1;
strcpy(addr_copy, str);
char *port_str = strrchr(addr_copy, ':');
if (!port_str) return -1;
*port_str = '\0';
port_str++;
return parse_sockaddr(addr_copy, port_str, sockaddr);
}
static uint32_t get_netif_index(const char *ifname) {
if (!ifname || strlen(ifname) == 0) return 0;
return if_nametoindex(ifname);
}
static struct CFG_CLIENT_LINK* create_client_link(struct CFG_SERVER* local_srv, const char *remote_addr) {
struct CFG_CLIENT_LINK *link = calloc(1, sizeof(struct CFG_CLIENT_LINK));
if (!link) return NULL;
link->local_srv = local_srv;
if (parse_address_and_port(remote_addr, &link->remote_addr) < 0) {
free(link);
return NULL;
}
struct connection_config_v2 *conn = &cfg->connections_v2[cfg->connection_v2_count];
link->next = NULL;
return link;
}
if (conn->route_count >= conn->route_capacity) {
if (ensure_capacity((void**)&conn->routes, &conn->route_capacity, sizeof(struct route_pair)) < 0) return -1;
static void free_cfg_client_links(struct CFG_CLIENT_LINK *links) {
while (links) {
struct CFG_CLIENT_LINK *next = links->next;
free(links);
links = next;
}
}
struct route_pair *route = &conn->routes[conn->route_count++];
if (assign_string(route->server_name, MAX_CONN_NAME_LEN, server) < 0) return -1;
if (assign_string(route->client_name, MAX_CONN_NAME_LEN, client) < 0) return -1;
static struct CFG_ROUTE_ENTRY* create_route_entry(const char *subnet_str) {
struct CFG_ROUTE_ENTRY *entry = calloc(1, sizeof(struct CFG_ROUTE_ENTRY));
if (!entry) return NULL;
if (parse_ip_with_netmask(subnet_str, &entry->ip, &entry->netmask) < 0) {
free(entry);
return NULL;
}
entry->next = NULL;
return entry;
}
static void free_route_entries(struct CFG_ROUTE_ENTRY *entries) {
while (entries) {
struct CFG_ROUTE_ENTRY *next = entries->next;
free(entries);
entries = next;
}
}
static int add_route_entry(struct CFG_ROUTE_ENTRY **list, const char *subnet_str) {
struct CFG_ROUTE_ENTRY *new_entry = create_route_entry(subnet_str);
if (!new_entry) return -1;
// Add to head of list
new_entry->next = *list;
*list = new_entry;
return 0;
}
static struct CFG_SERVER* find_server_by_name(struct CFG_SERVER *servers, const char *name) {
struct CFG_SERVER *srv = servers;
while (srv) {
if (strcmp(srv->name, name) == 0) {
return srv;
}
srv = srv->next;
}
return NULL;
}
static int parse_global(const char *key, const char *value, struct global_config *global) {
if (strcmp(key, "my_private_key") == 0) return assign_string(global->my_private_key_hex, MAX_KEY_LEN, value);
if (strcmp(key, "my_public_key") == 0) return assign_string(global->my_public_key_hex, MAX_KEY_LEN, value);
if (strcmp(key, "my_private_key") == 0) {
return assign_string(global->my_private_key_hex, MAX_KEY_LEN, value);
}
if (strcmp(key, "my_public_key") == 0) {
return assign_string(global->my_public_key_hex, MAX_KEY_LEN, value);
}
if (strcmp(key, "my_node_id") == 0) {
global->my_node_id = strtoull(value, NULL, 16);
return 0;
}
if (strcmp(key, "tun_ip") == 0) return assign_string(global->tun_ip, MAX_ADDR_LEN, value);
if (strcmp(key, "mtu") == 0) { global->mtu = atoi(value); return 0; }
if (strcmp(key, "control_ip") == 0) return assign_string(global->control_ip, MAX_ADDR_LEN, value);
if (strcmp(key, "control_port") == 0) { global->control_port = (uint16_t)atoi(value); return 0; }
if (strcmp(key, "net_debug") == 0) { global->net_debug = atoi(value); return 0; }
if (strcmp(key, "tun_ip") == 0) {
uint8_t netmask;
return parse_ip_with_netmask(value, &global->tun_ip, &netmask);
}
if (strcmp(key, "mtu") == 0) {
global->mtu = atoi(value);
return 0;
}
if (strcmp(key, "control_ip") == 0) {
// Store for later processing with control_port
return 0; // We'll handle this when we see control_port
}
if (strcmp(key, "control_port") == 0) {
// This is tricky - we need to get control_ip from previous parsing
// For now, we'll use a simple approach
struct global_config temp_global = *global;
// Assume we stored control_ip somewhere or use default
char control_ip[MAX_ADDR_LEN] = "127.0.0.1"; // Default
char port_str[16];
snprintf(port_str, sizeof(port_str), "%s", value);
parse_sockaddr(control_ip, port_str, &global->control_sock);
return 0;
}
if (strcmp(key, "net_debug") == 0) {
global->net_debug = atoi(value);
return 0;
}
return 0;
}
static int parse_server(const char *key, const char *value, struct server_config *srv) {
if (strcmp(key, "addr") == 0) return assign_string(srv->addr, MAX_ADDR_LEN, value);
if (strcmp(key, "so_mark") == 0) { srv->so_mark = atoi(value); return 0; }
if (strcmp(key, "netif") == 0) return assign_string(srv->netif, MAX_NETIF_LEN, value);
if (strcmp(key, "type") == 0) return 0; // Ignored: not stored or used
static int parse_server(const char *key, const char *value, struct CFG_SERVER *srv) {
if (strcmp(key, "addr") == 0) {
return parse_address_and_port(value, &srv->ip);
}
if (strcmp(key, "so_mark") == 0) {
srv->so_mark = atoi(value);
return 0;
}
if (strcmp(key, "netif") == 0) {
srv->netif_index = get_netif_index(value);
return 0;
}
if (strcmp(key, "type") == 0) {
if (strcmp(value, "public") == 0) {
srv->type = CFG_SERVER_TYPE_PUBLIC;
} else if (strcmp(value, "nat") == 0) {
srv->type = CFG_SERVER_TYPE_NAT;
} else if (strcmp(value, "private") == 0) {
srv->type = CFG_SERVER_TYPE_PRIVATE;
} else {
srv->type = CFG_SERVER_TYPE_UNKNOWN;
}
return 0;
}
return 0;
}
static int parse_client(const char *key, const char *value, struct client_config *cli) {
// Handle link=server:ip:port format
static int parse_client(const char *key, const char *value, struct CFG_CLIENT *cli, struct CFG_SERVER *servers) {
if (strcmp(key, "link") == 0) {
char link_copy[MAX_CONN_NAME_LEN * 2];
char link_copy[MAX_CONN_NAME_LEN + MAX_ADDR_LEN];
if (strlen(value) >= sizeof(link_copy)) return -1;
strcpy(link_copy, value);
@ -147,32 +269,32 @@ static int parse_client(const char *key, const char *value, struct client_config
*first_colon = '\0';
if (assign_string(cli->from, MAX_CONN_NAME_LEN, link_copy) < 0) return -1;
if (assign_string(cli->to_addr, MAX_ADDR_LEN, first_colon + 1) < 0) return -1;
return 1; // Signal that client is ready to be added
// Find server by name
struct CFG_SERVER *local_srv = find_server_by_name(servers, link_copy);
if (!local_srv) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Server '%s' not found for client link", link_copy);
return -1;
}
if (strcmp(key, "from") == 0) return assign_string(cli->from, MAX_CONN_NAME_LEN, value);
if (strcmp(key, "to_addr") == 0) return assign_string(cli->to_addr, MAX_ADDR_LEN, value);
if (strcmp(key, "peer_public_key") == 0) return assign_string(cli->peer_public_key_hex, MAX_KEY_LEN, value);
return 0;
}
struct CFG_CLIENT_LINK *new_link = create_client_link(local_srv, first_colon + 1);
if (!new_link) return -1;
static int parse_connection(const char *key, const char *value, struct connection_config_v2 *conn) {
if (strcmp(key, "link") == 0) {
char link_copy[MAX_CONN_NAME_LEN * 2];
if (strlen(value) >= sizeof(link_copy)) return -1;
strcpy(link_copy, value);
// Add to linked list (prepend)
new_link->next = cli->links;
cli->links = new_link;
char *colon = strchr(link_copy, ':');
if (!colon) return -1;
*colon = '\0';
return 0;
}
return add_route(NULL, link_copy, colon + 1); // NULL cfg - will use global
if (strcmp(key, "peer_public_key") == 0) {
return assign_string(cli->peer_public_key_hex, MAX_KEY_LEN, value);
}
if (strcmp(key, "keepalive") == 0) { conn->keepalive = atoi(value); return 0; }
if (strcmp(key, "peer_public_key") == 0) return assign_string(conn->peer_public_key_hex, MAX_KEY_LEN, value);
if (strcmp(key, "keepalive") == 0) {
cli->keepalive = atoi(value);
return 0;
}
return 0;
}
@ -204,7 +326,6 @@ static section_type_t parse_section_header(const char *line, char *name, size_t
if (strcasecmp(type, "server") == 0) return SECTION_SERVER;
if (strcasecmp(type, "client") == 0) return SECTION_CLIENT;
if (strcasecmp(type, "connection") == 0) return SECTION_CONNECTION;
return SECTION_UNKNOWN;
}
@ -214,8 +335,8 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
if (!cfg) return NULL;
section_type_t cur_section = SECTION_UNKNOWN;
struct server_config cur_server = {0};
struct client_config cur_client = {0};
struct CFG_SERVER *cur_server = NULL;
struct CFG_CLIENT *cur_client = NULL;
char line[MAX_LINE_LEN];
int line_num = 0;
@ -226,22 +347,31 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
if (trimmed[0] == '\0' || trimmed[0] == '#') continue;
if (trimmed[0] == '[') {
if (cur_section == SECTION_SERVER && cur_server.name[0]) {
if (add_server(cfg, &cur_server) < 0) goto error;
// Handle previous section
if (cur_section == SECTION_SERVER && cur_server) {
// Add server to linked list
cur_server->next = cfg->servers;
cfg->servers = cur_server;
cur_server = NULL;
}
if (cur_section == SECTION_CLIENT && cur_client.name[0]) {
if (add_client(cfg, &cur_client) < 0) goto error;
if (cur_section == SECTION_CLIENT && cur_client) {
// Add client to linked list
cur_client->next = cfg->clients;
cfg->clients = cur_client;
cur_client = NULL;
}
char name[MAX_CONN_NAME_LEN];
cur_section = parse_section_header(trimmed, name, sizeof(name));
if (cur_section == SECTION_SERVER) {
memset(&cur_server, 0, sizeof(cur_server));
strcpy(cur_server.name, name);
cur_server = calloc(1, sizeof(struct CFG_SERVER));
if (!cur_server) goto error;
strcpy(cur_server->name, name);
} else if (cur_section == SECTION_CLIENT) {
memset(&cur_client, 0, sizeof(cur_client));
strcpy(cur_client.name, name);
cur_client = calloc(1, sizeof(struct CFG_CLIENT));
if (!cur_client) goto error;
strcpy(cur_client->name, name);
}
continue;
}
@ -259,34 +389,20 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
}
break;
case SECTION_SERVER:
if (parse_server(key, value, &cur_server) < 0) {
if (cur_server && parse_server(key, value, cur_server) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid server key '%s'", filename, line_num, key);
}
break;
case SECTION_CLIENT:
{
int ret = parse_client(key, value, &cur_client);
if (ret == 1) {
// Client ready to add (from link=)
char saved_name[MAX_CONN_NAME_LEN];
strcpy(saved_name, cur_client.name); // Save name
if (add_client(cfg, &cur_client) < 0) goto error;
memset(&cur_client, 0, sizeof(cur_client));
strcpy(cur_client.name, saved_name); // Restore name for next link
} else if (ret < 0) {
if (cur_client && parse_client(key, value, cur_client, cfg->servers) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid client key '%s'", filename, line_num, key);
}
}
break;
case SECTION_ROUTING:
// Routing settings are not stored yet, just ignore
break;
case SECTION_CONNECTION:
if (cfg->connection_v2_count == 0) {
cfg->connection_v2_count = 1;
}
if (parse_connection(key, value, &cfg->connections_v2[0]) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "%s:%d: Invalid connection key '%s'", filename, line_num, key);
if (strcmp(key, "allowed_subnet") == 0) {
add_route_entry(&cfg->allowed_subnets, value);
} else if (strcmp(key, "my_subnet") == 0) {
add_route_entry(&cfg->my_subnets, value);
}
break;
default:
@ -295,16 +411,24 @@ static struct utun_config* parse_config_internal(FILE *fp, const char *filename)
}
}
if (cur_section == SECTION_SERVER && cur_server.name[0]) {
if (add_server(cfg, &cur_server) < 0) goto error;
// Handle final sections
if (cur_section == SECTION_SERVER && cur_server) {
cur_server->next = cfg->servers;
cfg->servers = cur_server;
}
if (cur_section == SECTION_CLIENT && cur_client.name[0] && cur_client.to_addr[0]) {
if (add_client(cfg, &cur_client) < 0) goto error;
if (cur_section == SECTION_CLIENT && cur_client) {
cur_client->next = cfg->clients;
cfg->clients = cur_client;
}
return cfg;
error:
if (cur_server) free(cur_server);
if (cur_client) {
free_cfg_client_links(cur_client->links);
free(cur_client);
}
free_config(cfg);
return NULL;
}
@ -324,53 +448,97 @@ struct utun_config* parse_config(const char *filename) {
void free_config(struct utun_config *config) {
if (!config) return;
free(config->servers);
free(config->clients);
if (config->connections_v2) {
for (int i = 0; i < config->connection_v2_count; i++) {
free(config->connections_v2[i].routes);
// Free servers
struct CFG_SERVER *server = config->servers;
while (server) {
struct CFG_SERVER *next = server->next;
free(server);
server = next;
}
free(config->connections_v2);
// Free clients and their links
struct CFG_CLIENT *client = config->clients;
while (client) {
struct CFG_CLIENT *next = client->next;
free_cfg_client_links(client->links);
free(client);
client = next;
}
// Free route entries
free_route_entries(config->allowed_subnets);
free_route_entries(config->my_subnets);
free(config);
}
static const char* ip_to_string(const struct IP *ip, char *buffer, size_t buffer_size) {
if (ip->family == AF_INET) {
inet_ntop(AF_INET, &ip->addr.v4, buffer, buffer_size);
} else if (ip->family == AF_INET6) {
inet_ntop(AF_INET6, &ip->addr.v6, buffer, buffer_size);
} else {
snprintf(buffer, buffer_size, "invalid");
}
return buffer;
}
void print_config(const struct utun_config *cfg) {
if (!cfg) return;
const struct global_config *g = &cfg->global;
char ip_buffer[64];
printf("Global:\n");
printf(" Private key: %s\n", g->my_private_key_hex);
printf(" Public key: %s\n", g->my_public_key_hex);
printf(" Node ID: %llx\n", (unsigned long long)g->my_node_id);
printf(" TUN IP: %s\n", g->tun_ip);
printf(" TUN IP: %s\n", ip_to_string(&g->tun_ip, ip_buffer, sizeof(ip_buffer)));
printf(" MTU: %d\n", g->mtu);
printf(" Control: %s:%u\n", g->control_ip, g->control_port);
printf(" Net debug: %d\n", g->net_debug);
printf("\nServers (%d):\n", cfg->server_count);
for (int i = 0; i < cfg->server_count; i++) {
const struct server_config *s = &cfg->servers[i];
printf(" %s: %s (mark=%d, netif=%s)\n", s->name, s->addr, s->so_mark, s->netif);
printf("\nServers:\n");
struct CFG_SERVER *s = cfg->servers;
while (s) {
char addr_buffer[64];
struct sockaddr_in *sin = (struct sockaddr_in *)&s->ip;
inet_ntop(AF_INET, &sin->sin_addr, addr_buffer, sizeof(addr_buffer));
printf(" %s: %s:%d (mark=%d, netif=%u, type=%u)\n",
s->name, addr_buffer, ntohs(sin->sin_port),
s->so_mark, s->netif_index, s->type);
s = s->next;
}
printf("\nClients (%d):\n", cfg->client_count);
for (int i = 0; i < cfg->client_count; i++) {
const struct client_config *c = &cfg->clients[i];
printf(" %s: %s (from %s)\n", c->name, c->to_addr, c->from);
printf("\nClients:\n");
struct CFG_CLIENT *c = cfg->clients;
while (c) {
printf(" %s:\n", c->name);
struct CFG_CLIENT_LINK *link = c->links;
int link_num = 1;
while (link) {
char addr_buffer[64];
struct sockaddr_in *sin = (struct sockaddr_in *)&link->remote_addr;
inet_ntop(AF_INET, &sin->sin_addr, addr_buffer, sizeof(addr_buffer));
printf(" Link %d: %s:%d (via %s)\n", link_num++, addr_buffer, ntohs(sin->sin_port), link->local_srv->name);
link = link->next;
}
printf(" Peer key: %s\n", c->peer_public_key_hex);
printf(" Keepalive: %d\n", c->keepalive);
c = c->next;
}
printf("\nConnections v2 (%d):\n", cfg->connection_v2_count);
for (int i = 0; i < cfg->connection_v2_count; i++) {
const struct connection_config_v2 *conn = &cfg->connections_v2[i];
printf(" Connection %d:\n", i);
for (int j = 0; j < conn->route_count; j++) {
printf(" %s -> %s\n", conn->routes[j].server_name, conn->routes[j].client_name);
printf("\nAllowed Subnets:\n");
struct CFG_ROUTE_ENTRY *allowed = cfg->allowed_subnets;
while (allowed) {
printf(" %s/%d\n", ip_to_string(&allowed->ip, ip_buffer, sizeof(ip_buffer)), allowed->netmask);
allowed = allowed->next;
}
printf(" Peer key: %s\n", conn->peer_public_key_hex);
printf(" Keepalive: %d\n", conn->keepalive);
printf("\nMy Subnets:\n");
struct CFG_ROUTE_ENTRY *my = cfg->my_subnets;
while (my) {
printf(" %s/%d\n", ip_to_string(&my->ip, ip_buffer, sizeof(ip_buffer)), my->netmask);
my = my->next;
}
}

78
src/config_parser.h

@ -4,6 +4,9 @@
#include <stdint.h>
#include <stddef.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef __cplusplus
extern "C" {
@ -12,66 +15,65 @@ extern "C" {
#define MAX_CONN_NAME_LEN 64
#define MAX_KEY_LEN 256
#define MAX_ADDR_LEN 64
#define MAX_NETIF_LEN 16
#define MAX_OPTION_VALUE_LEN 256
#define MAX_ALLOWED_SUBNETS 32
struct subnet_entry {
char subnet[MAX_ADDR_LEN];
struct IP {
sa_family_t family;
union {
struct in_addr v4;
struct in6_addr v6;
} addr;
};
struct server_config {
char name[MAX_CONN_NAME_LEN];
char addr[MAX_ADDR_LEN];
int so_mark;
char netif[MAX_NETIF_LEN];
char type[16]; // public/nat/private - parsed but not currently used
};
#define CFG_SERVER_TYPE_UNKNOWN 0
#define CFG_SERVER_TYPE_PUBLIC 1
#define CFG_SERVER_TYPE_NAT 2
#define CFG_SERVER_TYPE_PRIVATE 3
struct client_config {
struct CFG_SERVER {
struct CFG_SERVER* next;
char name[MAX_CONN_NAME_LEN];
char from[MAX_CONN_NAME_LEN];
char to_addr[MAX_ADDR_LEN];
char peer_public_key_hex[MAX_KEY_LEN];
struct sockaddr_storage ip; // ip:port
uint32_t netif_index;// if_nameindex, 0 - no interface specified
int so_mark;
uint8_t type; // public/nat/private
};
struct route_pair {
char server_name[MAX_CONN_NAME_LEN];
char client_name[MAX_CONN_NAME_LEN];
struct CFG_CLIENT_LINK {
struct CFG_CLIENT_LINK *next; // Next link in linked list
struct CFG_SERVER* local_srv;
struct sockaddr_storage remote_addr; // ip:port
};
struct connection_config_v2 {
struct route_pair *routes;
int route_count;
int route_capacity;
struct CFG_CLIENT {
char name[MAX_CONN_NAME_LEN];
char peer_public_key_hex[MAX_KEY_LEN];
int keepalive;
struct CFG_CLIENT_LINK *links; // Linked list of links
struct CFG_CLIENT *next; // Next client in linked list
};
struct CFG_ROUTE_ENTRY {
struct CFG_ROUTE_ENTRY* next;
struct IP ip;
uint8_t netmask;
};
struct global_config {
char my_private_key_hex[MAX_KEY_LEN];
char my_public_key_hex[MAX_KEY_LEN];
uint64_t my_node_id;
char tun_ip[MAX_ADDR_LEN];
struct IP tun_ip;
int mtu;
char control_ip[MAX_ADDR_LEN];
uint16_t control_port;
struct sockaddr_storage control_sock;
int net_debug;
};
struct utun_config {
struct global_config global;
struct server_config *servers;
int server_count;
int server_capacity;
struct client_config *clients;
int client_count;
int client_capacity;
struct connection_config_v2 *connections_v2;
int connection_v2_count;
int connection_v2_capacity;
struct subnet_entry allowed_subnets[MAX_ALLOWED_SUBNETS];
int allowed_subnet_count;
struct CFG_SERVER* servers;
struct CFG_CLIENT* clients;
struct CFG_ROUTE_ENTRY* allowed_subnets;
struct CFG_ROUTE_ENTRY* my_subnets;
};
struct utun_config* parse_config(const char *filename);
@ -79,8 +81,6 @@ void free_config(struct utun_config *config);
void print_config(const struct utun_config *config);
int update_config_keys(const char *filename, const char *priv_key, const char *pub_key);
void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex_str, size_t hex_len);
#ifdef __cplusplus
}
#endif

17
src/config_updater.c

@ -16,6 +16,15 @@
#define HEXNODEID_LEN 16 // 8 bytes * 2 hex chars
#define MAX_LINE_LEN 1024
void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex_str, size_t hex_len) {
if (!bytes || !hex_str || hex_len < len * 2 + 1) return;
for (size_t i = 0; i < len; i++) {
snprintf(hex_str + i * 2, hex_len - i * 2, "%02x", bytes[i]);
}
hex_str[len * 2] = '\0';
}
static int is_valid_hex_key(const char *key) {
if (!key || strlen(key) != HEXKEY_LEN) return 0;
for (int i = 0; i < HEXKEY_LEN; i++) {
@ -210,14 +219,14 @@ int config_ensure_keys_and_node_id(const char *filename) {
uint64_t new_node_id = 0;
if (need_priv_key || need_pub_key) {
struct secure_channel ctx;
if (sc_generate_keypair(&ctx) != SC_OK) {
struct SC_MYKEYS mykeys;
if (sc_generate_keypair(&mykeys) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CONFIG, "Failed to generate keypair");
free_config(config);
return -1;
}
bytes_to_hex(ctx.private_key, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
bytes_to_hex(ctx.public_key, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key));
bytes_to_hex(mykeys.private_key, SC_PRIVKEY_SIZE, new_priv_key, sizeof(new_priv_key));
bytes_to_hex(mykeys.public_key, SC_PUBKEY_SIZE, new_pub_key, sizeof(new_pub_key));
}
if (need_node_id) {

5
src/config_updater.h

@ -2,6 +2,9 @@
#ifndef CONFIG_UPDATER_H
#define CONFIG_UPDATER_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
@ -11,6 +14,8 @@ extern "C" {
// Returns 0 on success, -1 on error
int config_ensure_keys_and_node_id(const char *filename);
void bytes_to_hex(const uint8_t *bytes, size_t len, char *hex_str, size_t hex_len);
#ifdef __cplusplus
}
#endif

470
src/etcp.c

@ -1,43 +1,142 @@
// etcp.c - ETCP Protocol Implementation
#include "u_async.h"
#include "utun_instance.h"
#include "etcp.h"
#include "../lib/debug_config.h"
#include "crc32.h"
#include "secure_channel.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <time.h>
// Service packet headers (from protocol, but reset/init handled elsewhere)
#define ETCP_ACK_HEADER 0x01
#define ETCP_RETRANS_HEADER_BASE 0x10 // 0x10 to 0x2F for retrans requests
#define ETCP_PAYLOAD_HEADER 0x00
// Forward declarations of internal functions (adapted from etcp_master)
static void retransmit_check(struct ETCP_CONN* etcp);
static void update_metrics(struct ETCP_CONN* etcp, uint16_t rtt);
static uint16_t get_current_timestamp(void);
static uint16_t timestamp_diff(uint16_t t1, uint16_t t2);
static int id_compare(uint16_t id1, uint16_t id2);
static void retransmit_packet(struct ETCP_CONN* etcp, uint16_t id);
// Timer callbacks
static void tx_timer_callback(void* arg);
static void retransmit_timer_callback(void* arg);
// Internal queue callbacks
static void tx_queue_callback(struct ll_queue* q, struct ll_entry* entry, void* arg);
static void schedule_ack_timer(struct ETCP_CONN* etcp);
static void request_retransmission_for_gaps(struct ETCP_CONN* etcp);
static void queue_clear(struct ll_queue* q) {
struct ll_entry* entry;
while ((entry = queue_entry_get(q)) != NULL) {
queue_entry_free(entry);
}
}
static void etcp_update_window(struct ETCP_CONN* etcp) {
etcp->window_size = 65536; // Initial window size
etcp->retrans_timer_period = 20; // Default retransmit period
}
// Creating ETCP instance
struct ETCP_CONN* etcp_create(struct UTUN_INSTANCE* instance) {
if (!instance) return NULL;
// после создания надо добавить peer bublic key.
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* utun) {
if (!utun) return NULL;
struct ETCP_CONN* etcp = calloc(1, sizeof(struct ETCP_CONN));
if (!etcp) return NULL;
// TODO: generate real random node_id
etcp->node_id = 0x123456789ABCDEF0;
etcp->mtu = 1500; // Default MTU
etcp->instance = instance;
etcp->instance = utun;
etcp->state = 0;
// Initialize crypto context
etcp->crypto_ctx = calloc(1, sizeof(struct secure_channel));
if (!etcp->crypto_ctx) {
if (sc_init_ctx(&etcp->crypto_ctx, &etcp->instance->my_keys) != SC_OK) {
free(etcp);
return NULL;
}
if (sc_generate_keypair(etcp->crypto_ctx) != SC_OK) {
free(etcp->crypto_ctx);
// Initialize queues (tx_queue is input_queue)
etcp->input_queue = queue_new(utun->ua,NULL);
etcp->output_queue = queue_new(utun->ua,NULL);
if (!etcp->input_queue || !etcp->output_queue) {
if (etcp->input_queue) queue_free(etcp->input_queue);
if (etcp->output_queue) queue_free(etcp->output_queue);
free(etcp);
return NULL;
}
// Initialize packet pool
packet_pool_init(&etcp->packet_pool);
// Set callback for input_queue (tx_queue)
queue_set_callback(etcp->input_queue, tx_queue_callback, etcp);
// Initialize state (from master)
etcp->bandwidth = 10000; // Default: 10000 bytes per timebase (0.1us)
etcp->last_sent_timestamp = get_current_timestamp();
etcp->bytes_allowed = 0;
etcp_update_window(etcp); // Initialize window size and retrans timer
// Initialize lists
etcp->rx_list = NULL;
etcp->sent_list = NULL;
// Initialize metrics
etcp->rtt_last = 0;
etcp->rtt_avg_10 = 0;
etcp->rtt_avg_100 = 0;
etcp->jitter = 0;
etcp->bytes_sent_total = 0;
// Initialize statistics
etcp->retransmissions_count = 0;
etcp->ack_packets_count = 0;
etcp->control_packets_count = 0;
etcp->total_packets_sent = 0;
etcp->unique_packets_sent = 0;
etcp->bytes_received_total = 0;
// Initialize IDs
etcp->next_tx_id = 1;
etcp->last_sent_id = 0;
etcp->last_rx_id = 0;
etcp->last_delivered_id = 0;
// Initialize history
etcp->rtt_history_idx = 0;
etcp->rtt_history_count = 0;
// Initialize pending arrays
etcp->pending_ack_count = 0;
etcp->pending_retransmit_count = 0;
// Initialize window management
etcp->unacked_bytes = 0;
etcp->last_acked_id = 0;
etcp->last_rx_ack_id = 0;
etcp->retrans_timer_period = 20; // Default 2ms (20 timebase units)
etcp->next_retrans_time = 0;
etcp->window_blocked = 0;
// Forward progress tracking
etcp->oldest_missing_id = 0;
etcp->missing_since_time = 0;
// No timers yet
etcp->next_tx_timer = NULL;
etcp->retransmit_timer = NULL;
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_create: created instance with node_id=%llu, mtu=%d",
(unsigned long long)etcp->node_id, etcp->mtu);
(unsigned long long)etcp->instance->node_id, etcp->mtu);
etcp->next=utun->connections;
utun->connections=etcp;
return etcp;
}
@ -48,30 +147,185 @@ void etcp_destroy(struct ETCP_CONN* etcp) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "etcp_destroy: destroying instance");
// Free crypto context
if (etcp->crypto_ctx) {
// Secure channel context doesn't need special cleanup
free(etcp->crypto_ctx);
// Cancel timers
if (etcp->next_tx_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->next_tx_timer);
etcp->next_tx_timer = NULL;
}
if (etcp->retransmit_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->retransmit_timer);
etcp->retransmit_timer = NULL;
}
// Free queues
if (etcp->input_queue) queue_free(etcp->input_queue);
if (etcp->output_queue) queue_free(etcp->output_queue);
// Free rx_list
rx_packet_t* rx = etcp->rx_list;
while (rx) {
rx_packet_t* next = rx->next;
if (rx->data) free(rx->data);
free(rx);
rx = next;
}
// Clean packet pool
packet_pool_destroy(&etcp->packet_pool);
// Free sent_list
sent_packet_t* sent = etcp->sent_list;
while (sent) {
sent_packet_t* next = sent->next;
if (sent->data) free(sent->data);
free(sent);
sent = next;
}
free(etcp);
}
// Process incoming packet
void etcp_conn_input(struct ETCP_CONN* etcp, struct packet_buffer* pkt) {
// Process incoming packet (partial, truncated in original)
void etcp_conn_input(struct ETCP_CONN* etcp, struct ETCP_DGRAM* pkt) {
if (!etcp || !pkt) return;
DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "etcp_conn_input: packet len=%u", pkt->metadata.data_len);
// пока отправляй сразу в очередь. и из очереди сразу принимай через callback
uint8_t* data = pkt->data;
size_t len = pkt->data_len;
if (len < 4) return; // Min header
// TODO: process packet through ETCP protocol
// This will be implemented later for data transfer
uint16_t id = (data[0] << 8) | data[1];
uint16_t timestamp = (data[2] << 8) | data[3];
data += 4; len -= 4;
int has_payload = 0;
uint8_t* payload_data = NULL;
size_t payload_len = 0;
int is_duplicate = 0;
while (len > 0) {
uint8_t section_header = *data++;
len--;
if (section_header == ETCP_ACK_HEADER) {
// ACK section
uint8_t count = *data++;
len--;
for (uint8_t i = 0; i < count; i++) {
uint16_t ack_id = (data[0] << 8) | data[1];
uint16_t ack_ts = (data[2] << 8) | data[3];
data += 4; len -= 4;
// Process ACK: remove from sent_list, update window
sent_packet_t** ptr = &etcp->sent_list;
while (*ptr) {
if ((*ptr)->id == ack_id) {
sent_packet_t* to_free = *ptr;
*ptr = to_free->next;
etcp->unacked_bytes -= to_free->payload_len;
update_metrics(etcp, timestamp_diff(get_current_timestamp(), ack_ts));
free(to_free->data);
free(to_free);
break;
}
ptr = &(*ptr)->next;
}
}
// last_delivered and last_rx
uint16_t last_delivered = (data[0] << 8) | data[1];
uint16_t last_rx = (data[2] << 8) | data[3];
data += 4; len -= 4;
etcp->last_rx_ack_id = last_rx;
} else if ((section_header & 0xF0) == ETCP_RETRANS_HEADER_BASE) {
// Retrans request
uint8_t count = (section_header & 0x0F) + 1;
for (uint8_t i = 0; i < count; i++) {
uint16_t retrans_id = (data[0] << 8) | data[1];
data += 2;
len -= 2;
retransmit_packet(etcp, retrans_id);
}
// last_delivered and last_rx
uint16_t last_delivered = (data[0] << 8) | data[1];
uint16_t last_rx = (data[2] << 8) | data[3];
data += 4; len -= 4;
} else if (section_header == ETCP_PAYLOAD_HEADER) {
// Payload section
has_payload = 1;
payload_data = data;
payload_len = len;
break; // Payload is last
}
}
// Check for duplicate
rx_packet_t* rx = etcp->rx_list;
while (rx) {
if (rx->id == id) {
is_duplicate = 1;
break;
}
rx = rx->next;
}
if (is_duplicate) {
// Add to pending ACKs
if (etcp->pending_ack_count < 32) {
etcp->pending_ack_ids[etcp->pending_ack_count] = id;
etcp->pending_ack_timestamps[etcp->pending_ack_count] = timestamp;
etcp->pending_ack_count++;
}
schedule_ack_timer(etcp);
return;
}
// Insert into rx_list (sorted)
rx_packet_t* new_rx = calloc(1, sizeof(rx_packet_t));
if (!new_rx) return;
new_rx->id = id;
new_rx->timestamp = timestamp;
new_rx->has_payload = has_payload;
if (has_payload) {
new_rx->data = malloc(payload_len);
if (!new_rx->data) {
free(new_rx);
return;
}
memcpy(new_rx->data, payload_data, payload_len);
new_rx->data_len = payload_len;
etcp->bytes_received_total += payload_len;
}
// Insert sorted
rx_packet_t** ptr = &etcp->rx_list;
while (*ptr && id_compare((*ptr)->id, id) < 0) {
ptr = &(*ptr)->next;
}
new_rx->next = *ptr;
*ptr = new_rx;
// Update last_rx_id
if (id_compare(id, etcp->last_rx_id) > 0) {
etcp->last_rx_id = id;
}
// Add to pending ACKs
if (etcp->pending_ack_count < 32) {
etcp->pending_ack_ids[etcp->pending_ack_count] = id;
etcp->pending_ack_timestamps[etcp->pending_ack_count] = timestamp;
etcp->pending_ack_count++;
}
// Request retrans for gaps and deliver contiguous
request_retransmission_for_gaps(etcp);
schedule_ack_timer(etcp);
}
// Reset connection
// Reset connection (adapted from etcp_reset in master, without packet sending)
void etcp_conn_reset(struct ETCP_CONN* etcp) {
if (!etcp) return;
@ -80,19 +334,173 @@ void etcp_conn_reset(struct ETCP_CONN* etcp) {
// Reset state
etcp->state = 0;
// TODO: clear queues, reset sequence numbers, etc.
// Cancel timers
if (etcp->next_tx_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->next_tx_timer);
etcp->next_tx_timer = NULL;
}
if (etcp->retransmit_timer) {
uasync_cancel_timeout(etcp->instance->ua, etcp->retransmit_timer);
etcp->retransmit_timer = NULL;
}
// Clear queues (but keep them)
queue_clear(etcp->input_queue);
queue_clear(etcp->output_queue);
// Clear lists
rx_packet_t* rx = etcp->rx_list;
while (rx) {
rx_packet_t* next = rx->next;
if (rx->data) free(rx->data);
free(rx);
rx = next;
}
etcp->rx_list = NULL;
sent_packet_t* sent = etcp->sent_list;
while (sent) {
sent_packet_t* next = sent->next;
if (sent->data) free(sent->data);
free(sent);
sent = next;
}
etcp->sent_list = NULL;
// Reset metrics and stats
etcp->rtt_last = 0;
etcp->rtt_avg_10 = 0;
etcp->rtt_avg_100 = 0;
etcp->jitter = 0;
etcp->bytes_sent_total = 0;
etcp->retransmissions_count = 0;
etcp->ack_packets_count = 0;
etcp->control_packets_count = 0;
etcp->total_packets_sent = 0;
etcp->unique_packets_sent = 0;
etcp->bytes_received_total = 0;
// Reset IDs
etcp->next_tx_id = 1;
etcp->last_sent_id = 0;
etcp->last_rx_id = 0;
etcp->last_delivered_id = 0;
// Reset history
etcp->rtt_history_idx = 0;
etcp->rtt_history_count = 0;
// Reset pending
etcp->pending_ack_count = 0;
etcp->pending_retransmit_count = 0;
// Reset window
etcp->unacked_bytes = 0;
etcp->last_acked_id = 0;
etcp->last_rx_ack_id = 0;
etcp->next_retrans_time = 0;
etcp->window_blocked = 0;
// Reset forward progress
etcp->oldest_missing_id = 0;
etcp->missing_since_time = 0;
etcp_update_window(etcp);
}
// Getting statistics
// Getting statistics (extended with master stats)
void etcp_get_stats(struct ETCP_CONN* etcp, size_t* packets_sent, size_t* packets_recv,
size_t* pool_allocs, size_t* pool_reuse) {
if (!etcp) return;
if (packets_sent) *packets_sent = 0; // TODO: track
if (packets_recv) *packets_recv = 0; // TODO: track
if (packets_sent) *packets_sent = etcp->total_packets_sent;
if (packets_recv) *packets_recv = etcp->bytes_received_total / 100; // Approximate
if (pool_allocs || pool_reuse) {
size_t dummy_overflow;
packet_pool_get_stats(&etcp->packet_pool, pool_allocs, pool_reuse, &dummy_overflow);
memory_pool_get_stats(etcp->instance->pkt_pool, pool_allocs, pool_reuse);
}
}
// ... (rest of functions from master, adapted to ETCP_CONN and using etcp_link_send for tx)
// For tx: assume primary channel (etcp->channels), or iterate if multi-path
static void send_etcp_packet(struct ETCP_CONN* etcp, uint8_t* pkt, uint16_t len) {
if (!etcp->links) return; // No links
// Send to primary link (or iterate for multi-path)
char dg_mem[1600];
struct ETCP_DGRAM* dg=(struct ETCP_DGRAM*)&dg_mem;
dg->data_len=len;
dg->noencrypt_len=0;
dg->link=etcp->links;// first link TODO: добавить отправку по нескольким линкам
etcp_encrypt_send(dg);
}
static void update_metrics(struct ETCP_CONN* etcp, uint16_t rtt) {
etcp->rtt_last = rtt;
// Update averages, jitter, etc.
// Placeholder: etcp->rtt_avg_10 = (etcp->rtt_avg_10 * 9 + rtt) / 10;
// etcp->rtt_avg_100 = (etcp->rtt_avg_100 * 99 + rtt) / 100;
}
static uint16_t get_current_timestamp(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
return (uint16_t)(tv.tv_usec / 100); // Timebase 0.1ms
}
static uint16_t timestamp_diff(uint16_t t1, uint16_t t2) {
if (t1 >= t2) return t1 - t2;
return (0xFFFF - t2) + t1 + 1; // Wrap around
}
static int id_compare(uint16_t id1, uint16_t id2) {
int16_t diff = id1 - id2;
if (diff == 0) return 0;
return (diff > 0) ? 1 : -1; // Simple for now, add wrap logic if needed
}
static void retransmit_packet(struct ETCP_CONN* etcp, uint16_t id) {
sent_packet_t* sent = etcp->sent_list;
while (sent) {
if (sent->id == id) {
send_etcp_packet(etcp, sent->data, sent->data_len);
etcp->retransmissions_count++;
break;
}
sent = sent->next;
}
}
static void tx_queue_callback(struct ll_queue* q, struct ll_entry* entry, void* arg) {
(void)q;
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
// Process entry to send packet
// Placeholder: uint8_t* data = ll_entry_data(entry);
// size_t len = ll_entry_size(entry);
// // Build packet and send
// send_etcp_packet(etcp, data, len);
queue_resume_callback(q);
}
static void schedule_ack_timer(struct ETCP_CONN* etcp) {
// Placeholder: set timeout to send ACKs
}
static void request_retransmission_for_gaps(struct ETCP_CONN* etcp) {
// Detect gaps in rx_list and add to pending_retransmit
}
static void retransmit_check(struct ETCP_CONN* etcp) {
// Check sent_list for timeouts
}
static void tx_timer_callback(void* arg) {
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
// Handle next tx
}
static void retransmit_timer_callback(void* arg) {
struct ETCP_CONN* etcp = (struct ETCP_CONN*)arg;
retransmit_check(etcp);
}

105
src/etcp.h

@ -2,9 +2,7 @@
#ifndef ETCP_H
#define ETCP_H
#include "etcp_sockets.h"
#include "etcp_connections.h"
#include "packet_pool.h"
#include "ll_queue.h"
#include "secure_channel.h"
#include <stdint.h>
@ -16,43 +14,118 @@ extern "C" {
// Forward declarations
struct UTUN_INSTANCE;
// Internal structures (integrated from etcp_master)
typedef struct rx_packet {
struct rx_packet* next;
uint16_t id;
uint16_t timestamp;
uint8_t* data;
uint16_t data_len;
uint8_t has_payload;
} rx_packet_t;
typedef struct sent_packet {
struct sent_packet* next;
uint16_t id;
uint16_t timestamp;
uint8_t* data;
uint16_t data_len; // Total packet length
uint16_t payload_len; // Payload length (for window accounting)
uint16_t send_time;
uint8_t need_ack;
uint8_t need_retransmit;
} sent_packet_t;
struct ETCP_CONN {
// Node ID (64-bit, генерируется при первом запуске)
uint64_t node_id;
struct ETCP_CONN* next;
int mtu;
struct UTUN_INSTANCE* instance;
// Каналы (соединения) - linked list
struct ETCP_LINK* channels;
// Пул пакетов (общий для всего ETCP instance)
struct packet_pool packet_pool;
struct ETCP_LINK* links;
uint8_t state; // 0 - just created, 1 - initialized, 2 - connection established
// Криптография и состояние
struct secure_channel* crypto_ctx;
struct secure_channel crypto_ctx;
// Информация о пире (для клиентов)
uint64_t peer_node_id; // Node ID пира
uint8_t has_peer_key; // Флаг наличия публичного ключа пира
uint8_t peer_public_key[SC_PUBKEY_SIZE]; // Публичный ключ пира (бинарный)
struct ll_queue* input_queue;// отправитель -> input_queue -> etcp -> etcp_channel -> отправка (etcp_sockets)
struct ll_queue* output_queue;// etcp_sockets -> etcp_channel -> etcp -> output_queue -> получатель
struct ll_queue* input_queue; // отправитель -> input_queue -> etcp -> etcp_channel -> отправка (etcp_sockets)
struct ll_queue* output_queue; // etcp_sockets -> etcp_channel -> etcp -> output_queue -> получатель
// Integrated from etcp_master (retransmission logic)
rx_packet_t* rx_list;
sent_packet_t* sent_list;
// Metrics
uint16_t rtt_last;
uint16_t rtt_avg_10;
uint16_t rtt_avg_100;
uint16_t jitter;
uint16_t bandwidth; // Bytes per timebase (0.1us)
uint32_t bytes_sent_total;
uint16_t last_sent_timestamp;
uint32_t bytes_allowed;
// Statistics (extended existing get_stats)
uint32_t retransmissions_count;
uint32_t ack_packets_count;
uint32_t control_packets_count;
uint32_t total_packets_sent;
uint32_t unique_packets_sent;
uint32_t bytes_received_total;
// State
uint16_t next_tx_id;
uint16_t last_sent_id;
uint16_t last_rx_id;
uint16_t last_delivered_id;
// Timers
void* next_tx_timer;
void* retransmit_timer;
// RTT history
uint16_t rtt_history[100];
uint8_t rtt_history_idx;
uint8_t rtt_history_count;
// Pending ACKs
uint16_t pending_ack_ids[32];
uint16_t pending_ack_timestamps[32];
uint8_t pending_ack_count;
// Pending retransmits
uint16_t pending_retransmit_ids[32];
uint8_t pending_retransmit_count;
// Window management
uint32_t unacked_bytes;
uint32_t window_size;
uint16_t last_acked_id;
uint16_t last_rx_ack_id;
uint16_t retrans_timer_period; // Default 20 (2ms)
uint16_t next_retrans_time;
uint8_t window_blocked;
// Forward progress
uint16_t oldest_missing_id;
uint16_t missing_since_time;
// No reset/init here - handled in connections
};
// Создание пустого ETCP instance (после надо добавить ключи, каналы)
struct ETCP_CONN* etcp_create(struct UTUN_INSTANCE* instance);
struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance);
// Уничтожение ETCP instance
void etcp_destroy(struct ETCP_CONN* etcp);
// Обработать входящий пакет (вызывается из etcp_connections)
void etcp_conn_input(struct ETCP_CONN* etcp, struct packet_buffer* pkt);
void etcp_conn_input(struct ETCP_CONN* etcp, struct ETCP_DGRAM* pkt);
// Сброс соединения (переинициализация)
void etcp_conn_reset(struct ETCP_CONN* etcp);

5
src/etcp.h.demo

@ -1,5 +0,0 @@
// etcp.h - demo with original typedef
typedef struct ETCP_CONN etcp_t;
struct ETCP_CONN* etcp_create(utun_instance_t *instance);
void etcp_destroy(etcp_t* etcp);

1024
src/etcp_connections.c

File diff suppressed because it is too large Load Diff

100
src/etcp_connections.h

@ -1,14 +1,12 @@
#ifndef ETCP_CONNECTIONS_H
#define ETCP_CONNECTIONS_H
#include "packet_buffer.h"
#include "secure_channel.h"
#include "etcp_sockets.h"
#include "utun_instance.h"
#include <stdint.h>
#include <sys/socket.h>
// Forward declarations
struct UTUN_INSTANCE;
#define PACKET_DATA_SIZE 1536
// Типы кодограмм протокола
#define ETCP_INIT_REQUEST 0x02
@ -17,87 +15,71 @@ struct UTUN_INSTANCE;
#define ETCP_CHANNEL_RESPONSE 0x05
#define ETCP_RESET 0x06
// список активных подключений которые обслуживает сокет. каждый сокет может обслуживать много подключений
struct ETCP_CONNECTIONS {
struct ETCP_CONNECTIONS* next; // Linked list для всех соединений
// сокет для отправки/приема
struct ETCP_SOCKET socket; // Сокет через который идет трафик
struct ETCP_DGRAM {
struct ETCP_LINK* link;// откуда получена или куда отправялем
uint16_t data_len;
uint16_t noencrypt_len;// число байт (с конца) которые не надо шифровать. для передачи pubkey
uint8_t data[0];// сами данные
};
// список активных подключений которые обслуживает сокет. каждый сокет может обслуживать много подключений
struct ETCP_SOCKET {
struct ETCP_SOCKET* next; // Linked list для всех соединений
struct UTUN_INSTANCE* instance;
int fd; // Файловый дескриптор UDP сокета
struct sockaddr_storage local_addr; // Локальный адрес
// для входящих подключений - массив упорядоченный по ip_port_hash
// для входящих подключений (links) - массив упорядоченный по ip_port_hash
size_t max_channels; // сколько выделено памяти
size_t num_channels; // сколько активно
struct ETCP_LINK** links;// массив указателей на линки, сортированный по ip_port_hash
int errorcode;
size_t pkt_format_errors;
// Дополнительные поля для управления соединениями
struct ETCP_CONN* etcp; // Родительский ETCP instance
struct ETCP_CONNECTIONS* conns;// Указатель на себя (для совместимости)
// Статистика
size_t encrypt_errors;
size_t decrypt_errors;
size_t send_errors;
size_t recv_errors;
size_t total_encrypted;
size_t total_decrypted;
};
// ETCP Link - одно динамическое соединение (один путь)
struct ETCP_LINK {
uint32_t ip_port_hash; // crc32 для быстрого поиска
struct ETCP_LINK* next; // Linked list подключений для ETCP_CONN
struct ETCP_LINK* next; // Linked list подключений для ETCP_CONN (каждое подключение это child для ETCP_CONN)
struct ETCP_CONN* etcp; // подключение (parent)
struct ETCP_SOCKET* socket; // Сокет через который идет трафик
struct ETCP_CONN* etcp; // Родительский ETCP instance
struct ETCP_CONNECTIONS* conns; // Родительский connections (для статистики)
struct ETCP_SOCKET* conn; // сокет через который работаем
// Путь соединения
struct sockaddr_storage remote_addr; // Удалённый адрес
socklen_t remote_addr_len;
// Параметры соединения
uint16_t mtu; // MTU соединения
uint16_t keepalive_interval; // Keepalive интервал
uint8_t is_server; // инициирует подключение клиент
uint8_t initialized; // Флаг инициализации
uint64_t last_activity; // Время последней активности
};
// CONNECTIONS FUNCTIONS
struct ETCP_CONNECTIONS* etcp_connections_init(struct ETCP_CONN* etcp, const char* bind_addr, uint16_t port);
void etcp_connections_destroy(struct ETCP_CONNECTIONS* conns);
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* socket,
struct ETCP_CONNECTIONS* conns,
const struct sockaddr* remote_addr, socklen_t addr_len);
void etcp_link_close(struct ETCP_LINK* link);
int etcp_input(struct packet_buffer* pkt, struct ETCP_SOCKET* socket, struct ETCP_CONNECTIONS* conns);
int etcp_link_send(struct ETCP_CONN* etcp, struct ETCP_LINK* link, const uint8_t* data, size_t len);
// CONNECTION MANAGEMENT
struct ETCP_LINK* etcp_link_find_by_addr(struct ETCP_CONNECTIONS* conns, const struct sockaddr* addr, socklen_t addr_len);
int etcp_link_add_to_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link);
void etcp_link_remove_from_connections(struct ETCP_CONNECTIONS* conns, struct ETCP_LINK* link);
size_t encrypt_errors;
size_t decrypt_errors;
size_t send_errors;
size_t recv_errors;
size_t total_encrypted;
size_t total_decrypted;
};
// INITIALIZATION (создаёт listen-сокеты и подключения из конфига)
int init_connections(struct UTUN_INSTANCE* instance);
// Отправка кодограмм протокола
int etcp_link_send_init(struct ETCP_LINK* link, int mtu, uint16_t keepalive_interval);
int etcp_link_send_init_response(struct ETCP_LINK* link, int mtu, uint16_t keepalive_interval);
int etcp_link_send_channel_init(struct ETCP_LINK* link, uint16_t keepalive_interval);
int etcp_link_send_channel_response(struct ETCP_LINK* link);
int etcp_link_send_reset(struct ETCP_LINK* link);
// Utility functions
size_t etcp_connections_get_channel_count(const struct ETCP_CONNECTIONS* conns);
// Получение статистики шифрования
void etcp_connections_get_crypto_stats(const struct ETCP_CONNECTIONS* conns,
size_t* encrypt_errors,
size_t* decrypt_errors,
size_t* send_errors,
size_t* recv_errors,
size_t* total_encrypted,
size_t* total_decrypted);
// SOCKET FUNCTIONS
// добавляет новый версер (сокет для приёма и отправки кодограмм. обслуживает много подключений)
struct ETCP_SOCKET* etcp_socket_add(struct UTUN_INSTANCE* instance, struct sockaddr_storage* ip, uint32_t netif_index, int so_mark, uint8_t type);
// удаляет сокет и освобождает ресурсы (грохает все его подключения и сокет)
void etcp_socket_remove(struct ETCP_SOCKET* conn);
// connection functions
// создает новый канал связи для etcp подключения (ETCP_CONN)
struct ETCP_LINK* etcp_link_new(struct ETCP_CONN* etcp, struct ETCP_SOCKET* conn, struct sockaddr_storage* remote_addr, uint8_t is_server);
void etcp_link_close(struct ETCP_LINK* link);
//int etcp_input_cbk(struct packet_buffer* pkt, struct ETCP_SOCKET* conn);// получает расшифрованный пакет
int etcp_encrypt_send(struct ETCP_DGRAM* dgram);// зашифровывает и отправляет пакет
#endif // ETCP_CONNECTIONS_H

16
src/etcp_protocol.txt

@ -25,23 +25,15 @@
Кодограммы с этими секциями обрабатываются в etcp_connections (в этих кодограммах всегда только одна секция):
г) Init запрос - заголовок 0x02:
[0x02] [my_node_id 64bit] [my mtu high] [my mty low] [keepalive high] [keepalive low] [my publick key (32 байта, не шифруется)]
- Инициирует первый connection и + etcp instance
г) Init запрос - заголовок 0x02 (со сбросом etcp сессии) или 0x04 (бес сброса):
[0x02/0x04] [my_node_id 64bit] [my mtu high] [my mty low] [keepalive high] [keepalive low] [my publick key (32 байта, не шифруется)]
- Инициирует новый connection для tcp instance. если tcp instance нет (первое подключение) - создаёт. Между нодами только одно подключение, но можно добавлять каналы.
- Публичный ключ отправляется в конце пакета без шифрования, чтобы получатель мог установить его и расшифровать остальную часть пакета
д) Init подтверждение - заголовок 0x03:
[0x03] [my_node_id 64bit] [my mtu high] [my mty low] [keepalive high] [keepalive low]
[0x03] [my_node_id 64bit] [my mtu high] [my mty low]
- Подтверждение инициализации
г) Channel init запрос:
[0x04] [my_node_id 64bit] [keepalive high] [keepalive low]
- Инициирует новый connection в существующем etcp instance
д) Channel init подтверждение - заголовок 0x05:
[0x05] [my_node_id 64bit] [my publick key (32 байта, не шифруется)]
- Подтверждение иниуиализации дополнительного канала
е) Reset запрос:
[0x06]
- отправляется в ответ отправителю, если соединение не инициализировано и получен пакет не init (0x02, 0x03)

31
src/etcp_sockets.h

@ -1,31 +0,0 @@
#ifndef ETCP_SOCKET_H
#define ETCP_SOCKET_H
#include "packet_buffer.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdint.h>
// Forward declarations
struct ETCP_CONN;
// ETCP Socket - интегрированный UDP сокет с ETCP функциональностью
struct ETCP_SOCKET {
int fd; // Файловый дескриптор UDP сокета
struct sockaddr_storage local_addr; // Локальный адрес
socklen_t local_addr_len; // Длина локального адреса
struct ETCP_CONN* etcp; // Родительский ETCP instance
uint32_t socket_id; // Уникальный ID сокета
// Callback для получения данных
void (*recv_callback)(struct ETCP_SOCKET* sock, struct packet_buffer* pkt, void* user_data);
void* recv_user_data; // User data для callback
};
typedef void (*etcp_socket_recv_cb)(struct ETCP_SOCKET* sock,
struct packet_buffer* pkt,
void* user_data);
#endif // ETCP_SOCKET_H

62
src/memory_pool.c

@ -1,62 +0,0 @@
#include "memory_pool.h"
#include <stdlib.h>
#include <string.h>
// Инициализировать пул памяти
void memory_pool_init(struct memory_pool* pool, size_t object_size) {
pool->object_size = object_size;
pool->free_count = 0;
pool->allocations = 0;
pool->reuse_count = 0;
memset(pool->free_list, 0, sizeof(pool->free_list));
}
// Выделить объект из пула или из malloc
void* memory_pool_alloc(struct memory_pool* pool) {
pool->allocations++;
// Если есть свободные объекты в пуле, использовать их
if (pool->free_count > 0) {
pool->free_count--;
pool->reuse_count++;
void* obj = pool->free_list[pool->free_count];
pool->free_list[pool->free_count] = NULL;
return obj;
}
// Иначе выделить через malloc
return malloc(pool->object_size);
}
// Освободить объект в пул или в free
void memory_pool_free(struct memory_pool* pool, void* obj) {
if (!obj) return;
// Если пул не заполнен, сохранить объект для повторного использования
if (pool->free_count < MEMORY_POOL_SIZE) {
pool->free_list[pool->free_count] = obj;
pool->free_count++;
return;
}
// Иначе освободить через free
free(obj);
}
// Получить статистику пула
void memory_pool_get_stats(struct memory_pool* pool, size_t* allocations, size_t* reuse_count) {
if (allocations) *allocations = pool->allocations;
if (reuse_count) *reuse_count = pool->reuse_count;
}
// Очистить пул памяти
void memory_pool_destroy(struct memory_pool* pool) {
// Освободить все объекты в пуле
for (int i = 0; i < pool->free_count; i++) {
if (pool->free_list[i]) {
free(pool->free_list[i]);
pool->free_list[i] = NULL;
}
}
pool->free_count = 0;
}

36
src/memory_pool.h

@ -1,36 +0,0 @@
#ifndef MEMORY_POOL_H
#define MEMORY_POOL_H
#include <stddef.h> // для size_t
// Размер пула для объектов одинакового размера
#define MEMORY_POOL_SIZE 64
// Структура пула памяти для оптимизации аллокаций
struct memory_pool {
void* free_list[MEMORY_POOL_SIZE]; // Список свободных блоков
int free_count; // Количество свободных блоков
size_t object_size; // Размер объектов в пуле
size_t allocations; // Статистика: всего аллокаций
size_t reuse_count; // Статистика: повторное использование
};
// Тип пула памяти
typedef struct memory_pool memory_pool_t;
// Инициализировать пул памяти
void memory_pool_init(struct memory_pool* pool, size_t object_size);
// Выделить объект из пула или из malloc
void* memory_pool_alloc(struct memory_pool* pool);
// Освободить объект в пул или в free
void memory_pool_free(struct memory_pool* pool, void* obj);
// Получить статистику пула
void memory_pool_get_stats(struct memory_pool* pool, size_t* allocations, size_t* reuse_count);
// Очистить пул памяти
void memory_pool_destroy(struct memory_pool* pool);
#endif // MEMORY_POOL_H

76
src/packet_buffer.h

@ -1,76 +0,0 @@
#ifndef PACKET_BUFFER_H
#define PACKET_BUFFER_H
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
// Стадии обработки пакета
typedef enum {
PACKET_STAGE_RX_SOCKET, // Получен из сокета (зашифрован)
PACKET_STAGE_RX_DECRYPTED, // Расшифрован (plain-text)
PACKET_STAGE_RX_ETCP, // Обработан ETCP (собран фрагмент)
PACKET_STAGE_RX_APP, // Передан в приложение
PACKET_STAGE_TX_APP, // Из приложения на отправку
PACKET_STAGE_TX_ETCP, // Обработан ETCP (фрагментирован)
PACKET_STAGE_TX_ENCRYPTED // Зашифрован, готов к отправке
} packet_stage_t;
// Флаги пакета
#define PACKET_FLAG_ENCRYPTED (1 << 0)
#define PACKET_FLAG_FRAGMENTED (1 << 1)
#define PACKET_FLAG_RETRANSMIT (1 << 2)
#define PACKET_FLAG_CONTROL (1 << 3) // Служебный пакет (RST, INIT)
// Размеры
#define PACKET_METADATA_SIZE 64
#define PACKET_DATA_SIZE 1536
#define PACKET_TOTAL_SIZE 1600
#define PACKET_POOL_SIZE 64
// Основная структура пакета
struct packet_buffer {
// Метаданные (64 байта)
struct {
struct ETCP_SOCKET* s; // Сокет через который получен/ планируется отправить
struct etcp_channel* channel; // Канал ETCP (NULL до поиска)
struct sockaddr_storage remote_addr;// Адрес удаленного endpoint
uint64_t timestamp_us; // Время создания пакета
uint16_t data_len; // Длина данных в буфере
uint8_t stage; // Стадия обработки пакета
} metadata;
struct packet_buffer* next_free; // Для пула - используется только когда пакет свободен
uint8_t data[0]; // Данные (variable size)
};
// Инлайн функции для удобного доступа
// Доступ к данным
static inline uint8_t* packet_data(struct packet_buffer* pkt) {
return pkt->data;
}
static inline struct sockaddr* packet_remote_addr(struct packet_buffer* pkt) {
return (struct sockaddr*)&pkt->metadata.remote_addr;
}
static inline uint16_t packet_remote_port(const struct packet_buffer* pkt) {
if (pkt->metadata.remote_addr.ss_family == AF_INET) {
return ((struct sockaddr_in*)&pkt->metadata.remote_addr)->sin_port;
}
return 0;
}
// Инициализация
static inline void packet_init(struct packet_buffer* pkt) {
memset(&pkt->metadata, 0, sizeof(pkt->metadata));
pkt->metadata.timestamp_us = 0; // TODO: get current time
pkt->metadata.stage = PACKET_STAGE_RX_SOCKET;
}
#endif // PACKET_BUFFER_H

85
src/packet_pool.c

@ -1,85 +0,0 @@
#include "packet_pool.h"
#include "packet_buffer.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void packet_pool_init(struct packet_pool* pool) {
pool->free_count = 0;
pool->allocations = 0;
pool->reuse_count = 0;
pool->overflow_count = 0;
memset(pool->free_list, 0, sizeof(pool->free_list));
// Предварительно аллоцируем пакеты
for (int i = 0; i < PACKET_POOL_SIZE / 2; i++) {
struct packet_buffer* pkt = calloc(1, PACKET_TOTAL_SIZE);
if (pkt) {
pool->free_list[pool->free_count++] = pkt;
}
}
}
struct packet_buffer* packet_pool_get(struct packet_pool* pool) {
pool->allocations++;
// Если есть свободные пакеты в пуле, использовать их
if (pool->free_count > 0) {
pool->reuse_count++;
pool->free_count--;
struct packet_buffer* pkt = pool->free_list[pool->free_count];
pool->free_list[pool->free_count] = NULL;
// Инициализировать метаданные
packet_init(pkt);
pkt->next_free = NULL;
return pkt;
}
// Иначе выделить через malloc
pool->overflow_count++;
struct packet_buffer* pkt = calloc(1, PACKET_TOTAL_SIZE);
if (pkt) {
packet_init(pkt);
}
return pkt;
}
void packet_pool_put(struct packet_pool* pool, struct packet_buffer* pkt) {
if (!pkt) return;
// Если пул не заполнен, сохранить пакет для повторного использования
if (pool->free_count < PACKET_POOL_SIZE) {
pool->free_list[pool->free_count] = pkt;
pool->free_count++;
return;
}
// Иначе освободить через free (переполнение)
free(pkt);
}
void packet_pool_get_stats(const struct packet_pool* pool,
size_t* allocations,
size_t* reuse_count,
size_t* overflow_count) {
if (allocations) *allocations = pool->allocations;
if (reuse_count) *reuse_count = pool->reuse_count;
if (overflow_count) *overflow_count = pool->overflow_count;
}
void packet_pool_destroy(struct packet_pool* pool) {
// Освободить все пакеты в пуле
for (int i = 0; i < pool->free_count; i++) {
if (pool->free_list[i]) {
free(pool->free_list[i]);
pool->free_list[i] = NULL;
}
}
pool->free_count = 0;
pool->allocations = 0;
pool->reuse_count = 0;
pool->overflow_count = 0;
}

35
src/packet_pool.h

@ -1,35 +0,0 @@
#ifndef PACKET_POOL_H
#define PACKET_POOL_H
#include "packet_buffer.h"
// Размер пула пакетов
#define PACKET_POOL_SIZE 64
struct packet_pool {
struct packet_buffer* free_list[PACKET_POOL_SIZE]; // Список свободных пакетов
int free_count; // Количество свободных пакетов
size_t allocations; // Всего аллокаций
size_t reuse_count; // Повторное использование
size_t overflow_count; // Переполнения пула
};
// Инициализация пула
void packet_pool_init(struct packet_pool* pool);
// Получить пакет из пула
struct packet_buffer* packet_pool_get(struct packet_pool* pool);
// Вернуть пакет в пул
void packet_pool_put(struct packet_pool* pool, struct packet_buffer* pkt);
// Получить статистику
void packet_pool_get_stats(const struct packet_pool* pool,
size_t* allocations,
size_t* reuse_count,
size_t* overflow_count);
// Очистить пул и освободить всю память
void packet_pool_destroy(struct packet_pool* pool);
#endif // PACKET_POOL_H

45
src/secure_channel.c

@ -68,7 +68,7 @@ static int sc_validate_key(const uint8_t *public_key)
return uECC_valid_public_key(public_key, curve);
}
sc_status_t sc_generate_keypair(sc_context_t *ctx)
sc_status_t sc_generate_keypair(struct SC_MYKEYS *ctx)
{
if (!ctx) {
return SC_ERR_INVALID_ARG;
@ -81,23 +81,25 @@ sc_status_t sc_generate_keypair(sc_context_t *ctx)
/* Set custom RNG function */
uECC_set_rng(sc_rng);
if (!uECC_make_key(ctx->public_key, ctx->private_key, curve)) {
if (!uECC_make_key(ctx->pk->public_key, ctx->pk->private_key, curve)) {
return SC_ERR_CRYPTO;
}
return SC_OK;
}
ctx->initialized = 1;
ctx->peer_key_set = 0;
ctx->session_ready = 0;
ctx->tx_counter = 0;
ctx->rx_counter = 0;
// Конвертация hex строки в бинарный формат
static int hex_to_binary(const char *hex_str, uint8_t *binary, size_t binary_len) {
if (!hex_str || !binary || strlen(hex_str) != binary_len * 2) return -1;
return SC_OK;
for (size_t i = 0; i < binary_len; i++) {
unsigned int byte;
if (sscanf(hex_str + i * 2, "%2x", &byte) != 1) return -1;
binary[i] = (uint8_t)byte;
}
return 0;
}
sc_status_t sc_init_local_keys(sc_context_t *ctx,
const uint8_t *public_key,
const uint8_t *private_key)
{
sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_key, const char *private_key) {
if (!ctx || !public_key || !private_key) {
return SC_ERR_INVALID_ARG;
}
@ -111,9 +113,14 @@ sc_status_t sc_init_local_keys(sc_context_t *ctx,
return SC_ERR_INVALID_ARG;
}
memcpy(ctx->public_key, public_key, SC_PUBKEY_SIZE);
memcpy(ctx->private_key, private_key, SC_PRIVKEY_SIZE);
if (hex_to_binary(public_key, mykeys->public_key, SC_PUBKEY_SIZE)) return SC_ERR_INVALID_ARG;
if (hex_to_binary(private_key, mykeys->private_key, SC_PRIVKEY_SIZE)) return SC_ERR_INVALID_ARG;
return SC_OK;
}
sc_status_t sc_init_ctx(sc_context_t *ctx, struct SC_MYKEYS *mykeys) {
ctx->pk=mykeys;
ctx->initialized = 1;
ctx->peer_key_set = 0;
ctx->session_ready = 0;
@ -123,10 +130,14 @@ sc_status_t sc_init_local_keys(sc_context_t *ctx,
return SC_OK;
}
sc_status_t sc_set_peer_public_key(sc_context_t *ctx,
const uint8_t *peer_public_key)
{
sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key_h, int mode) {
uint8_t shared_secret[SC_SHARED_SECRET_SIZE];
uint8_t peer_public_key[SC_PUBKEY_SIZE];
if (mode) {
if (hex_to_binary(peer_public_key_h, peer_public_key, SC_PUBKEY_SIZE)) return SC_ERR_INVALID_ARG;
}
else memcpy(peer_public_key, peer_public_key_h, SC_PUBKEY_SIZE);
if (!ctx || !peer_public_key) {
return SC_ERR_INVALID_ARG;

28
src/secure_channel.h

@ -27,12 +27,15 @@
typedef int sc_status_t;
typedef struct secure_channel sc_context_t;
// Контекст защищенного канала
struct secure_channel {
struct SC_MYKEYS {
/* Локальные ключи */
uint8_t private_key[SC_PRIVKEY_SIZE];
uint8_t public_key[SC_PUBKEY_SIZE];
};
// Контекст защищенного канала
struct secure_channel {
struct SC_MYKEYS* pk;
/* Ключи пира (после key exchange) */
uint8_t peer_public_key[SC_PUBKEY_SIZE];
uint8_t session_key[SC_SESSION_KEY_SIZE]; /* Derived session key */
@ -49,22 +52,13 @@ struct secure_channel {
};
// Функции инициализации
sc_status_t sc_generate_keypair(sc_context_t *ctx);
sc_status_t sc_init_local_keys(sc_context_t *ctx,
const uint8_t *public_key,
const uint8_t *private_key);
sc_status_t sc_set_peer_public_key(sc_context_t *ctx,
const uint8_t *peer_public_key);
sc_status_t sc_init_ctx(sc_context_t *ctx, struct SC_MYKEYS *mykeys);
sc_status_t sc_generate_keypair(struct SC_MYKEYS *keys);
sc_status_t sc_init_local_keys(struct SC_MYKEYS *mykeys, const char *public_key, const char *private_key);
sc_status_t sc_set_peer_public_key(sc_context_t *ctx, const char *peer_public_key, int mode);// mode: 0-bin 1-hex key format
// Криптографические операции
sc_status_t sc_encrypt(sc_context_t *ctx,
const uint8_t *plaintext, size_t plaintext_len,
uint8_t *ciphertext, size_t *ciphertext_len);
sc_status_t sc_decrypt(sc_context_t *ctx,
const uint8_t *ciphertext, size_t ciphertext_len,
uint8_t *plaintext, size_t *plaintext_len);
sc_status_t sc_derive_shared_key(sc_context_t *ctx);
sc_status_t sc_encrypt(sc_context_t *ctx, const uint8_t *plaintext, size_t plaintext_len, uint8_t *ciphertext, size_t *ciphertext_len);
sc_status_t sc_decrypt(sc_context_t *ctx, const uint8_t *ciphertext, size_t ciphertext_len, uint8_t *plaintext, size_t *plaintext_len);
#endif // SECURE_CHANNEL_H

79
src/tun_if.c

@ -249,3 +249,82 @@ static uint32_t tun_get_dest_ip(const uint8_t *packet, size_t len) {
memcpy(&dest_ip, packet + 16, 4);
return dest_ip;
}
// Extract destination IPv4 address from packet
static uint32_t get_dest_ip(const uint8_t *packet, size_t len) {
if (len < 20) return 0; // Minimum IPv4 header size
// Check IP version (first nibble)
uint8_t version = (packet[0] >> 4) & 0x0F;
if (version != 4) return 0;
// Destination IP is at offset 16
uint32_t dest_ip;
memcpy(&dest_ip, packet + 16, 4);
return dest_ip;
}
/*
// Callback for TUN device read events
static void tun_read_callback(int fd, void* user_arg) {
struct UTUN_INSTANCE *instance = (struct UTUN_INSTANCE*)user_arg;
uint8_t buffer[MAX_PACKET_SIZE];
// Read from TUN device
ssize_t nread = tun_read(fd, buffer, sizeof(buffer));
if (nread < 0) {
if (errno == EINTR) return;
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to read from TUN: %s", strerror(errno));
return;
}
if (nread > 0) {
// Route packet based on destination IP
uint32_t dest_ip = get_dest_ip(buffer, nread);
struct route_entry route;
if (routing_table_lookup(instance->routing_table, dest_ip, &route)) {
// Found route, send to next hop connection
if (route.next_hop) {
if (conn_send(route.next_hop, buffer, nread) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to send packet via route");
}
} else {
// Local route - no forwarding needed
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "Local packet, no forwarding");
}
} else {
// No route found, drop packet
char ip_str[16];
ip_to_string(dest_ip, ip_str);
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "No route for destination IP %s", ip_str);
}
}
}
// Register sockets with uasync
int utun_instance_register_sockets(struct UTUN_INSTANCE *instance) {
if (!instance || !instance->ua || instance->tun.fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Invalid instance or TUN fd");
return -1;
}
// Register TUN file descriptor
if (uasync_add_socket(instance->ua, instance->tun.fd,
tun_read_callback, NULL, NULL, instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to register TUN socket");
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Registered TUN socket (fd=%d)", instance->tun.fd);
return 0;
}
// Unregister sockets
void utun_instance_unregister_sockets(struct UTUN_INSTANCE *instance) {
if (!instance || !instance->ua) return;
if (instance->tun.fd >= 0) {
uasync_remove_socket(instance->ua, instance->tun.fd);
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Unregistered TUN socket");
}
}
*/

2
src/tun_if.h

@ -10,6 +10,8 @@
extern "C" {
#endif
#define MAX_PACKET_SIZE 1500
// TUN interface configuration
struct tun_config {
char ifname[16]; // Interface name (e.g., "tun12")

28
src/utun.c

@ -36,7 +36,6 @@ utun instances: можно stop() - он ывзывает закрытие вс
// Global wakeup pipe write fd for signal handler
static int g_wakeup_pipe_write_fd = -1;
#define MAX_PACKET_SIZE 2048
#define DEFAULT_CONFIG "utun.conf"
#define DEFAULT_PIDFILE "/var/run/utun.pid"
@ -231,13 +230,13 @@ static FILE* open_logfile(const char *logfile) {
// Main function
static volatile sig_atomic_t g_running = 1;
static struct UTUN_INSTANCE *g_instance = NULL;
struct UASYNC* main_ua = NULL;
static void signal_handler(int sig) {
(void)sig;
g_running = 0;
if (g_instance && g_instance->ua) {
uasync_wakeup(g_instance->ua);
if (main_ua) {
uasync_wakeup(main_ua);
}
}
@ -250,8 +249,16 @@ int main(int argc, char *argv[]) {
return 0;
}
// Create uasync instance
struct UASYNC* ua = uasync_create();
main_ua=ua;
if (!ua) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to create uasync instance");
return 1;
}
// Create and initialize instance
struct UTUN_INSTANCE *instance = utun_instance_create(args.config_file, args.log_file);
struct UTUN_INSTANCE *instance = utun_instance_create(ua, args.config_file, args.log_file);
if (!instance) {
fprintf(stderr, "Failed to create UTUN instance\n");
return 1;
@ -280,7 +287,6 @@ int main(int argc, char *argv[]) {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGHUP, signal_handler);
g_instance = instance;
// Daemonize if not in foreground
if (!args.foreground) {
@ -297,15 +303,11 @@ int main(int argc, char *argv[]) {
return 1;
}
// Main event loop
FILE *log_fp = open_logfile(args.log_file);
fprintf(log_fp ? log_fp : stderr, "Starting main loop...\n");
while (g_running) { utun_instance_run(instance); if (instance->running == 0) break;
}
while (instance->running) uasync_poll(ua, 100);
// Cleanup
fprintf(log_fp ? log_fp : stderr, "Shutting down...\n");
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Shutdown");
utun_instance_unregister_sockets(instance);
utun_instance_destroy(instance);
remove_pidfile(args.pid_file);

52
src/utun_fixes.c

@ -1,52 +0,0 @@
#include "utun_instance.h"
#include "routing.h"
#include "etcp_connections.h"
#include "config_parser.h"
#include "../lib/u_async.h"
conn_handle_t* route_get_next_hop(routing_table_t* rt, uint32_t dest_ip) {
route_entry_t route = {0};
if (!rt || !routing_table_lookup(rt, dest_ip, &route)) return NULL;
return (conn_handle_t*)route.next_hop;
}
static int add_default_route(struct UTUN_INSTANCE* *instance) {
if (!instance || !instance->routing_table || instance->connection_count == 0) return -1;
route_entry_t default_route = {0};
default_route.network = 0; // 0.0.0.0
default_route.prefix_length = 0;
default_route.next_hop_ip = 0;
default_route.next_hop = (struct ETCP_CONNECTIONS*)instance->connections[0]->conns;
default_route.type = ROUTE_TYPE_STATIC;
default_route.flags = ROUTE_FLAG_ACTIVE;
default_route.created_time = 0;
return routing_table_insert((routing_table_t*)instance->routing_table, &default_route) ? 0 : -1;
}
void conn_destroy(conn_handle_t* conn);
int conn_send(conn_handle_t* conn, const uint8_t* data, size_t len);
uint32_t get_dest_ip(const uint8_t *packet, size_t len);
int parse_subnet(const char* subnet_str, uint32_t* network, uint8_t* prefix_len);
conn_handle_t* route_find_connection(struct UTUN_INSTANCE** instance, uint32_t dest_ip) {
if (!instance || !instance->routing_table) return NULL;
route_entry_t route = {0};
if (!routing_table_lookup((routing_table_t*)instance->routing_table, dest_ip, &route)) {
return NULL;
}
struct ETCP_CONNECTIONS* conns = route.next_hop;
if (!conns) return NULL;
// Find matching connection handle
for (int i = 0; i < instance->connection_count; i++) {
if (instance->connections[i] && instance->connections[i]->conns == conns) {
return instance->connections[i];
}
}
return NULL;
}

208
src/utun_instance.c

@ -14,24 +14,22 @@
#include <unistd.h>
#include <arpa/inet.h>
#define MAX_PACKET_SIZE 2048
// Forward declarations
static void tun_read_callback(int fd, void* user_arg);
static uint32_t get_dest_ip(const uint8_t *packet, size_t len);
static void utun_instance_set(struct UTUN_INSTANCE *instance);
// Global instance for signal handlers
static struct UTUN_INSTANCE *g_instance = NULL;
// Create and initialize root instance
struct UTUN_INSTANCE* utun_instance_create(const char *config_file, const char *log_file) {
struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char *config_file, const char *log_file) {
struct UTUN_INSTANCE *instance = calloc(1, sizeof(struct UTUN_INSTANCE));
if (!instance) return NULL;
// Initialize basic fields
instance->running = 0;
instance->log_fp = NULL;
instance->ua = ua;
// Ensure keys and node_id exist in config
if (config_ensure_keys_and_node_id(config_file) < 0) {
@ -55,81 +53,14 @@ struct UTUN_INSTANCE* utun_instance_create(const char *config_file, const char *
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to open log file %s: %s", log_file, strerror(errno));
}
}
// Create uasync instance
instance->ua = uasync_create();
if (!instance->ua) {
DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "Failed to create uasync instance");
utun_instance_destroy(instance);
return NULL;
}
// uasync has built-in wakeup mechanism, no need for manual pipe
// Set node_id from config
instance->node_id = instance->config->global.my_node_id;
// Set global instance
utun_instance_set(instance);
return instance;
}
// Destroy instance and cleanup resources
void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
if (!instance) return;
// Stop running
instance->running = 0;
// Cleanup components
if (instance->ua) {
uasync_destroy(instance->ua);
}
if (instance->routing_table) {
routing_table_destroy(instance->routing_table);
}
// Cleanup connections
if (instance->connections) {
for (int i = 0; i < instance->connection_count; i++) {
if (instance->connections[i]) {
conn_destroy(instance->connections[i]);
}
}
free(instance->connections);
}
// Cleanup TUN
if (instance->tun.fd >= 0) {
tun_close(&instance->tun);
}
// Cleanup config
if (instance->config) {
free_config(instance->config);
}
// Close log file
if (instance->log_fp) {
fclose(instance->log_fp);
}
// uasync cleanup will handle its built-in wakeup pipe
// Clear global instance
if (g_instance == instance) {
g_instance = NULL;
}
free(instance);
}
// Initialize all components
int utun_instance_init(struct UTUN_INSTANCE *instance) {
if (!instance || !instance->config) return -1;
// Set my keys
sc_init_local_keys(&instance->my_keys, instance->config->global.my_public_key_hex, instance->config->global.my_private_key_hex);
instance->pkt_pool=memory_pool_init(PACKET_DATA_SIZE+100);
/*
// Initialize TUN device
if (tun_create(&instance->tun) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to create TUN device");
@ -163,117 +94,72 @@ int utun_instance_init(struct UTUN_INSTANCE *instance) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "Failed to create routing table");
return -1;
}
*/
// Initialize connections from configuration
if (init_connections(instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to initialize connections");
// Cleanup will be handled by utun_instance_destroy
return -1;
return NULL;
}
return 0;
}
// Start main event loop (single iteration)
void utun_instance_run(struct UTUN_INSTANCE *instance) {
if (!instance || !instance->ua) return;
// Single iteration - returns after processing events or timeout
uasync_poll(instance->ua, 1000); // 100ms timeout
return instance;
}
// Stop instance
void utun_instance_stop(struct UTUN_INSTANCE *instance) {
// Destroy instance and cleanup resources
void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
if (!instance) return;
// Stop running
instance->running = 0;
// Wakeup main loop using built-in uasync wakeup
// Cleanup components
if (instance->ua) {
uasync_wakeup(instance->ua);
uasync_destroy(instance->ua);
}
}
// Global instance access
struct UTUN_INSTANCE* utun_instance_get(void) {
return g_instance;
}
void utun_instance_set(struct UTUN_INSTANCE *instance) {
g_instance = instance;
}
// Extract destination IPv4 address from packet
static uint32_t get_dest_ip(const uint8_t *packet, size_t len) {
if (len < 20) return 0; // Minimum IPv4 header size
// Check IP version (first nibble)
uint8_t version = (packet[0] >> 4) & 0x0F;
if (version != 4) return 0;
// Destination IP is at offset 16
uint32_t dest_ip;
memcpy(&dest_ip, packet + 16, 4);
return dest_ip;
}
// Callback for TUN device read events
static void tun_read_callback(int fd, void* user_arg) {
struct UTUN_INSTANCE *instance = (struct UTUN_INSTANCE*)user_arg;
uint8_t buffer[MAX_PACKET_SIZE];
// Read from TUN device
ssize_t nread = tun_read(fd, buffer, sizeof(buffer));
if (nread < 0) {
if (errno == EINTR) return;
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to read from TUN: %s", strerror(errno));
return;
if (instance->routing_table) {
routing_table_destroy(instance->routing_table);
}
if (nread > 0) {
// Route packet based on destination IP
uint32_t dest_ip = get_dest_ip(buffer, nread);
struct route_entry route;
if (routing_table_lookup(instance->routing_table, dest_ip, &route)) {
// Found route, send to next hop connection
if (route.next_hop) {
if (conn_send(route.next_hop, buffer, nread) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "Failed to send packet via route");
}
} else {
// Local route - no forwarding needed
DEBUG_DEBUG(DEBUG_CATEGORY_ROUTING, "Local packet, no forwarding");
// Cleanup connections
if (instance->connections) {
// etcp_socket_remove(...)
}
} else {
// No route found, drop packet
char ip_str[16];
ip_to_string(dest_ip, ip_str);
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "No route for destination IP %s", ip_str);
// Cleanup TUN
if (instance->tun.fd >= 0) {
tun_close(&instance->tun);
}
// Cleanup config
if (instance->config) {
free_config(instance->config);
}
}
// Register sockets with uasync
int utun_instance_register_sockets(struct UTUN_INSTANCE *instance) {
if (!instance || !instance->ua || instance->tun.fd < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Invalid instance or TUN fd");
return -1;
// Close log file
if (instance->log_fp) {
fclose(instance->log_fp);
}
// Register TUN file descriptor
if (uasync_add_socket(instance->ua, instance->tun.fd,
tun_read_callback, NULL, NULL, instance) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_TUN, "Failed to register TUN socket");
return -1;
// uasync cleanup will handle its built-in wakeup pipe
// Clear global instance
if (g_instance == instance) {
g_instance = NULL;
}
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Registered TUN socket (fd=%d)", instance->tun.fd);
return 0;
free(instance);
}
// Unregister sockets
void utun_instance_unregister_sockets(struct UTUN_INSTANCE *instance) {
if (!instance || !instance->ua) return;
if (instance->tun.fd >= 0) {
uasync_remove_socket(instance->ua, instance->tun.fd);
DEBUG_INFO(DEBUG_CATEGORY_TUN, "Unregistered TUN socket");
// Stop instance
void utun_instance_stop(struct UTUN_INSTANCE *instance) {
if (!instance) return;
instance->running = 0;
// Wakeup main loop using built-in uasync wakeup
if (instance->ua) {
memory_pool_destroy(instance->pkt_pool);
uasync_wakeup(instance->ua);
}
}

22
src/utun_instance.h

@ -4,14 +4,15 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "../lib/memory_pool.h"
#include "secure_channel.h"
#include "tun_if.h"
// Forward declarations
struct utun_config;
struct uasync_s;
struct routing_table;
struct ETCP_CONNECTIONS;
typedef struct ETCP_CONNECTIONS conn_handle_t;
struct ETCP_CONN;
struct ETCP_SOCKET;
// uTun instance configuration
@ -21,34 +22,33 @@ struct UTUN_INSTANCE {
// TUN interface
struct tun_config tun;
char tun_name[64];
char tun_ip[64];
char tun_ip_mask[64];
void *tun_socket_id; // Socket ID from uasync_add_socket
// Identification
uint64_t node_id;
struct SC_MYKEYS my_keys;
// Main async context
struct uasync_s *ua;
struct UASYNC* ua;
// State
int running;
FILE *log_fp;
struct memory_pool* pkt_pool;
// Routing
struct routing_table *routing_table;
// Connections
conn_handle_t **connections;
int connection_count;
struct ETCP_CONN* connections;// linked-list
// First listen socket
struct ETCP_SOCKET* first_listen_socket;
// Active sockets
struct ETCP_SOCKET* etcp_sockets;// linked-list
};
// Functions
struct UTUN_INSTANCE* utun_instance_create(const char* config_file, const char* log_file);
struct UTUN_INSTANCE* utun_instance_create(struct UASYNC* ua, const char* config_file, const char* log_file);
void utun_instance_destroy(struct UTUN_INSTANCE* instance);
int utun_instance_init(struct UTUN_INSTANCE *instance);
void utun_instance_run(struct UTUN_INSTANCE *instance);

19
test_invalid_keys.conf

@ -0,0 +1,19 @@
[global]
my_private_key=8d893d54930fc1b428e17c3ef01f0eb7578fba00bc43f3b5260b30aace064c81
my_public_key=193ba8d4829818e2571789432ec065963ee364b1c524ba04902130e9a0961ab7
my_node_id=3e62f4ffc02efa41
tun_ip=10.0.0.1/24
mtu=1500
control_ip=127.0.0.1
control_port=12345
net_debug=0
[routing]
allowed_subnet=10.0.0.0/24
[server:test]
addr=192.168.0.10:1234
netif=et

16
test_no_keys.conf

@ -0,0 +1,16 @@
[global]
tun_ip=10.0.0.1/24
mtu=1500
control_ip=127.0.0.1
control_port=12345
net_debug=0
my_node_id=338e13218f721d15
my_private_key=ccb9f2ae2f348f7f8fc8618c58395c242107a802a1de5f674e81ecd03bfc7a0d
my_public_key=d12a4aa8d289b15264c47c867812620abab80890164b22e0f69918d696d91ccd
[routing]
allowed_subnet=10.0.0.0/24
[server:test]
addr=192.168.0.10:1234
netif=eth0

BIN
tests/test_etcp_crypto

Binary file not shown.

213
tests/test_etcp_crypto.c

@ -0,0 +1,213 @@
// test_etcp_crypto.c - Test ETCP encryption/decryption with secure channel
#include "etcp.h"
#include "config_parser.h"
#include "secure_channel.h"
#include "etcp_connections.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
// Test configuration
#define TEST_PORT 32345
#define CLIENT_PORT 32346
#define TEST_DATA "Hello, encrypted world!"
#define TEST_DATA_LEN 23
// Helper: create UDP socket bound to port
static int create_udp_socket(int port) {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) return -1;
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
close(fd);
return -1;
}
return fd;
}
// Helper: generate test keys
static void generate_test_keys(uint8_t* server_priv, uint8_t* server_pub,
uint8_t* client_priv, uint8_t* client_pub) {
// Use fixed test keys for reproducibility
const char* server_priv_hex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
const char* server_pub_hex = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789";
const char* client_priv_hex = "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210";
const char* client_pub_hex = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
for (int i = 0; i < 32; i++) {
sscanf(server_priv_hex + i*2, "%2hhx", &server_priv[i]);
sscanf(server_pub_hex + i*2, "%2hhx", &server_pub[i]);
sscanf(client_priv_hex + i*2, "%2hhx", &client_priv[i]);
sscanf(client_pub_hex + i*2, "%2hhx", &client_pub[i]);
}
}
// Helper: hex to binary
static int hex_to_bin(const char* hex, uint8_t* bin, size_t len) {
if (strlen(hex) != len * 2) return -1;
for (size_t i = 0; i < len; i++) {
if (sscanf(hex + i*2, "%2hhx", &bin[i]) != 1) return -1;
}
return 0;
}
int main(void) {
printf("=== ETCP Crypto Test ===\n");
// Generate test keys
uint8_t server_priv[32], server_pub[32];
uint8_t client_priv[32], client_pub[32];
generate_test_keys(server_priv, server_pub, client_priv, client_pub);
// Create UDP sockets
int server_fd = create_udp_socket(TEST_PORT);
int client_fd = create_udp_socket(CLIENT_PORT);
if (server_fd < 0 || client_fd < 0) {
printf("ERROR: Failed to create sockets\n");
return 1;
}
// Create ETCP instances
struct ETCP_CONN* server_etcp = calloc(1, sizeof(struct ETCP_CONN));
struct ETCP_CONN* client_etcp = calloc(1, sizeof(struct ETCP_CONN));
if (!server_etcp || !client_etcp) {
printf("ERROR: Failed to allocate ETCP instances\n");
return 1;
}
// Initialize secure channels
server_etcp->crypto_ctx = calloc(1, sizeof(sc_context_t));
client_etcp->crypto_ctx = calloc(1, sizeof(sc_context_t));
if (!server_etcp->crypto_ctx || !client_etcp->crypto_ctx) {
printf("ERROR: Failed to allocate crypto contexts\n");
return 1;
}
// Set keys
memcpy(server_etcp->crypto_ctx->private_key, server_priv, 32);
memcpy(server_etcp->crypto_ctx->public_key, server_pub, 32);
server_etcp->crypto_ctx->initialized = 1;
memcpy(client_etcp->crypto_ctx->private_key, client_priv, 32);
memcpy(client_etcp->crypto_ctx->public_key, client_pub, 32);
client_etcp->crypto_ctx->initialized = 1;
// Client sets server's public key as peer
memcpy(client_etcp->peer_public_key, server_pub, 32);
client_etcp->has_peer_key = 1;
sc_set_peer_public_key(client_etcp->crypto_ctx, server_pub);
// Server will get client's public key from INIT packet
// Create connections managers
struct ETCP_CONNECTIONS* server_conns = etcp_connections_init(server_etcp, "127.0.0.1", 0);
struct ETCP_CONNECTIONS* client_conns = etcp_connections_init(client_etcp, "127.0.0.1", 0);
if (!server_conns || !client_conns) {
printf("ERROR: Failed to create connections\n");
return 1;
}
// Create client link (client initiates connection)
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(TEST_PORT);
struct ETCP_LINK* client_link = etcp_link_new(client_etcp, &client_conns->socket, client_conns,
(struct sockaddr*)&server_addr, sizeof(server_addr));
if (!client_link) {
printf("ERROR: Failed to create client link\n");
return 1;
}
// Send INIT from client to server
printf("Sending INIT from client...\n");
if (etcp_link_send_init(client_link, 1500, 30) < 0) {
printf("ERROR: Failed to send INIT\n");
return 1;
}
// Server should receive INIT and create link
usleep(100000); // 100ms
struct sockaddr_in from_addr;
socklen_t from_len = sizeof(from_addr);
uint8_t buffer[2048];
ssize_t received = recvfrom(server_fd, buffer, sizeof(buffer), MSG_DONTWAIT,
(struct sockaddr*)&from_addr, &from_len);
if (received > 0) {
printf("Server received %zd bytes\n", received);
// Process packet on server
struct packet_buffer pkt;
memcpy(pkt.data, buffer, received);
pkt.metadata.data_len = received;
pkt.metadata.remote_addr = *(struct sockaddr_storage*)&from_addr;
pkt.metadata.s = &server_conns->socket;
if (etcp_input(&pkt, &server_conns->socket, server_conns) == 0) {
printf("Server processed INIT successfully\n");
// Check that server got client's public key
if (server_etcp->has_peer_key) {
printf("Server received client's public key\n");
}
}
}
// Test encrypted data transfer
printf("\nTesting encrypted data transfer...\n");
const char* test_data = TEST_DATA;
if (etcp_link_send(client_etcp, client_link, (const uint8_t*)test_data, TEST_DATA_LEN) == 0) {
printf("Client sent encrypted data\n");
}
usleep(100000);
received = recvfrom(server_fd, buffer, sizeof(buffer), MSG_DONTWAIT,
(struct sockaddr*)&from_addr, &from_len);
if (received > 0) {
printf("Server received %zd encrypted bytes\n", received);
// In real scenario, etcp_input would decrypt and process
}
// Print statistics
printf("\n=== Statistics ===\n");
size_t enc_err, dec_err, send_err, total_enc, total_dec;
etcp_connections_get_crypto_stats(server_conns, &enc_err, &dec_err, &send_err, NULL, &total_enc, &total_dec);
printf("Server: encrypted=%zu, decrypted=%zu, errors=%zu/%zu/%zu\n",
total_enc, total_dec, enc_err, dec_err, send_err);
etcp_connections_get_crypto_stats(client_conns, &enc_err, &dec_err, &send_err, NULL, &total_enc, &total_dec);
printf("Client: encrypted=%zu, decrypted=%zu, errors=%zu/%zu/%zu\n",
total_enc, total_dec, enc_err, dec_err, send_err);
// Cleanup
close(server_fd);
close(client_fd);
free(server_etcp->crypto_ctx);
free(client_etcp->crypto_ctx);
free(server_etcp);
free(client_etcp);
printf("\n=== Test completed ===\n");
return 0;
}

BIN
tests/test_etcp_crypto_fix

Binary file not shown.

219
tests/test_etcp_crypto_fix.c

@ -0,0 +1,219 @@
// test_etcp_crypto.c - Test ETCP encryption/decryption with secure channel
#include "etcp.h"
#include "config_parser.h"
#include "secure_channel.h"
#include "etcp_connections.h"
#include "packet_buffer.h"
#include "packet_pool.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
// Test configuration
#define TEST_PORT 42345
#define CLIENT_PORT 42346
#define TEST_DATA "Hello, encrypted world!"
#define TEST_DATA_LEN 23
// Helper: create UDP socket bound to port
static int create_udp_socket(int port) {
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) return -1;
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
close(fd);
return -1;
}
return fd;
}
// Helper: generate test keys
static void generate_test_keys(uint8_t* server_priv, uint8_t* server_pub,
uint8_t* client_priv, uint8_t* client_pub) {
// Use fixed test keys for reproducibility
const char* server_priv_hex = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
const char* server_pub_hex = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789";
const char* client_priv_hex = "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210";
const char* client_pub_hex = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
for (int i = 0; i < 32; i++) {
sscanf(server_priv_hex + i*2, "%2hhx", &server_priv[i]);
sscanf(server_pub_hex + i*2, "%2hhx", &server_pub[i]);
sscanf(client_priv_hex + i*2, "%2hhx", &client_priv[i]);
sscanf(client_pub_hex + i*2, "%2hhx", &client_pub[i]);
}
}
// Helper: hex to binary
static int hex_to_bin(const char* hex, uint8_t* bin, size_t len) {
if (strlen(hex) != len * 2) return -1;
for (size_t i = 0; i < len; i++) {
if (sscanf(hex + i*2, "%2hhx", &bin[i]) != 1) return -1;
}
return 0;
}
int main(void) {
printf("=== ETCP Crypto Test ===\n");
// Generate test keys
uint8_t server_priv[32], server_pub[32];
uint8_t client_priv[32], client_pub[32];
generate_test_keys(server_priv, server_pub, client_priv, client_pub);
// Create UDP sockets
int server_fd = create_udp_socket(TEST_PORT);
int client_fd = create_udp_socket(CLIENT_PORT);
if (server_fd < 0 || client_fd < 0) {
printf("ERROR: Failed to create sockets\n");
return 1;
}
// Create ETCP instances
struct ETCP_CONN* server_etcp = calloc(1, sizeof(struct ETCP_CONN));
struct ETCP_CONN* client_etcp = calloc(1, sizeof(struct ETCP_CONN));
if (!server_etcp || !client_etcp) {
printf("ERROR: Failed to allocate ETCP instances\n");
return 1;
}
// Initialize secure channels
server_etcp->crypto_ctx = calloc(1, sizeof(sc_context_t));
client_etcp->crypto_ctx = calloc(1, sizeof(sc_context_t));
if (!server_etcp->crypto_ctx || !client_etcp->crypto_ctx) {
printf("ERROR: Failed to allocate crypto contexts\n");
return 1;
}
// Set keys
memcpy(server_etcp->crypto_ctx->private_key, server_priv, 32);
memcpy(server_etcp->crypto_ctx->public_key, server_pub, 32);
server_etcp->crypto_ctx->initialized = 1;
memcpy(client_etcp->crypto_ctx->private_key, client_priv, 32);
memcpy(client_etcp->crypto_ctx->public_key, client_pub, 32);
client_etcp->crypto_ctx->initialized = 1;
// Client sets server's public key as peer
memcpy(client_etcp->peer_public_key, server_pub, 32);
client_etcp->has_peer_key = 1;
sc_set_peer_public_key(client_etcp->crypto_ctx, server_pub);
// Server will get client's public key from INIT packet
// Create connections managers
struct ETCP_CONNECTIONS* server_conns = etcp_connections_init(server_etcp, "127.0.0.1", 0);
struct ETCP_CONNECTIONS* client_conns = etcp_connections_init(client_etcp, "127.0.0.1", 0);
if (!server_conns || !client_conns) {
printf("ERROR: Failed to create connections\n");
return 1;
}
// Create client link (client initiates connection)
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(TEST_PORT);
struct ETCP_LINK* client_link = etcp_link_new(client_etcp, &client_conns->socket, client_conns,
(struct sockaddr*)&server_addr, sizeof(server_addr));
if (!client_link) {
printf("ERROR: Failed to create client link\n");
return 1;
}
// Send INIT from client to server
printf("Sending INIT from client...\n");
if (etcp_link_send_init(client_link, 1500, 30) < 0) {
printf("ERROR: Failed to send INIT\n");
return 1;
}
// Server should receive INIT and create link
usleep(100000); // 100ms
struct sockaddr_in from_addr;
socklen_t from_len = sizeof(from_addr);
uint8_t buffer[2048];
ssize_t received = recvfrom(server_fd, buffer, sizeof(buffer), MSG_DONTWAIT,
(struct sockaddr*)&from_addr, &from_len);
if (received > 0) {
printf("Server received %zd bytes\n", received);
// Process packet on server - use packet pool
struct packet_buffer* pkt = packet_pool_get(&server_etcp->packet_pool);
if (pkt) {
memcpy(packet_data(pkt), buffer, received);
pkt->metadata.data_len = received;
pkt->metadata.remote_addr = *(struct sockaddr_storage*)&from_addr;
pkt->metadata.s = &server_conns->socket;
if (etcp_input(pkt, &server_conns->socket, server_conns) == 0) {
printf("Server processed INIT successfully\n");
// Check that server got client's public key
if (server_etcp->has_peer_key) {
printf("Server received client's public key\n");
}
}
packet_pool_put(&server_etcp->packet_pool, pkt);
}
}
// Test encrypted data transfer
printf("\nTesting encrypted data transfer...\n");
const char* test_data = TEST_DATA;
if (etcp_link_send(client_etcp, client_link, (const uint8_t*)test_data, TEST_DATA_LEN) == 0) {
printf("Client sent encrypted data\n");
}
usleep(100000);
received = recvfrom(server_fd, buffer, sizeof(buffer), MSG_DONTWAIT,
(struct sockaddr*)&from_addr, &from_len);
if (received > 0) {
printf("Server received %zd encrypted bytes\n", received);
// In real scenario, etcp_input would decrypt and process
}
// Print statistics
printf("\n=== Statistics ===\n");
size_t enc_err, dec_err, send_err, total_enc, total_dec;
etcp_connections_get_crypto_stats(server_conns, &enc_err, &dec_err, &send_err, NULL, &total_enc, &total_dec);
printf("Server: encrypted=%zu, decrypted=%zu, errors=%zu/%zu/%zu\n",
total_enc, total_dec, enc_err, dec_err, send_err);
etcp_connections_get_crypto_stats(client_conns, &enc_err, &dec_err, &send_err, NULL, &total_enc, &total_dec);
printf("Client: encrypted=%zu, decrypted=%zu, errors=%zu/%zu/%zu\n",
total_enc, total_dec, enc_err, dec_err, send_err);
// Cleanup
close(server_fd);
close(client_fd);
free(server_etcp->crypto_ctx);
free(client_etcp->crypto_ctx);
free(server_etcp);
free(client_etcp);
printf("\n=== Test completed ===\n");
return 0;
}

42
utun_test_backup.conf

@ -0,0 +1,42 @@
[global]
my_private_key=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
my_public_key=abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789
my_node_id=babababa1123 # 64bit ID
option=value1,value2
tun_ip=10.0.0.1
mtu=1500 # MTU for all connections (0 = use default 1500)
control_ip=127.0.0.1
control_port=12345
net_debug=0
[routing]
allowed_subnet=10.0.0.0/24
allowed_subnet=10.22.0.0/16
allowed_subnet=10.23.0.0/16
my_subnet=10.23.5.0/24
my_subnet=10.23.6.0/24
# мои адреса и каналы
[server: wired1_fast]
addr=192.168.0.10:1234
so_mark=100
netif=eth0
type=nat # public / nat / private
[server: wireless_bkp]
addr=192.168.0.10:1234
so_mark=100
netif=eth0
type=public # public / nat / private
[client: client_test1]
# линки
link=wired1_fast:192.168.0.20:1234
link=wired1_fast:1.2.3.4:1234
link=wireless_bkp:192.168.0.20:1234
link=wireless_bkp:1.2.3.4:1234
#keepalive после ресета etcp передаём серверу
keepalive=1
peer_public_key=deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
Loading…
Cancel
Save