From 667d32a3e79599959de917ad6da3d0461e99620e Mon Sep 17 00:00:00 2001 From: bol-van Date: Sat, 16 Nov 2024 13:09:31 +0300 Subject: [PATCH 01/11] blockcheck: summary report all working strategies, tpws limit mss check --- blockcheck.sh | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/blockcheck.sh b/blockcheck.sh index dca4bad..ebed7a4 100755 --- a/blockcheck.sh +++ b/blockcheck.sh @@ -1011,6 +1011,15 @@ tpws_curl_test() echo - checking tpws $3 $4 $5 $6 $7 $8 $9${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"} local ALL_PROXY="socks5://127.0.0.1:$SOCKS_PORT" ws_curl_test tpws_start "$@"${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"} + local code=$? + [ "$code" = 0 ] && { + local testf=$1 dom=$2 + shift; shift; + local strategy="$@" + strategy_append_extra_tpws + report_append "ipv${IPV} $dom $testf : tpws ${WF:+$WF }$strategy" + } + return $code } pktws_curl_test() { @@ -1019,7 +1028,26 @@ pktws_curl_test() # $3,$4,$5, ... - nfqws/dvtws params echo - checking $PKTWSD ${WF:+$WF }$3 $4 $5 $6 $7 $8 $9${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"} ws_curl_test pktws_start "$@"${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"} + local code=$? + [ "$code" = 0 ] && { + local testf=$1 dom=$2 + shift; shift; + local strategy="$@" + strategy_append_extra_pktws + report_append "ipv${IPV} $dom $testf : $PKTWSD ${WF:+$WF }$strategy" + } + return $code } + +strategy_append_extra_pktws() +{ + strategy="${strategy:+$strategy${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}}" +} +strategy_append_extra_tpws() +{ + strategy="${strategy:+$strategy${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"}}" +} + xxxws_curl_test_update() { # $1 - xxx_curl_test function @@ -1071,7 +1099,7 @@ report_strategy() strategy="$(echo "$strategy" | xargs)" echo "!!!!! $1: working strategy found for ipv${IPV} $2 : $3 $strategy !!!!!" echo - report_append "ipv${IPV} $2 $1 : $3 ${WF:+$WF }$strategy" +# report_append "ipv${IPV} $2 $1 : $3 ${WF:+$WF }$strategy" return 0 else echo "$1: $3 strategy for ipv${IPV} $2 not found" @@ -1314,7 +1342,7 @@ pktws_check_domain_http_bypass() local strategy pktws_check_domain_http_bypass_ "$@" - strategy="${strategy:+$strategy${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}}" + strategy_append_extra_pktws report_strategy $1 $3 $PKTWSD } @@ -1359,7 +1387,7 @@ pktws_check_domain_http3_bypass() local strategy pktws_check_domain_http3_bypass_ "$@" - strategy="${strategy:+$strategy $PKTWS_EXTRA $PKTWS_EXTRA_1 $PKTWS_EXTRA_2 $PKTWS_EXTRA_3 $PKTWS_EXTRA_4 $PKTWS_EXTRA_5 $PKTWS_EXTRA_6 $PKTWS_EXTRA_7 $PKTWS_EXTRA_8 $PKTWS_EXTRA_9}" + strategy_append_extra_pktws report_strategy $1 $2 $PKTWSD } warn_mss() @@ -1402,12 +1430,14 @@ tpws_check_domain_http_bypass_() tpws_curl_test_update $1 $3 $s && [ "$SCANLEVEL" = quick ] && return done else + local need_mss=1 for mss in '' 88; do s3=${mss:+--mss=$mss} for s2 in '' '--oob' '--disorder' ${oobdis:+"$oobdis"}; do for pos in $splits_tls; do tpws_curl_test_update $1 $3 --split-pos=$pos $s2 $s3 && warn_mss $s3 && [ "$SCANLEVEL" != force ] && { [ "$SCANLEVEL" = quick ] && return + need_mss=0 break } done @@ -1416,12 +1446,14 @@ tpws_check_domain_http_bypass_() for s2 in '--tlsrec=midsld' '--tlsrec=sniext+1 --split-pos=midsld' '--tlsrec=sniext+4 --split-pos=midsld' '--tlsrec=sniext+1 --split-pos=1,midsld' '--tlsrec=sniext+4 --split-pos=1,midsld' ; do tpws_curl_test_update $1 $3 $s2 $s $s3 && warn_mss $s3 && [ "$SCANLEVEL" != force ] && { [ "$SCANLEVEL" = quick ] && return + need_mss=0 break } done done # only linux supports mss [ "$UNAME" = Linux -a "$sec" = 1 ] || break + [ "$SCANLEVEL" = force -o "$need_mss" = 1 ] || break done fi } @@ -1433,7 +1465,7 @@ tpws_check_domain_http_bypass() local strategy tpws_check_domain_http_bypass_ "$@" - strategy="${strategy:+$strategy${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"}}" + strategy_append_extra_tpws report_strategy $1 $3 tpws } From ee44aebcc410aa7a6aadf74b795550eaca7c957f Mon Sep 17 00:00:00 2001 From: bol-van Date: Sat, 16 Nov 2024 15:19:36 +0300 Subject: [PATCH 02/11] nfqws,tpws: remove @config from android --- nfq/dvtws | Bin 0 -> 157392 bytes nfq/nfqws.c | 12 ++++++------ nfq/params.h | 4 ++-- tpws/params.h | 4 ++-- tpws/tpws.c | 12 ++++++------ 5 files changed, 16 insertions(+), 16 deletions(-) create mode 100644 nfq/dvtws diff --git a/nfq/dvtws b/nfq/dvtws new file mode 100644 index 0000000000000000000000000000000000000000..2d9d47ea75fd4e43a78edb2d42cb7901c545d2b4 GIT binary patch literal 157392 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\t\t\t; read file for options. must be the only argument. other options are ignored.\n\n" #endif " --debug=0|1|syslog|@\n" @@ -1099,7 +1099,7 @@ static void exithelp_clean(void) exithelp(); } -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) // no static to not allow optimizer to inline this func (save stack) void config_from_file(const char *filename) { @@ -1192,7 +1192,7 @@ int main(int argc, char **argv) } #endif -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) if (argc>=2 && (argv[1][0]=='@' || argv[1][0]=='$')) { config_from_file(argv[1]+1); @@ -1963,7 +1963,7 @@ int main(int argc, char **argv) } // do not need args from file anymore -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) cleanup_args(); #endif argv=NULL; argc=0; diff --git a/nfq/params.h b/nfq/params.h index 630d20d..3b59f36 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -14,7 +14,7 @@ #include #include #include -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) #include #endif @@ -104,7 +104,7 @@ bool dp_list_have_autohostlist(struct desync_profile_list_head *head); struct params_s { -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) wordexp_t wexp; // for file based config #endif diff --git a/tpws/params.h b/tpws/params.h index 3afa694..acf9f4a 100644 --- a/tpws/params.h +++ b/tpws/params.h @@ -6,7 +6,7 @@ #include #include #include -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) #include #endif @@ -85,7 +85,7 @@ void dp_list_destroy(struct desync_profile_list_head *head); struct params_s { -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) wordexp_t wexp; // for file based config #endif diff --git a/tpws/tpws.c b/tpws/tpws.c index 8136da3..b154be7 100644 --- a/tpws/tpws.c +++ b/tpws/tpws.c @@ -122,7 +122,7 @@ static int get_default_ttl(void) static void exithelp(void) { printf( -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) " @|$\t\t; read file for options. must be the only argument. other options are ignored.\n\n" #endif " --bind-addr=|\t; for v6 link locals append %%interface_name\n" @@ -215,7 +215,7 @@ static void exithelp(void) ); exit(1); } -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) static void cleanup_args() { wordfree(¶ms.wexp); @@ -223,7 +223,7 @@ static void cleanup_args() #endif static void cleanup_params(void) { -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) cleanup_args(); #endif @@ -472,7 +472,7 @@ static bool parse_pf_list(char *opt, struct port_filters_head *pfl) return true; } -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) // no static to not allow optimizer to inline this func (save stack) void config_from_file(const char *filename) { @@ -547,7 +547,7 @@ void parse_params(int argc, char *argv[]) dp = &dpl->dp; dp->n = ++desync_profile_count; -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) if (argc>=2 && (argv[1][0]=='@' || argv[1][0]=='$')) { config_from_file(argv[1]+1); @@ -1288,7 +1288,7 @@ void parse_params(int argc, char *argv[]) SplitDebug(); VPRINT("\n"); -#ifndef __OpenBSD__ +#if !defined( __OpenBSD__) && !defined(__ANDROID__) // do not need args from file anymore cleanup_args(); #endif From 69007b5098b7558e50ef3ceabfa068196779d606 Mon Sep 17 00:00:00 2001 From: bol-van Date: Sat, 16 Nov 2024 16:43:26 +0300 Subject: [PATCH 03/11] improve compile docs --- docs/compile/build_howto_openwrt.txt | 38 +++++++++++++++++++--------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/docs/compile/build_howto_openwrt.txt b/docs/compile/build_howto_openwrt.txt index 9ff5f1a..cc20108 100644 --- a/docs/compile/build_howto_openwrt.txt +++ b/docs/compile/build_howto_openwrt.txt @@ -3,19 +3,33 @@ How to compile native programs for use in openwrt 1) Download latest SDK for your platform from https://downloads.openwrt.org - curl -o - https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz | tar -Jxvf - - cd openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64 +examples : -2) ./scripts/feeds update -a - ./scripts/feeds install -a +curl -o - https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz | tar -Jxvf - +cd openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64 -3) cp -R /opt/zapret/docs/compile/openwrt/. . - cp -R /opt/zapret/tpws package/zapret/tpws - cp -R /opt/zapret/nfq package/zapret/nfqws - cp -R /opt/zapret/mdig package/zapret/mdig - cp -R /opt/zapret/ip2net package/zapret/ip2net +curl -o - https://downloads.openwrt.org/snapshots/targets/x86/64/openwrt-sdk-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst | tar --zstd -xvf - +cd openwrt-sdk-x86-64_gcc-13.3.0_musl.Linux-x86_64 -4) make package/{tpws,nfqws,mdig,ip2net}/compile +2) Install required libs -5) find bin -name tpws*.ipk - #take your tpws*.ipk , nfqws*.ipk , ip2net*.ipk, mdig*.ipk from there +./scripts/feeds update base packages +./scripts/feeds install libnetfilter-queue zlib libcap + +3) Prepare openwrt package definitions + +sudo make -C /opt/zapret clean +cp -R /opt/zapret/docs/compile/openwrt/. . +cp -R /opt/zapret/tpws package/zapret/tpws +cp -R /opt/zapret/nfq package/zapret/nfqws +cp -R /opt/zapret/mdig package/zapret/mdig +cp -R /opt/zapret/ip2net package/zapret/ip2net + +4) Compile + +make package/{tpws,nfqws,mdig,ip2net}/compile + +5) Get result + +ls -l bin/packages/*/base +# take your ipk or apk from there From faa9a3e71434370a4bc392fb82890b255c55db01 Mon Sep 17 00:00:00 2001 From: bol-van Date: Sat, 16 Nov 2024 16:50:08 +0300 Subject: [PATCH 04/11] docs: remove wireguard patch info --- docs/readme.md | 12 +- docs/wireguard/010-wg-mod.patch | 133 ---------- docs/wireguard/wireguard-mod.txt | 250 ------------------ .../wireguard_iproute_openwrt.txt | 2 +- 4 files changed, 9 insertions(+), 388 deletions(-) delete mode 100644 docs/wireguard/010-wg-mod.patch delete mode 100644 docs/wireguard/wireguard-mod.txt rename docs/{wireguard => }/wireguard_iproute_openwrt.txt (98%) diff --git a/docs/readme.md b/docs/readme.md index e93b529..0edd17c 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1326,9 +1326,12 @@ linux, но через раз приобретает статус INVALID в con переменные. ``` -DOMAINS - список тестируемых доменов через пробел +CURL - замена программы curl CURL_MAX_TIME - время таймаута curl в секундах CURL_MAX_TIME_QUIC - время таймаута curl для quic. если не задано, используется значение CURL_MAX_TIME +CURL_CMD=1 - показывать команды curl +CURL_OPT - дополнительные параметры curl. `-k` - игнор сертификатов. `-v` - подробный вывод протокола +DOMAINS - список тестируемых доменов через пробел HTTP_PORT, HTTPS_PORT, QUIC_PORT - номера портов для соответствующих протоколов SKIP_DNSCHECK=1 - отказ от проверки DNS SKIP_TPWS=1 - отказ от тестов tpws @@ -1338,7 +1341,6 @@ PKTWS_EXTRA_1 .. PKTWS_EXTRA_9, TPWS_EXTRA_1 .. TPWS_EXTRA_9 - отдельно SECURE_DNS=0|1 - принудительно выключить или включить DoH DOH_SERVERS - список URL DoH через пробел для автоматического выбора работающего сервера DOH_SERVER - конкретный DoH URL, отказ от поиска -CURL - замена программы curl ``` Пример запуска с переменными:\ @@ -1471,7 +1473,7 @@ nfqws начнет получать адреса пакетов из локал Таким образом, все 3 режима вполне могут задействоваться вместе. Так же безусловно и независимо, в добавок к стандартным опциям, применяются все custom скрипты в `init.d/{sysv,openwrt,macos}/custom.d`. -Однако, при комбинировании tpws и nfqws не все так просто , как может показаться на первый взгляд. +Однако, при комбинировании tpws и nfqws с пересечением по L3/L4 протоколам не все так просто , как может показаться на первый взгляд. Первым всегда работает tpws, за ним - nfqws. На nfqws попадает уже "задуренный" трафик от tpws. Получается, что дурилка дурит дурилку, и дурилка не срабатывает, потому что ее задурили. Вот такой веселый момент. nfqws перестает распознавать протоколы и применять методы. @@ -1480,6 +1482,8 @@ nfqws начнет получать адреса пакетов из локал Комбинирование tpws и nfqws является продвинутым вариантом, требующим глубокого понимания происходящего. Очень желательно проанализировать действия nfqws по `--debug` логу. Все ли так, как вы задумали. +Одновременное использование tpws и nfqws без пересечения по L3/L4 (то есть nfqws - udp, tpws - tcp или nfqws - port 443, tpws - port 80 или nfqws - ipv4, tpws - ipv6) проблем не представляет. + `tpws-socks` требует настройки параметров `tpws`, но не требует перехвата трафика. Остальные опции требуют раздельно настройки перехвата трафика и опции самих демонов. Каждая опция предполагает запуск одного инстанса соответствующего демона. Все различия методов дурения @@ -2143,7 +2147,7 @@ Openwrt является одной из немногих относительн Если не работает автономный обход, приходится перенаправлять трафик через сторонний хост. Предлагается использовать прозрачный редирект через socks5 посредством `iptables+redsocks`, либо `iptables+iproute+vpn`. Настройка варианта с redsocks на openwrt описана в [redsocks.txt](./redsocks.txt). -Настройка варианта с `iproute+wireguard` - в [wireguard_iproute_openwrt.txt](./wireguard/wireguard_iproute_openwrt.txt). +Настройка варианта с `iproute+wireguard` - в [wireguard_iproute_openwrt.txt](./wireguard_iproute_openwrt.txt). ## Почему стоит вложиться в покупку VPS diff --git a/docs/wireguard/010-wg-mod.patch b/docs/wireguard/010-wg-mod.patch deleted file mode 100644 index 1577da6..0000000 --- a/docs/wireguard/010-wg-mod.patch +++ /dev/null @@ -1,133 +0,0 @@ -Index: WireGuard-0.0.20190123/src/cookie.c -=================================================================== ---- WireGuard-0.0.20190123.orig/src/cookie.c -+++ WireGuard-0.0.20190123/src/cookie.c -@@ -193,6 +193,8 @@ void wg_cookie_message_create(struct mes - xchacha20poly1305_encrypt(dst->encrypted_cookie, cookie, COOKIE_LEN, - macs->mac1, COOKIE_LEN, dst->nonce, - checker->cookie_encryption_key); -+ // MOD : randomize trash -+ dst->header.trash = gen_trash(); - } - - void wg_cookie_message_consume(struct message_handshake_cookie *src, -Index: WireGuard-0.0.20190123/src/messages.h -=================================================================== ---- WireGuard-0.0.20190123.orig/src/messages.h -+++ WireGuard-0.0.20190123/src/messages.h -@@ -53,23 +53,41 @@ enum limits { - MAX_QUEUED_PACKETS = 1024 /* TODO: replace this with DQL */ - }; - -+/* - enum message_type { -- MESSAGE_INVALID = 0, -- MESSAGE_HANDSHAKE_INITIATION = 1, -- MESSAGE_HANDSHAKE_RESPONSE = 2, -- MESSAGE_HANDSHAKE_COOKIE = 3, -- MESSAGE_DATA = 4 -+ MESSAGE_INVALID = 0, -+ MESSAGE_HANDSHAKE_INITIATION = 1, -+ MESSAGE_HANDSHAKE_RESPONSE = 2, -+ MESSAGE_HANDSHAKE_COOKIE = 3, -+ MESSAGE_DATA = 4 - }; -+*/ -+ -+// MOD : message type -+enum message_type { -+ MESSAGE_INVALID = 0xE319CCD0, -+ MESSAGE_HANDSHAKE_INITIATION = 0x48ADE198, -+ MESSAGE_HANDSHAKE_RESPONSE = 0xFCA6A8F3, -+ MESSAGE_HANDSHAKE_COOKIE = 0x64A3BB18, -+ MESSAGE_DATA = 0x391820AA -+}; -+ -+// MOD : generate fast trash without true RNG -+__le32 gen_trash(void); - - struct message_header { -- /* The actual layout of this that we want is: -- * u8 type -- * u8 reserved_zero[3] -- * -- * But it turns out that by encoding this as little endian, -- * we achieve the same thing, and it makes checking faster. -- */ -- __le32 type; -+ /* The actual layout of this that we want is: -+ * u8 type -+ * u8 reserved_zero[3] -+ * -+ * But it turns out that by encoding this as little endian, -+ * we achieve the same thing, and it makes checking faster. -+ */ -+ -+ // MOD : trash field to change message size and add 4 byte offset to all fields -+ __le32 trash; -+ -+ __le32 type; - }; - - struct message_macs { -Index: WireGuard-0.0.20190123/src/noise.c -=================================================================== ---- WireGuard-0.0.20190123.orig/src/noise.c -+++ WireGuard-0.0.20190123/src/noise.c -@@ -17,6 +17,24 @@ - #include - #include - -+ -+// MOD : trash generator -+__le32 gtrash = 0; -+__le32 gen_trash(void) -+{ -+ if (gtrash) -+ gtrash = gtrash*1103515243 + 12345; -+ else -+ // first value is true random -+ get_random_bytes_wait(>rash, sizeof(gtrash)); -+ return gtrash; -+} -+ - /* This implements Noise_IKpsk2: - * - * <- s -@@ -515,6 +533,10 @@ wg_noise_handshake_create_initiation(str - &handshake->entry); - - handshake->state = HANDSHAKE_CREATED_INITIATION; -+ -+ // MOD : randomize trash -+ dst->header.trash = gen_trash(); -+ - ret = true; - - out: -@@ -655,6 +677,10 @@ bool wg_noise_handshake_create_response( - &handshake->entry); - - handshake->state = HANDSHAKE_CREATED_RESPONSE; -+ -+ // MOD : randomize trash -+ dst->header.trash = gen_trash(); -+ - ret = true; - - out: -Index: WireGuard-0.0.20190123/src/send.c -=================================================================== ---- WireGuard-0.0.20190123.orig/src/send.c -+++ WireGuard-0.0.20190123/src/send.c -@@ -200,6 +200,10 @@ static bool encrypt_packet(struct sk_buf - header->header.type = cpu_to_le32(MESSAGE_DATA); - header->key_idx = keypair->remote_index; - header->counter = cpu_to_le64(PACKET_CB(skb)->nonce); -+ -+ // MOD : randomize trash -+ header->header.trash = gen_trash(); -+ - pskb_put(skb, trailer, trailer_len); - - /* Now we can encrypt the scattergather segments */ diff --git a/docs/wireguard/wireguard-mod.txt b/docs/wireguard/wireguard-mod.txt deleted file mode 100644 index 878aa44..0000000 --- a/docs/wireguard/wireguard-mod.txt +++ /dev/null @@ -1,250 +0,0 @@ -!!! Эта инструкция написана еще до включения wireguard в ядро linux. -!!! Процесс сборки для in-tree модулей отличается. -!!! Цель данного чтива - дать идею для программистов как можно исправить исходники wireguard -!!! для преодоления DPI. Автор не преследует цели поддерживать готовые патчи для актуальных версий. -!!! Вместо патчинга гораздо проще использовать навесное решение ipobfs. - -Посвящено возможной блокировке в РФ VPN протоколов через DPI. -Предпосылками являются последние законодательные акты и во всю сочащиеся "секретные" записки. -В РФ разрабатываются и готовятся к применению более продвинутые решения по блокировке трафика. -Вполне вероятно будут резать стандартные VPN протоколы. Нам надо быть к этому готовыми. - -Один из возможных и перспективных путей решения данного вопроса - кустомная модификация -исходников VPN с целью незначительного изменения протокола, ломающего стандартные модули обнаружения в DPI. -Это относительно сложно, доступно только для гиков. -Никто не будет разрабатывать специальные модули обнаружения в DPI, если только кто-то не сделает простое и -удобное решение для всех, и его станут широко применять. Но это маловероятно, и даже если и так, -то всегда можно модифицировать протокол чуток по другому. Делать моды для DPI несравненно дольше -и дороже, чем клепать на коленке изменения протокола для wireguard. - - -ЗАМЕЧЕНИЕ : альтернативой модификации конечного софта для VPN является использование "навесных" -обфускаторов. см : https://github.com/bol-van/ipobfs - - -Рассмотрю что нам надо пропатчить в wireguard. Модифицированный wireguard проверен на виртуалках -с десктопным linux, он работает, сообщения в wireshark действительно не вписываются в стандартный -протокол и не опознаются. - -Wireguard протокол очень простой. Все сообщения описаны в messages.h -Поставим себе целью сделать 2 простые модификации : -1) Добавим в начало всех сообщений немного мусора, чтобы изменить размер сообщений и смещения полей -2) Изменим коды типов сообщений -Этого может быть вполне достаточно для обмана DPI - ---messages.h-------------------------- -/* -enum message_type { - MESSAGE_INVALID = 0, - MESSAGE_HANDSHAKE_INITIATION = 1, - MESSAGE_HANDSHAKE_RESPONSE = 2, - MESSAGE_HANDSHAKE_COOKIE = 3, - MESSAGE_DATA = 4 -}; -*/ - -// MOD : message type -enum message_type { - MESSAGE_INVALID = 0xE319CCD0, - MESSAGE_HANDSHAKE_INITIATION = 0x48ADE198, - MESSAGE_HANDSHAKE_RESPONSE = 0xFCA6A8F3, - MESSAGE_HANDSHAKE_COOKIE = 0x64A3BB18, - MESSAGE_DATA = 0x391820AA -}; - -// MOD : generate fast trash without true RNG -__le32 gen_trash(void); - -struct message_header { - /* The actual layout of this that we want is: - * u8 type - * u8 reserved_zero[3] - * - * But it turns out that by encoding this as little endian, - * we achieve the same thing, and it makes checking faster. - */ - - // MOD : trash field to change message size and add 4 byte offset to all fields - __le32 trash; - - __le32 type; -}; --------------------------------------- - -Напишем функцию для генерации trash. Функция должна быть быстрая, важно не замедлить скорость. -Мы не расчитываем, что нас будут специально ловить, иначе бы пришлось делать полноценный обфускатор. -Задача лишь сломать стандартный модуль обнаружения протокола wireguard. Потому истинная рандомность -trash не важна. -Но все же немного "трэша" не повредит. Гонки между тредами так же пофигистичны. Это же трэш. - ---noise.c----------------------------- -// MOD : trash generator -__le32 gtrash = 0; -__le32 gen_trash(void) -{ - if (gtrash) - gtrash = gtrash*1103515243 + 12345; - else - // first value is true random - get_random_bytes_wait(>rash, sizeof(gtrash)); - return gtrash; -} --------------------------------------- - -Теперь осталось найти все места, где создаются сообщения и внести туда заполнение поля trash. -Сообщений всего 4. Их можно найти по присваиванию полю type одного из значений enum message_type. - -2 места в noise.c в функциях wg_noise_handshake_create_initiation и wg_noise_handshake_create_response, -1 место в cookie.c в функции wg_cookie_message_create -Дописываем в конец инициализации структуры сообщения : - --------------------------------------- - // MOD : randomize trash - dst->header.trash = gen_trash(); --------------------------------------- - -и 1 место в send.c в функции encrypt_packet - --------------------------------------- - // MOD : randomize trash - header->header.trash = gen_trash(); --------------------------------------- - - -Вот и весь патчинг. Полный patch (версия wireguard 0.0.20190123) лежит в 010-wg-mod.patch. -Патчинг кода - самое простое. Для десктопного linux дальше все просто. -Пересобираем через make, устанавливаем через make install, перегружаем -модуль wireguard, перезапускаем интерфейсы, и все готово. - -Настоящий геморой начнется когда вы это попытаетесь засунуть на роутер под openwrt. -Одна из больших проблем linux - отсутствие совместимости драйверов на уровне бинариков. -Поэтому собирать необходимо в точности под вашу версию ядра и в точности под его .config. -Вам придется либо полностью самостоятельно собирать всю прошивку, либо найти SDK в точности -от вашей версии прошивки для вашей архитектуры и собрать модуль с помощью этого SDK. -Последний вариант более легкий. -Для сборки вам понадобится система на linux x86_64. Ее можно установить в виртуалке. -Теоретически можно пользоваться WSL из win10, но на практике там очень медленное I/O, -по крайней мере на старых версиях win10. Безумно медленное. Будете собирать вечность. -Может в новых win10 что-то и улучшили, но я бы сразу расчитывал на полноценный linux. - -Находим здесь вашу версию : https://downloads.openwrt.org/ -Скачиваем файл openwrt-sdk-*.tar.xz или lede-sdk-*.tar.xz -Например : https://downloads.openwrt.org/releases/18.06.2/targets/ar71xx/generic/openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64.tar.xz -Если ваша версия непонятна или стара, то проще будет найти последнюю прошивку и перешить роутер. -Распаковываем SDK. Следующими командами можно собрать оригинальный вариант wireguard : - -# scripts/feeds update -a -# scripts/feeds install -a -# make defconfig -# make -j 4 package/wireguard/compile - -Сборка будет довольно долгой. Ведь придется подтащить ядро, собрать его, собрать зависимости. -"-j 4" означает использовать 4 потока. Впишите вместо 4 количество доступных cpu cores. - -Получим следующие файлы : - -openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/bin/targets/ar71xx/generic/packages/kmod-wireguard_4.9.152+0.0.20190123-1_mips_24kc.ipk -openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/bin/packages/mips_24kc/base/wireguard-tools_0.0.20190123-1_mips_24kc.ipk - -Но это будет оригинальный wireguard. Нам нужен патченый. -Установим quilt и mc для нормального редактора вместо vim : - -# sudo apt-get update -# sudo apt-get install quilt mc - -# make package/wireguard/clean -# make package/wireguard/prepare V=s QUILT=1 - - -Сорцы приготовлены для сборки в : - openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/build_dir/target-mips_24kc_musl/linux-ar71xx_generic/WireGuard-0.0.20190123/src - -# cd build_dir/target-mips_24kc_musl/linux-ar71xx_generic/WireGuard-0.0.20190123/src -# quilt push -a -# quilt new 010-wg-mod.patch -# export EDITOR=mcedit - -Далее будет открываться редактор mcedit, в который нужно вносить изменения в каждый файл : - -# quilt edit messages.h -# quilt edit cookie.c -# quilt edit noise.c -# quilt edit send.c -# quilt diff -# quilt refresh - -Получили файл патча в : -openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/build_dir/target-mips_24kc_musl/linux-ar71xx_generic/WireGuard-0.0.20190123/patches/010-wg-mod.patch - -Выходим в корень SDK. - -# make package/wireguard/compile V=99 - -Если не было ошибок, то получили измененные ipk. -Патч можно зафиксировать в описании пакета : - -# make package/wireguard/update - -Получим : -openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/feeds/base/package/network/services/wireguard/patches/010-wg-mod.patch -При последующей очистке и пересборке он будет автоматом применяться. - - -АЛЬТЕРНАТИВА : можно не возиться с quilt. -сделайте -# make package/wireguard/clean -# make package/wireguard/prepare -и напрямую модифицируйте или копируйте файлы в - openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/build_dir/target-mips_24kc_musl/linux-ar71xx_generic/WireGuard-0.0.20190123/src -затем -# make package/wireguard/compile - -Если нужно поменять версию wireguard, то идите в -openwrt-sdk-18.06.2-ar71xx-generic_gcc-7.3.0_musl.Linux-x86_64/feeds/base/package/network/services/wireguard/Makefile -поменяйте там версию в PKG_VERSION на последнюю из : https://git.zx2c4.com/WireGuard -скачайте tar.xz с этой версией , вычислите его sha256sum, впишите в PKG_HASH - -1 раз где-нибудь пропатчите файлы последней версии wireguard в текстовом редакторе, скопируйте в build_dir, -сделайте версию для openwrt. эти же файлы скопируйте на ваш сервер с десктопным linux, сделайте там make / make install - -Но имейте в виду, что build_dir - локация для временных файлов. -make clean оттуда все снесет, включая ваши модификации. Модифицированные файлы лучше сохранить отдельно, -чтобы потом было легко скопировать обратно. - -Полученные ipk копируем на роутер в /tmp, устанавливаем через -# cd /tmp -# rm -r /tmp/opkg-lists -# opkg install *.ipk -Если требует зависимостей, то -# opkg update -# opkg install .... <зависимости> -# rm -r /tmp/opkg-lists -# opkg install *.ipk - -В /tmp/opkg-lists opkg хранит кэш списка пакетов. Если попытаться установить файл ipk, и такой же пакет -найдется в репозитории, opkg будет устанавливать из репозитория. А нам это не надо. - -# rmmod wireguard -# kmodloader -# dmesg | tail -должны увидеть что-то вроде : -[8985.415490] wireguard: WireGuard 0.0.20190123 loaded. See www.wireguard.com for information. -[8985.424178] wireguard: Copyright (C) 2015-2019 Jason A. Donenfeld . All Rights Reserved. -значит модуль загрузился - -Могут понадобиться ключи opkg --force-reinstall, --force-depends. ---force-depends поможет при несоответствии hash версии ядра. То есть версия x.x.x та же самая, но hash конфигурации разный. -При несоответствии x.x.x вы что-то делаете не так, работать это не будет. -Например : 4.14.56-1-b1186491495127cc6ff81d29c00a91fc, 4.14.56-1-3f8a21a63974cfb7ee67e41f2d4b805d -Это свидетельствует о несоответствии .config ядра при сборке прошивки и в SDK. -Если несоответствие легкое, то может все прокатить, но при более серьезной разнице в .config модуль может не загрузиться -или вызвать стабильные или хаотические падения ядра и перезагрузки (включая вариант беcконечной перезагрузки - bootloop). -Так что перед --force-depends убедитесь, что знаете как лечится такая ситуация, и не стоит это делать при отсутствии физического -доступа к девайсу. - -Когда поднимите линк, и вдруг ничего не будет работать, то посмотрите в wireshark udp пакеты -на порт endpoint. Они не должны начинаться с 0,1,2,3,4. В первых 4 байтах должен быть рандом, -в следующих 4 байтах - значения из измененного enum message_type. Если пакет все еще начинается с 0..4, -значит модуль wireguard оригинальный, что-то не собралось, не скопировалось, не перезапустилось. -В противном случае должен подняться линк, пинги ходить. Значит вы победили, поздравляю. -Регулятору будет намного сложнее поймать ваш VPN. diff --git a/docs/wireguard/wireguard_iproute_openwrt.txt b/docs/wireguard_iproute_openwrt.txt similarity index 98% rename from docs/wireguard/wireguard_iproute_openwrt.txt rename to docs/wireguard_iproute_openwrt.txt index 8258b44..f9d610a 100644 --- a/docs/wireguard/wireguard_iproute_openwrt.txt +++ b/docs/wireguard_iproute_openwrt.txt @@ -236,7 +236,7 @@ config rule --- Подготовка zapret --- -Выполните install_easy.sh. Он настроит режим обхода DPI. Если обход DPI не нужен - выберите MODE=filter. +Выполните install_easy.sh. Он настроит режим обхода DPI. Если обход DPI не нужен - не включайте tpws и nfqws. Так же инсталятор заресолвит домены из ipset/zapret-hosts-user-ipban.txt и внесет крон-джоб для периодического обновления ip. Если вы используете в своих правилах ipset zapret, то он ресолвится и обновляется только, если выбран режим фильтрации обхода DPI по ipset. From ebcec6e79da7da7871ec6b3629afecf520764156 Mon Sep 17 00:00:00 2001 From: bol-van Date: Sat, 16 Nov 2024 18:25:53 +0300 Subject: [PATCH 05/11] tpws: support android versions 5+ --- tpws/Makefile | 7 +- tpws/andr/_musl_license.txt | 26 +++++ tpws/andr/getifaddrs.c | 216 ++++++++++++++++++++++++++++++++++++ tpws/andr/ifaddrs.h | 8 ++ tpws/andr/netlink.c | 54 +++++++++ tpws/andr/netlink.h | 94 ++++++++++++++++ tpws/helpers.c | 7 +- tpws/tpws.c | 7 +- 8 files changed, 416 insertions(+), 3 deletions(-) create mode 100644 tpws/andr/_musl_license.txt create mode 100644 tpws/andr/getifaddrs.c create mode 100644 tpws/andr/ifaddrs.h create mode 100644 tpws/andr/netlink.c create mode 100644 tpws/andr/netlink.h diff --git a/tpws/Makefile b/tpws/Makefile index b2b00ea..00814c4 100644 --- a/tpws/Makefile +++ b/tpws/Makefile @@ -2,12 +2,17 @@ CC ?= gcc CFLAGS += -std=gnu99 -Os CFLAGS_BSD = -Wno-address-of-packed-member LIBS = -lz -lpthread +LIBS_ANDROID = -lz SRC_FILES = *.c +SRC_FILES_ANDROID = $(SRC_FILES) andr/*.c all: tpws tpws: $(SRC_FILES) - $(CC) -s $(CFLAGS) -o $@ $(SRC_FILES) $(LDFLAGS) $(LIBS) + $(CC) -s $(CFLAGS) -o tpws $(SRC_FILES) $(LDFLAGS) $(LIBS) + +android: $(SRC_FILES) + $(CC) -s $(CFLAGS) -o tpws $(SRC_FILES_ANDROID) $(LDFLAGS) $(LIBS_ANDROID) bsd: $(SRC_FILES) $(CC) -s $(CFLAGS) $(CFLAGS_BSD) -Iepoll-shim/include -o tpws $(SRC_FILES) epoll-shim/src/*.c $(LDFLAGS) $(LIBS) diff --git a/tpws/andr/_musl_license.txt b/tpws/andr/_musl_license.txt new file mode 100644 index 0000000..5ae13c6 --- /dev/null +++ b/tpws/andr/_musl_license.txt @@ -0,0 +1,26 @@ +Code in this dir is taken from musl libc to support old android versions <7.0 + +musl as a whole is licensed under the following standard MIT license: + +---------------------------------------------------------------------- +Copyright 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +---------------------------------------------------------------------- diff --git a/tpws/andr/getifaddrs.c b/tpws/andr/getifaddrs.c new file mode 100644 index 0000000..74df4d6 --- /dev/null +++ b/tpws/andr/getifaddrs.c @@ -0,0 +1,216 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include "netlink.h" + +#define IFADDRS_HASH_SIZE 64 + +/* getifaddrs() reports hardware addresses with PF_PACKET that implies + * struct sockaddr_ll. But e.g. Infiniband socket address length is + * longer than sockaddr_ll.ssl_addr[8] can hold. Use this hack struct + * to extend ssl_addr - callers should be able to still use it. */ +struct sockaddr_ll_hack { + unsigned short sll_family, sll_protocol; + int sll_ifindex; + unsigned short sll_hatype; + unsigned char sll_pkttype, sll_halen; + unsigned char sll_addr[24]; +}; + +union sockany { + struct sockaddr sa; + struct sockaddr_ll_hack ll; + struct sockaddr_in v4; + struct sockaddr_in6 v6; +}; + +struct ifaddrs_storage { + struct ifaddrs ifa; + struct ifaddrs_storage *hash_next; + union sockany addr, netmask, ifu; + unsigned int index; + char name[IFNAMSIZ+1]; +}; + +struct ifaddrs_ctx { + struct ifaddrs *first; + struct ifaddrs *last; + struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE]; +}; + +void freeifaddrs(struct ifaddrs *ifp) +{ + struct ifaddrs *n; + while (ifp) { + n = ifp->ifa_next; + free(ifp); + ifp = n; + } +} + +static void copy_addr(struct sockaddr **r, int af, union sockany *sa, void *addr, size_t addrlen, int ifindex) +{ + uint8_t *dst; + int len; + + switch (af) { + case AF_INET: + dst = (uint8_t*) &sa->v4.sin_addr; + len = 4; + break; + case AF_INET6: + dst = (uint8_t*) &sa->v6.sin6_addr; + len = 16; + if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_MC_LINKLOCAL(addr)) + sa->v6.sin6_scope_id = ifindex; + break; + default: + return; + } + if (addrlen < len) return; + sa->sa.sa_family = af; + memcpy(dst, addr, len); + *r = &sa->sa; +} + +static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, int prefixlen) +{ + uint8_t addr[16] = {0}; + int i; + + if (prefixlen > 8*sizeof(addr)) prefixlen = 8*sizeof(addr); + i = prefixlen / 8; + memset(addr, 0xff, i); + if (i < sizeof(addr)) addr[i++] = 0xff << (8 - (prefixlen % 8)); + copy_addr(r, af, sa, addr, sizeof(addr), 0); +} + +static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr, size_t addrlen, int ifindex, unsigned short hatype) +{ + if (addrlen > sizeof(sa->ll.sll_addr)) return; + sa->ll.sll_family = AF_PACKET; + sa->ll.sll_ifindex = ifindex; + sa->ll.sll_hatype = hatype; + sa->ll.sll_halen = addrlen; + memcpy(sa->ll.sll_addr, addr, addrlen); + *r = &sa->sa; +} + +static int netlink_msg_to_ifaddr(void *pctx, struct nlmsghdr *h) +{ + struct ifaddrs_ctx *ctx = pctx; + struct ifaddrs_storage *ifs, *ifs0; + struct ifinfomsg *ifi = NLMSG_DATA(h); + struct ifaddrmsg *ifa = NLMSG_DATA(h); + struct rtattr *rta; + int stats_len = 0; + + if (h->nlmsg_type == RTM_NEWLINK) { + for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) { + if (rta->rta_type != IFLA_STATS) continue; + stats_len = RTA_DATALEN(rta); + break; + } + } else { + for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0; ifs0 = ifs0->hash_next) + if (ifs0->index == ifa->ifa_index) + break; + if (!ifs0) return 0; + } + + ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len); + if (ifs == 0) return -1; + + if (h->nlmsg_type == RTM_NEWLINK) { + ifs->index = ifi->ifi_index; + ifs->ifa.ifa_flags = ifi->ifi_flags; + + for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) { + switch (rta->rta_type) { + case IFLA_IFNAME: + if (RTA_DATALEN(rta) < sizeof(ifs->name)) { + memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta)); + ifs->ifa.ifa_name = ifs->name; + } + break; + case IFLA_ADDRESS: + copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type); + break; + case IFLA_BROADCAST: + copy_lladdr(&ifs->ifa.ifa_broadaddr, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type); + break; + case IFLA_STATS: + ifs->ifa.ifa_data = (void*)(ifs+1); + memcpy(ifs->ifa.ifa_data, RTA_DATA(rta), RTA_DATALEN(rta)); + break; + } + } + if (ifs->ifa.ifa_name) { + unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE; + ifs->hash_next = ctx->hash[bucket]; + ctx->hash[bucket] = ifs; + } + } else { + ifs->ifa.ifa_name = ifs0->ifa.ifa_name; + ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags; + for (rta = NLMSG_RTA(h, sizeof(*ifa)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) { + switch (rta->rta_type) { + case IFA_ADDRESS: + /* If ifa_addr is already set we, received an IFA_LOCAL before + * so treat this as destination address */ + if (ifs->ifa.ifa_addr) + copy_addr(&ifs->ifa.ifa_dstaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index); + else + copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index); + break; + case IFA_BROADCAST: + copy_addr(&ifs->ifa.ifa_broadaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index); + break; + case IFA_LOCAL: + /* If ifa_addr is set and we get IFA_LOCAL, assume we have + * a point-to-point network. Move address to correct field. */ + if (ifs->ifa.ifa_addr) { + ifs->ifu = ifs->addr; + ifs->ifa.ifa_dstaddr = &ifs->ifu.sa; + memset(&ifs->addr, 0, sizeof(ifs->addr)); + } + copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index); + break; + case IFA_LABEL: + if (RTA_DATALEN(rta) < sizeof(ifs->name)) { + memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta)); + ifs->ifa.ifa_name = ifs->name; + } + break; + } + } + if (ifs->ifa.ifa_addr) + gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen); + } + + if (ifs->ifa.ifa_name) { + if (!ctx->first) ctx->first = &ifs->ifa; + if (ctx->last) ctx->last->ifa_next = &ifs->ifa; + ctx->last = &ifs->ifa; + } else { + free(ifs); + } + return 0; +} + +int getifaddrs(struct ifaddrs **ifap) +{ + struct ifaddrs_ctx _ctx, *ctx = &_ctx; + int r; + memset(ctx, 0, sizeof *ctx); + r = __rtnetlink_enumerate(AF_UNSPEC, AF_UNSPEC, netlink_msg_to_ifaddr, ctx); + if (r == 0) *ifap = ctx->first; + else freeifaddrs(ctx->first); + return r; +} diff --git a/tpws/andr/ifaddrs.h b/tpws/andr/ifaddrs.h new file mode 100644 index 0000000..a729809 --- /dev/null +++ b/tpws/andr/ifaddrs.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +#if __ANDROID_API__ < 24 +void freeifaddrs(struct ifaddrs *); +int getifaddrs(struct ifaddrs **); +#endif diff --git a/tpws/andr/netlink.c b/tpws/andr/netlink.c new file mode 100644 index 0000000..ad61a2d --- /dev/null +++ b/tpws/andr/netlink.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +#include "netlink.h" + +static int __netlink_enumerate(int fd, unsigned int seq, int type, int af, + int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx) +{ + struct nlmsghdr *h; + union { + uint8_t buf[8192]; + struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req; + struct nlmsghdr reply; + } u; + int r, ret; + + memset(&u.req, 0, sizeof(u.req)); + u.req.nlh.nlmsg_len = sizeof(u.req); + u.req.nlh.nlmsg_type = type; + u.req.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; + u.req.nlh.nlmsg_seq = seq; + u.req.g.rtgen_family = af; + r = send(fd, &u.req, sizeof(u.req), 0); + if (r < 0) return r; + + while (1) { + r = recv(fd, u.buf, sizeof(u.buf), MSG_DONTWAIT); + if (r <= 0) return -1; + for (h = &u.reply; NLMSG_OK(h, (void*)&u.buf[r]); h = NLMSG_NEXT(h)) { + if (h->nlmsg_type == NLMSG_DONE) return 0; + if (h->nlmsg_type == NLMSG_ERROR) return -1; + ret = cb(ctx, h); + if (ret) return ret; + } + } +} + +int __rtnetlink_enumerate(int link_af, int addr_af, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx) +{ + int fd, r; + + fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE); + if (fd < 0) return -1; + r = __netlink_enumerate(fd, 1, RTM_GETLINK, link_af, cb, ctx); + if (!r) r = __netlink_enumerate(fd, 2, RTM_GETADDR, addr_af, cb, ctx); + close(fd); + return r; +} diff --git a/tpws/andr/netlink.h b/tpws/andr/netlink.h new file mode 100644 index 0000000..939c56c --- /dev/null +++ b/tpws/andr/netlink.h @@ -0,0 +1,94 @@ +#include + +/* linux/netlink.h */ + +#define NETLINK_ROUTE 0 + +struct nlmsghdr { + uint32_t nlmsg_len; + uint16_t nlmsg_type; + uint16_t nlmsg_flags; + uint32_t nlmsg_seq; + uint32_t nlmsg_pid; +}; + +#define NLM_F_REQUEST 1 +#define NLM_F_MULTI 2 +#define NLM_F_ACK 4 + +#define NLM_F_ROOT 0x100 +#define NLM_F_MATCH 0x200 +#define NLM_F_ATOMIC 0x400 +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +#define NLMSG_NOOP 0x1 +#define NLMSG_ERROR 0x2 +#define NLMSG_DONE 0x3 +#define NLMSG_OVERRUN 0x4 + +/* linux/rtnetlink.h */ + +#define RTM_NEWLINK 16 +#define RTM_GETLINK 18 +#define RTM_NEWADDR 20 +#define RTM_GETADDR 22 + +struct rtattr { + unsigned short rta_len; + unsigned short rta_type; +}; + +struct rtgenmsg { + unsigned char rtgen_family; +}; + +struct ifinfomsg { + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; + int ifi_index; + unsigned ifi_flags; + unsigned ifi_change; +}; + +/* linux/if_link.h */ + +#define IFLA_ADDRESS 1 +#define IFLA_BROADCAST 2 +#define IFLA_IFNAME 3 +#define IFLA_STATS 7 + +/* linux/if_addr.h */ + +struct ifaddrmsg { + uint8_t ifa_family; + uint8_t ifa_prefixlen; + uint8_t ifa_flags; + uint8_t ifa_scope; + uint32_t ifa_index; +}; + +#define IFA_ADDRESS 1 +#define IFA_LOCAL 2 +#define IFA_LABEL 3 +#define IFA_BROADCAST 4 + +/* musl */ + +#define NETLINK_ALIGN(len) (((len)+3) & ~3) +#define NLMSG_DATA(nlh) ((void*)((char*)(nlh)+sizeof(struct nlmsghdr))) +#define NLMSG_DATALEN(nlh) ((nlh)->nlmsg_len-sizeof(struct nlmsghdr)) +#define NLMSG_DATAEND(nlh) ((char*)(nlh)+(nlh)->nlmsg_len) +#define NLMSG_NEXT(nlh) (struct nlmsghdr*)((char*)(nlh)+NETLINK_ALIGN((nlh)->nlmsg_len)) +#define NLMSG_OK(nlh,end) ((char*)(end)-(char*)(nlh) >= sizeof(struct nlmsghdr)) + +#define RTA_DATA(rta) ((void*)((char*)(rta)+sizeof(struct rtattr))) +#define RTA_DATALEN(rta) ((rta)->rta_len-sizeof(struct rtattr)) +#define RTA_DATAEND(rta) ((char*)(rta)+(rta)->rta_len) +#define RTA_NEXT(rta) (struct rtattr*)((char*)(rta)+NETLINK_ALIGN((rta)->rta_len)) +#define RTA_OK(rta,end) ((char*)(end)-(char*)(rta) >= sizeof(struct rtattr)) + +#define NLMSG_RTA(nlh,len) ((void*)((char*)(nlh)+sizeof(struct nlmsghdr)+NETLINK_ALIGN(len))) +#define NLMSG_RTAOK(rta,nlh) RTA_OK(rta,NLMSG_DATAEND(nlh)) + +int __rtnetlink_enumerate(int link_af, int addr_af, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx); diff --git a/tpws/helpers.c b/tpws/helpers.c index 94cff17..c25fd4f 100644 --- a/tpws/helpers.c +++ b/tpws/helpers.c @@ -7,11 +7,16 @@ #include #include #include -#include #include #include #include +#ifdef __ANDROID__ +#include "andr/ifaddrs.h" +#else +#include +#endif + #include "helpers.h" int unique_size_t(size_t *pu, int ct) diff --git a/tpws/tpws.c b/tpws/tpws.c index b154be7..8d30561 100644 --- a/tpws/tpws.c +++ b/tpws/tpws.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -25,6 +24,12 @@ #include #include +#ifdef __ANDROID__ +#include "andr/ifaddrs.h" +#else +#include +#endif + #include "tpws.h" #ifdef BSD From 8624ae1c4abacc74b02f3fe2c75d7abf84b9c781 Mon Sep 17 00:00:00 2001 From: bol-van Date: Sat, 16 Nov 2024 18:34:55 +0300 Subject: [PATCH 06/11] makefiles use fixed executable names --- ip2net/Makefile | 2 +- mdig/Makefile | 2 +- nfq/Makefile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ip2net/Makefile b/ip2net/Makefile index 56f6b17..dedffbe 100644 --- a/ip2net/Makefile +++ b/ip2net/Makefile @@ -9,7 +9,7 @@ SRC_FILES = ip2net.c qsort.c all: ip2net ip2net: $(SRC_FILES) - $(CC) -s $(CFLAGS) -o $@ $(SRC_FILES) $(LDFLAGS) $(LIBS) + $(CC) -s $(CFLAGS) -o ip2net $(SRC_FILES) $(LDFLAGS) $(LIBS) bsd: $(SRC_FILES) $(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o ip2net $(SRC_FILES) $(LDFLAGS) $(LIBS) diff --git a/mdig/Makefile b/mdig/Makefile index 0100b5b..972195d 100644 --- a/mdig/Makefile +++ b/mdig/Makefile @@ -9,7 +9,7 @@ SRC_FILES = *.c all: mdig mdig: $(SRC_FILES) - $(CC) -s $(CFLAGS) -o $@ $(SRC_FILES) $(LDFLAGS) $(LIBS) + $(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LDFLAGS) $(LIBS) bsd: $(SRC_FILES) $(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o mdig $(SRC_FILES) $(LDFLAGS) $(LIBS) diff --git a/nfq/Makefile b/nfq/Makefile index 8e4c058..3bdfd25 100644 --- a/nfq/Makefile +++ b/nfq/Makefile @@ -15,7 +15,7 @@ SRC_FILES = *.c crypto/*.c all: nfqws nfqws: $(SRC_FILES) - $(CC) -s $(CFLAGS) -o $@ $(SRC_FILES) $(LDFLAGS) $(LIBS_LINUX) + $(CC) -s $(CFLAGS) -o nfqws $(SRC_FILES) $(LDFLAGS) $(LIBS_LINUX) bsd: $(SRC_FILES) $(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o dvtws $(SRC_FILES) $(LDFLAGS) $(LIBS_BSD) From 2816f9383184fe6d6ac5f3b711639bc0025cc921 Mon Sep 17 00:00:00 2001 From: bol-van Date: Sat, 16 Nov 2024 18:35:56 +0300 Subject: [PATCH 07/11] makefiles use fixed executable names --- nfq/BSDmakefile | 2 +- tpws/BSDmakefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nfq/BSDmakefile b/nfq/BSDmakefile index ff5475e..e782a07 100644 --- a/nfq/BSDmakefile +++ b/nfq/BSDmakefile @@ -6,7 +6,7 @@ SRC_FILES = *.c crypto/*.c all: dvtws dvtws: $(SRC_FILES) - $(CC) $(CFLAGS) -o $@ $(SRC_FILES) $(LDFLAGS) $(LIBS) + $(CC) $(CFLAGS) -o dvtws $(SRC_FILES) $(LDFLAGS) $(LIBS) clean: rm -f dvtws diff --git a/tpws/BSDmakefile b/tpws/BSDmakefile index e9ad901..c86e4bd 100644 --- a/tpws/BSDmakefile +++ b/tpws/BSDmakefile @@ -6,7 +6,7 @@ SRC_FILES = *.c all: tpws tpws: $(SRC_FILES) - $(CC) $(CFLAGS) -Iepoll-shim/include -o $@ $(SRC_FILES) epoll-shim/src/*.c $(LDFLAGS) $(LIBS) + $(CC) $(CFLAGS) -Iepoll-shim/include -o tpws $(SRC_FILES) epoll-shim/src/*.c $(LDFLAGS) $(LIBS) clean: rm -f tpws *.o From 97f20a1cb5e2f9f73f857a31532bd75730e07596 Mon Sep 17 00:00:00 2001 From: bol-van Date: Sat, 16 Nov 2024 19:08:02 +0300 Subject: [PATCH 08/11] improve compile doc --- docs/compile/build_howto_openwrt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/compile/build_howto_openwrt.txt b/docs/compile/build_howto_openwrt.txt index cc20108..3a61c85 100644 --- a/docs/compile/build_howto_openwrt.txt +++ b/docs/compile/build_howto_openwrt.txt @@ -18,12 +18,12 @@ cd openwrt-sdk-x86-64_gcc-13.3.0_musl.Linux-x86_64 3) Prepare openwrt package definitions -sudo make -C /opt/zapret clean cp -R /opt/zapret/docs/compile/openwrt/. . cp -R /opt/zapret/tpws package/zapret/tpws cp -R /opt/zapret/nfq package/zapret/nfqws cp -R /opt/zapret/mdig package/zapret/mdig cp -R /opt/zapret/ip2net package/zapret/ip2net +rm -f package/zapret/tpws/tpws/tpws package/zapret/nfqws/nfq/nfqws package/zapret/mdig/mdig/mdig package/zapret/ip2net/ip2net/ip2net 4) Compile From 53546a8d92cfd1824a7872def83fbdec9a25bc82 Mon Sep 17 00:00:00 2001 From: bol-van Date: Sat, 16 Nov 2024 19:32:58 +0300 Subject: [PATCH 09/11] update makefiles to build all progs for android --- Makefile | 13 +++++++++++++ ip2net/Makefile | 2 ++ mdig/Makefile | 4 ++++ nfq/Makefile | 2 ++ 4 files changed, 21 insertions(+) diff --git a/Makefile b/Makefile index 68145e0..00e6c20 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,19 @@ all: clean done \ done +android: clean + @mkdir -p "$(TGT)"; \ + for dir in $(DIRS); do \ + find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \ + $(MAKE) -C "$$dir" android || exit; \ + for exe in "$$dir/"*; do \ + if [ -f "$$exe" ] && [ -x "$$exe" ]; then \ + mv -f "$$exe" "${TGT}" ; \ + ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \ + fi \ + done \ + done + bsd: clean @mkdir -p "$(TGT)"; \ for dir in $(DIRS); do \ diff --git a/ip2net/Makefile b/ip2net/Makefile index dedffbe..b144893 100644 --- a/ip2net/Makefile +++ b/ip2net/Makefile @@ -11,6 +11,8 @@ all: ip2net ip2net: $(SRC_FILES) $(CC) -s $(CFLAGS) -o ip2net $(SRC_FILES) $(LDFLAGS) $(LIBS) +android: ip2net + bsd: $(SRC_FILES) $(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o ip2net $(SRC_FILES) $(LDFLAGS) $(LIBS) diff --git a/mdig/Makefile b/mdig/Makefile index 972195d..ae2137a 100644 --- a/mdig/Makefile +++ b/mdig/Makefile @@ -3,6 +3,7 @@ CFLAGS += -std=gnu99 -Os CFLAGS_BSD = -Wno-address-of-packed-member CFLAGS_WIN = -static LIBS = -lpthread +LIBS_ANDROID = LIBS_WIN = -lws2_32 SRC_FILES = *.c @@ -11,6 +12,9 @@ all: mdig mdig: $(SRC_FILES) $(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LDFLAGS) $(LIBS) +android: $(SRC_FILES) + $(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LDFLAGS) $(LIBS_ANDROID) + bsd: $(SRC_FILES) $(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o mdig $(SRC_FILES) $(LDFLAGS) $(LIBS) diff --git a/nfq/Makefile b/nfq/Makefile index 3bdfd25..92adf99 100644 --- a/nfq/Makefile +++ b/nfq/Makefile @@ -17,6 +17,8 @@ all: nfqws nfqws: $(SRC_FILES) $(CC) -s $(CFLAGS) -o nfqws $(SRC_FILES) $(LDFLAGS) $(LIBS_LINUX) +android: nfqws + bsd: $(SRC_FILES) $(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o dvtws $(SRC_FILES) $(LDFLAGS) $(LIBS_BSD) From a8432a3caa23214bae01470ada852df858b4805f Mon Sep 17 00:00:00 2001 From: spvkgn Date: Sun, 17 Nov 2024 11:00:02 +0500 Subject: [PATCH 10/11] github: minor changes --- .github/workflows/build.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b798237..bac611b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -87,15 +87,14 @@ jobs: export LDFLAGS="-Os" # netfilter libs - git clone --depth 1 -b libmnl-1.0.5 git://git.netfilter.org/libmnl - git clone --depth 1 -b libnfnetlink-1.0.2 git://git.netfilter.org/libnfnetlink - git clone --depth 1 -b libnetfilter_queue-1.0.5 git://git.netfilter.org/libnetfilter_queue + wget -qO- https://www.netfilter.org/pub/libnfnetlink/libnfnetlink-1.0.2.tar.bz2 | tar -xj + wget -qO- https://www.netfilter.org/pub/libmnl/libmnl-1.0.5.tar.bz2 | tar -xj + wget -qO- https://www.netfilter.org/pub/libnetfilter_queue/libnetfilter_queue-1.0.5.tar.bz2 | tar -xj for i in libmnl libnfnetlink libnetfilter_queue ; do ( - cd $i - ./autogen.sh && \ - ./configure --prefix= --host=$TARGET --enable-static --disable-shared && \ + cd $i-* + ./configure --prefix= --host=$TARGET --enable-static --disable-shared --disable-dependency-tracking make install -j$(nproc) DESTDIR=$DEPS_DIR ) sed -i "s|^prefix=.*|prefix=$DEPS_DIR|g" $DEPS_DIR/lib/pkgconfig/$i.pc @@ -106,7 +105,7 @@ jobs: xargs -I{} wget -qO- https://github.com/madler/zlib/archive/refs/tags/{}.tar.gz | tar -xz ( cd zlib-* - ./configure --prefix= --static && \ + ./configure --prefix= --static make install -j$(nproc) DESTDIR=$DEPS_DIR ) From 30443ed31d0b3dbdd9812869c563fb7dc300e08a Mon Sep 17 00:00:00 2001 From: spvkgn Date: Sun, 17 Nov 2024 10:21:28 +0500 Subject: [PATCH 11/11] github: build for Android --- .github/workflows/build.yml | 98 ++++++++++++++++--- .../libnetfilter_queue-android.patch | 41 ++++++++ 2 files changed, 126 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/libnetfilter_queue-android.patch diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bac611b..b9a2d90 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -281,9 +281,77 @@ jobs: path: zapret-*.zip if-no-files-found: error + build-android: + name: Android ${{ matrix.abi }} + runs-on: ubuntu-latest + strategy: + matrix: + include: + - abi: armeabi-v7a + target: armv7a-linux-androideabi + - abi: arm64-v8a + target: aarch64-linux-android + - abi: x86 + target: i686-linux-android + - abi: x86_64 + target: x86_64-linux-android + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + path: zapret + + - name: Build + env: + ABI: ${{ matrix.abi }} + TARGET: ${{ matrix.target }} + run: | + DEPS_DIR=$GITHUB_WORKSPACE/deps + export TOOLCHAIN=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64 + export API=21 + export CC="$TOOLCHAIN/bin/clang --target=$TARGET$API" + export AR=$TOOLCHAIN/bin/llvm-ar + export AS=$CC + export LD=$TOOLCHAIN/bin/ld + export RANLIB=$TOOLCHAIN/bin/llvm-ranlib + export STRIP=$TOOLCHAIN/bin/llvm-strip + export PKG_CONFIG_PATH=$DEPS_DIR/lib/pkgconfig + + # optimize for size + export CFLAGS="-Os -flto=auto" + export LDFLAGS="-Os" + + # netfilter libs + wget -qO- https://www.netfilter.org/pub/libnfnetlink/libnfnetlink-1.0.2.tar.bz2 | tar -xj + wget -qO- https://www.netfilter.org/pub/libmnl/libmnl-1.0.5.tar.bz2 | tar -xj + wget -qO- https://www.netfilter.org/pub/libnetfilter_queue/libnetfilter_queue-1.0.5.tar.bz2 | tar -xj + patch -p1 -d libnetfilter_queue-* -i ../zapret/.github/workflows/libnetfilter_queue-android.patch + + for i in libmnl libnfnetlink libnetfilter_queue ; do + ( + cd $i-* + CFLAGS="$CFLAGS -Wno-implicit-function-declaration" \ + ./configure --prefix= --host=$TARGET --enable-static --disable-shared --disable-dependency-tracking + make install -j$(nproc) DESTDIR=$DEPS_DIR + ) + sed -i "s|^prefix=.*|prefix=$DEPS_DIR|g" $DEPS_DIR/lib/pkgconfig/$i.pc + done + + # zapret + CFLAGS="$CFLAGS -I$DEPS_DIR/include" LDFLAGS="$LDFLAGS -L$DEPS_DIR/lib" \ + make -C zapret android -j$(nproc) + zip zapret-android-$ABI.zip -j zapret/binaries/my/* + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: zapret-android-${{ matrix.abi }} + path: zapret-*.zip + if-no-files-found: error + release: if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') - needs: [ build-linux, build-windows, build-macos, build-freebsd ] + needs: [ build-linux, build-windows, build-macos, build-freebsd, build-android ] permissions: contents: write runs-on: ubuntu-latest @@ -342,18 +410,22 @@ jobs: if [ -d $dir ]; then echo "Processing $dir" case $dir in - *-freebsd-x86_64 ) run_dir freebsd-x64 ;; - *-linux-arm ) run_dir arm ;; - *-linux-arm64 ) run_dir aarch64 ;; - *-linux-mips64 ) run_dir mips64r2-msb ;; - *-linux-mipselsf ) run_dir mips32r1-lsb ;; - *-linux-mipssf ) run_dir mips32r1-msb ;; - *-linux-ppc ) run_dir ppc ;; - *-linux-x86 ) run_dir x86 ;; - *-linux-x86_64 ) run_dir x86_64 ;; - *-mac-x64 ) run_dir mac64 ;; - *-win-x86 ) run_dir win32 ;; - *-win-x86_64 ) run_dir win64 ;; + *-android-arm64-v8a ) run_dir android-aarch64 ;; + *-android-armeabi-v7a ) run_dir android-arm ;; + *-android-x86 ) run_dir android-x86 ;; + *-android-x86_64 ) run_dir android-x86_64 ;; + *-freebsd-x86_64 ) run_dir freebsd-x64 ;; + *-linux-arm ) run_dir arm ;; + *-linux-arm64 ) run_dir aarch64 ;; + *-linux-mips64 ) run_dir mips64r2-msb ;; + *-linux-mipselsf ) run_dir mips32r1-lsb ;; + *-linux-mipssf ) run_dir mips32r1-msb ;; + *-linux-ppc ) run_dir ppc ;; + *-linux-x86 ) run_dir x86 ;; + *-linux-x86_64 ) run_dir x86_64 ;; + *-mac-x64 ) run_dir mac64 ;; + *-win-x86 ) run_dir win32 ;; + *-win-x86_64 ) run_dir win64 ;; esac fi done diff --git a/.github/workflows/libnetfilter_queue-android.patch b/.github/workflows/libnetfilter_queue-android.patch new file mode 100644 index 0000000..a0ce64b --- /dev/null +++ b/.github/workflows/libnetfilter_queue-android.patch @@ -0,0 +1,41 @@ +--- a/src/extra/pktbuff.c ++++ b/src/extra/pktbuff.c +@@ -14,7 +14,7 @@ + #include /* for memcpy */ + #include + +-#include ++#include + #include + #include + +--- a/src/nlmsg.c ++++ b/src/nlmsg.c +@@ -21,7 +21,7 @@ + + #include + +-#include ++// #include + + #include "internal.h" + +--- a/src/extra/tcp.c ++++ b/src/extra/tcp.c +@@ -139,12 +139,16 @@ void nfq_tcp_compute_checksum_ipv6(struc + * (union is compatible to any of its members) + * This means this part of the code is -fstrict-aliasing safe now. + */ ++#ifndef __ANDROID__ + union tcp_word_hdr { + struct tcphdr hdr; + uint32_t words[5]; + }; ++#endif + ++#ifndef tcp_flag_word + #define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words[3]) ++#endif + + /** + * nfq_pkt_snprintf_tcp_hdr - print tcp header into one buffer in a humnan