From 256c2d7e50de0f91fdec8439a08c8e22c3b3b115 Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 18 Nov 2024 16:28:22 +0300 Subject: [PATCH 1/9] doc works --- docs/bsd.md | 14 +- docs/bsdfw.txt | 2 +- docs/changes.txt | 25 ++ docs/readme.en.md | 2 +- docs/readme.md | 596 ++++++++++++++++++++++++---------------------- 5 files changed, 349 insertions(+), 290 deletions(-) diff --git a/docs/bsd.md b/docs/bsd.md index ad57b72..56304d7 100644 --- a/docs/bsd.md +++ b/docs/bsd.md @@ -143,7 +143,7 @@ $ ipfw -q -f flush zapret, добавив в параметры `--daemon`. Например так: ```sh $ pkill ^dvtws$ -$ /opt/zapret/nfq/dvtws --port=989 --daemon --dpi-desync=split2 +$ /opt/zapret/nfq/dvtws --port=989 --daemon --dpi-desync=multisplit --dpi-desync-split-pos=2 ``` Для перезапуска фаервола и демонов достаточно будет сделать: @@ -209,7 +209,7 @@ $ ipfw delete 100 $ ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted xmit em0 # required for autottl mode only $ ipfw add 100 divert 989 tcp from any 80,443 to any tcpflags syn,ack in not diverted recv em0 -$ /opt/zapret/nfq/dvtws --port=989 --dpi-desync=split2 +$ /opt/zapret/nfq/dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 ``` #### Трафик только на таблицу zapret, за исключением таблицы nozapret @@ -220,7 +220,7 @@ $ ipfw add 100 allow tcp from me to table\(nozapret\) 80,443 $ ipfw add 100 divert 989 tcp from any to table\(zapret\) 80,443 out not diverted not sockarg xmit em0 # required for autottl mode only $ ipfw add 100 divert 989 tcp from table\(zapret\) 80,443 to any tcpflags syn,ack in not diverted not sockarg recv em0 -$ /opt/zapret/nfq/dvtws --port=989 --dpi-desync=split2 +$ /opt/zapret/nfq/dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 ``` @@ -317,7 +317,7 @@ sysctl net.inet6.ip6.pfil.inbound=ipfw,pf ipfw delete 100 ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted xmit em0 pkill ^dvtws$ -dvtws --daemon --port 989 --dpi-desync=split2 +dvtws --daemon --port 989 --dpi-desync=multisplit --dpi-desync-split-pos=2 # required for newer pfsense versions (2.6.0 tested) to return ipfw to functional state pfctl -d ; pfctl -e @@ -357,7 +357,7 @@ rdr pass on em1 inet6 proto tcp to port {80,443} -> fe80::20c:29ff:5ae3:4821 por ```sh $ pfctl -a zapret -f /etc/zapret.anchor $ pkill ^tpws$ -$ tpws --daemon --port=988 --enable-pf --bind-addr=127.0.0.1 --bind-iface6=em1 --bind-linklocal=force --split-http-req=method --split-pos=2 +$ tpws --daemon --port=988 --enable-pf --bind-addr=127.0.0.1 --bind-iface6=em1 --bind-linklocal=force --split-pos=2 ``` 4. После перезагрузки проверьте, что правила создались: @@ -424,7 +424,7 @@ pass out quick on em0 proto tcp to port {80,443} divert-packet port 989 no sta ```sh $ pfctl -f /etc/pf.conf -$ ./dvtws --port=989 --dpi-desync=split2 +$ ./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 ``` #### Трафик только на таблицу zapret, за исключением таблицы nozapret @@ -456,7 +456,7 @@ pass out quick on em0 inet6 proto tcp to port {80,443} divert-p ```sh $ pfctl -f /etc/pf.conf -$ ./dvtws --port=989 --dpi-desync=split2 +$ ./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 ``` diff --git a/docs/bsdfw.txt b/docs/bsdfw.txt index 2575c69..8202322 100644 --- a/docs/bsdfw.txt +++ b/docs/bsdfw.txt @@ -70,7 +70,7 @@ pass in quick on em0 proto tcp from port {80,443} flags SA/SA divert-packet por pass in quick on em0 proto tcp from port {80,443} no state pass out quick on em0 proto tcp to port {80,443} divert-packet port 989 no state pfctl -f /etc/pf.conf -./dvtws --port=989 --dpi-desync=split2 +./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 ; dvtws with table limitations : to zapret,zapret6 but not to nozapret,nozapret6 ; reload tables : pfctl -f /etc/pf.conf diff --git a/docs/changes.txt b/docs/changes.txt index 64d5f57..8721ddf 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -363,3 +363,28 @@ nfqws,tpws: use alternate $ sign for $ repo: binaries removed from repo. git actions binaries build in releases. uninstall_easy.sh: offer to remove dependencies in openwrt install_easy.sh: allow to download lists in autohostlist filter mode + +v69: + +nfqws, tpws: multisplit/multidisorder support. +nfqws: name change split->fakedsplit, disorder->fakeddisorder. compat : old names are synonyms +nfqws: --dpi-desync-split-http-req, --dpi-desync-split-tls deprecated. compat : these parameters add split point to multisplit. +nfqws: --dpi-desync=split2|disorder2 deprecated. compat: they are now synonyms for multisplit/multidisorder +nfqws: cancel seqovl if MTU is exceeded (linux only). cancel seqovl for disorder if seqovl>=first_part_size. +nfqws: fixed splits in multiple TLS segments. +tpws: --split-tls,--split-tls deprecated. compat : these parameters add split point to multisplit. +tpws: --tlsrec now takes pos markers. compat : old names are converted to pos markers +tpws: --tlsrec-pos deprecated. compat : sets absolute pos marker +nfqws,tpws: chown autohostlist, autohostlist debug log and debug log files after options parse +nfqws,tpws: set EXEDIR env var to use in @config (won't work for stadalone winws without /bin/sh) +dvtws: set random/increasing ip_id value in generated packets +mdig: fixed parsing of DNS reply in windows (stdin is opened as text, not binary) +tpws: support compile for android NDK api level >= 21 (Android 5.0) +repo: build for android NDK api level 21 (Android 5.0) +install_easy: support for APK package manager in openwrt +blockcheck: removed ignore CA question +blockcheck: removed IGNORE_CA, CURL_VERBOSE +blockcheck: added CURL_OPT +blockcheck: new strategies support +blockcheck: test sequence rework +blockcheck: view all working strategies in summary diff --git a/docs/readme.en.md b/docs/readme.en.md index 612ea9e..4890200 100644 --- a/docs/readme.en.md +++ b/docs/readme.en.md @@ -1,4 +1,4 @@ -# zapret v.68 +# zapret v.69 # SCAMMER WARNING diff --git a/docs/readme.md b/docs/readme.md index 0edd17c..5042fa7 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,4 +1,4 @@ -# zapret v.68 +# zapret v.69 # ВНИМАНИЕ, остерегайтесь мошенников @@ -22,7 +22,13 @@ zapret является свободным и open source. - [Когда это работать не будет](#когда-это-работать-не-будет) - [nfqws](#nfqws) - [АТАКА ДЕСИНХРОНИЗАЦИИ DPI](#атака-десинхронизации-dpi) + - [ФЕЙКИ](#фейки) + - [TCP СЕГМЕНТАЦИЯ](#tcp-сегментация) + - [ПЕРЕКРЫТИЕ SEQUENCE NUMBERS](#перекрытие-sequence-numbers) + - [СПЕЦИФИЧЕСКИЕ РЕЖИМЫ IPV6](#специфические-режимы-ipv6) - [КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ](#комбинирование-методов-десинхронизации) + - [РЕАКЦИЯ DPI НА ОТВЕТ СЕРВЕРА](#реакция-dpi-на-ответ-сервера) + - [IPTABLES ДЛЯ NFQWS](#iptables-для-nfqws) - [РЕЖИМ SYNACK](#режим-synack) - [РЕЖИМ SYNDATA](#режим-syndata) - [ВИРТУАЛЬНЫЕ МАШИНЫ](#виртуальные-машины) @@ -32,7 +38,12 @@ zapret является свободным и open source. - [IP ФРАГМЕНТАЦИЯ](#ip-фрагментация) - [МНОЖЕСТВЕННЫЕ СТРАТЕГИИ](#множественные-стратегии) - [tpws](#tpws) + - [TCP СЕГМЕНТАЦИЯ В TPWS](#tcp-сегментация-в-tpws) + - [TLSREC](#tlsrec) + - [MSS](#mss) + - [ДРУГИЕ ПАРАМЕТРЫ ДУРЕНИЯ](#другие-параметры-дурения) - [МНОЖЕСТВЕННЫЕ СТРАТЕГИИ](#множественные-стратегии-1) + - [СЛУЖЕБНЫЕ ПАРАМЕТРЫ](#служебные-параметры) - [Способы получения списка заблокированных IP](#способы-получения-списка-заблокированных-ip) - [ip2net](#ip2net) - [mdig](#mdig) @@ -265,7 +276,7 @@ dvtws, собираемый из тех же исходников (см. [док --hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета --hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase --domcase ; домен после Host: сделать таким : TeSt.cOm ---dpi-desync=[,][,,][, ; бит fwmark для пометки десинхронизирующих пакетов, чтобы они повторно не падали в очередь. default = 0x40000000 --dpi-desync-ttl= ; установить ttl для десинхронизирующих пакетов --dpi-desync-ttl6= ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение ttl @@ -274,10 +285,8 @@ dvtws, собираемый из тех же исходников (см. [док --dpi-desync-fooling= ; дополнительные методики как сделать, чтобы фейковый пакет не дошел до сервера. none md5sig badseq badsum datanoack hopbyhop hopbyhop2 --dpi-desync-repeats= ; посылать каждый генерируемый в nfqws пакет N раз (не влияет на остальные пакеты) --dpi-desync-skip-nosni=0|1 ; 1(default)=не применять dpi desync для запросов без hostname в SNI, в частности для ESNI ---dpi-desync-split-pos=<1..1500> ; (только для split*, disorder*) разбивать пакет на указанной позиции ---dpi-desync-split-http-req=method|host ; разбивка http request на указанном логическом месте ---dpi-desync-split-tls=sni|sniext ; разбивка tls client hello на указанном логическом месте ---dpi-desync-split-seqovl= ; использовать sequence overlap перед первым отсылаемым оригинальным tcp сегментом +--dpi-desync-split-pos=N|-N|marker+N|marker-N ; список через запятую маркеров для tcp сегментации в режимах split и disorder +--dpi-desync-split-seqovl=N|-N|marker+N|marker-N ; единичный маркер, определяющий величину перекрытия sequence в режимах split и disorder. для split поддерживается только положительное число. --dpi-desync-split-seqovl-pattern=|0xHEX ; чем заполнять фейковую часть overlap --dpi-desync-badseq-increment= ; инкремент sequence number для badseq. по умолчанию -10000 --dpi-desync-badack-increment= ; инкремент ack sequence number для badseq. по умолчанию -66000 @@ -315,12 +324,6 @@ dvtws, собираемый из тех же исходников (см. [док --ipset-exclude= ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая. ``` -Параметры манипуляции могут сочетаться в любых комбинациях. - -> [!TIP] -> **ЗАМЕЧАНИЕ.** Параметр `--wsize` считается устаревшим и более не поддерживается в скриптах. Функции сплита выполняются в -> рамках атаки десинхронизации. Это быстрее и избавляет от целого ряда недостатков wsize. - `--debug` позволяет выводить подробный лог действий на консоль, в syslog или в файл. Может быть важен порядок следования опций. `--debug` лучше всего указывать в самом начале. Опции анализируются последовательно. Если ошибка будет при проверке опции, а до анализа `--debug` еще дело не дошло, то сообщения не будут выведены в файл или syslog. При @@ -332,18 +335,24 @@ dvtws, собираемый из тех же исходников (см. [док ### АТАКА ДЕСИНХРОНИЗАЦИИ DPI -Суть ее в следующем. После выполнения tcp 3-way handshake идет первый пакет с данными от клиента. Там обычно `GET / ...` -или TLS ClientHello. Мы дропаем этот пакет, заменяя чем-то другим. Это может быть поддельная версия с безобидным, но -валидным запросом http или https (вариант `fake`), пакет сброса соединения (варианты `rst`, `rstack`), разбитый на части -оригинальный пакет с перепутанным порядком следования сегментов + обрамление первого сегмента фейками (`disorder`), то -же самое без перепутывания порядка сегментов (`split`). fakeknown отличается от fake тем, что применяется только к -распознанному протоколу. В литературе такие атаки еще называют **TCB desynchronization** и **TCB teardown**. Надо, чтобы -фейковые пакеты дошли до DPI, но не дошли до сервера. На вооружении есть следующие возможности : установить низкий TTL, -посылать пакет с инвалидной чексуммой, добавлять tcp option **MD5 signature**, испортить sequence numbers. Все они не -лишены недостатков. +Суть ее в следующем. Берется оригинальный запрос, модифицируется, добавляется поддельная информация (фейки) +таким образом, чтобы ОС сервера передала серверному процессу оригинальный запрос в неизменном виде, а DPI увидел другое. +То, что он блокировать не станет. Сервер видит одно, DPI - другое. DPI не понимает, что передается запрещенный запрос и не блокирует его. -* `md5sig` работает не на всех серверах. Пакеты с md5 обычно отбрасывают только linux. -* `badsum` не сработает, если ваше устройство за NAT, который не пропускает пакеты с инвалидной суммой. Наиболее +Есть арсенал возможностей, чтобы достичь такого результата. +Это может быть передача фейк пакетов, чтобы они дошли до DPI, но не дошли до сервера. Может использоваться фрагментация на уровне TCP (сегментация) или на уровне IP. +Есть атаки, основанные на игре с tcp sequence numbers или с перепутыванием порядка следования tcp сегментов. +Методы могут сочетаться в различных вариантах. + +### ФЕЙКИ + +Фейки - это отдельные сгенерированные nfqws пакеты, несущие ложную информацию для DPI. +Они либо не должны дойти до сервера, либо могут дойти, но должны быть им отброшены. +Иначе получается слом tcp соединения или нарушение целостности передаваемого потока, что гарантированно приводит к поломке ресурса. +Есть ряд методов для решения этой задачи. + +* `md5sig` добавляет TCP опцию **MD5 signature**. Работает не на всех серверах. Пакеты с md5 обычно отбрасывают только linux. +* `badsum` портит контрольную сумму TCP. Не сработает, если ваше устройство за NAT, который не пропускает пакеты с инвалидной суммой. Наиболее распространенная настройка NAT роутера в Linux их не пропускает. На Linux построено большинство домашних роутеров. Непропускание обеспечивается так : настройка ядра sysctl по умолчанию `net.netfilter.nf_conntrack_checksum=1` заставляет conntrack проверять tcp и udp чексуммы входящих пакетов и @@ -359,7 +368,8 @@ dvtws, собираемый из тех же исходников (см. [док себя ведут некоторые роутеры на базе mediatek. badsum пакеты уходят с клиентской ОС, но роутером не видятся в br-lan через tcpdump. При этом если nfqws выполняется на самом роутере, обход может работать. badsum нормально уходят с внешнего интерфейса. -* Пакеты с `badseq` будут наверняка отброшены принимающим узлом, но так же и DPI, если он ориентируется на sequence +* `badseq` увеличивает TCP sequence number на определенное значение, выводя его тем самым из TCP window. + Такие пакеты будут наверняка отброшены принимающим узлом, но так же и DPI, если он ориентируется на sequence numbers. По умолчанию смещение seq выбирается -10000. Практика показала, что некоторые DPI не пропускают seq вне определенного окна. Однако, такое небольшое смещение может вызвать проблемы при существенной потоковой передаче и потере пакетов. Если вы используете `--dpi-desync-any-protocol`, может понадобится установить badseq increment @@ -383,7 +393,8 @@ dvtws, собираемый из тех же исходников (см. [док может ломать NAT и не всегда работает с iptables, если используется masquerade, даже с локальной системы (почти всегда на роутерах ipv4). На системах c iptables без masquerade и на nftables работает без ограничений. Экспериментально выяснено, что многие провайдерские NAT не отбрасывают эти пакеты, потому работает даже с внутренним провайдерским IP. - Но linux NAT оно не пройдет, так что за домашним роутером эта техника не сработает, но может сработать с него. + Но linux NAT оно не пройдет, так что за домашним роутером эта техника скорее всего не сработает, но может сработать с него. + Может сработать и через роутер, если подключение по проводу, и на роутере включено аппаратное ускорение. * `autottl`. Суть режима в автоматическом определении TTL, чтобы он почти наверняка прошел DPI и немного не дошел до сервера. Берутся базовые значения TTL 64,128,255, смотрится входящий пакет (да, требуется направить первый входящий пакет на nfqws !). Вычисляется длина пути, отнимается `delta` (1 по @@ -396,44 +407,64 @@ dvtws, собираемый из тех же исходников (см. [док Режимы дурения могут сочетаться в любых комбинациях. `--dpi-desync-fooling` берет множество значений через запятую. -Для режимов fake, rst, rstack после фейка отправляем оригинальный пакет. +### TCP СЕГМЕНТАЦИЯ -Режим disorder делит оригинальный пакет на 2 части и отправляет следующую комбинацию в указанном порядке : + * `multisplit`. нарезаем запрос на указанных в `--dpi-desync-split-pos` позициях. + * `multidisorder`. нарезаем запрос на указанных в `--dpi-desync-split-pos` позициях и отправляем в обратном порядке. + * `fakedsplit`. нарезаем запрос на 2 части, обрамляя его фейками : фейк 1-й части, 1 часть, фейк 1-й части, 2 часть + * `fakeddisorder`. нарезаем запрос на 2 части, обрамляя его фейками : 2 часть, фейк 1-й части, 1 часть, фейк 1 части. -1. 2-я часть пакета -2. поддельная 1-я часть пакета, поле данных заполнено нулями -3. 1-я часть пакета -4. поддельная 1-я часть пакета, поле данных заполнено нулями. отсылка 2-й раз. Оригинальный пакет дропается всегда. +Для определения позиций нарезки используются маркеры. -Параметр `--dpi-desync-split-pos` позволяет указать байтовую позицию, на которой происходит разбивка. По умолчанию - 2. -Если позиция больше длины пакета, позиция выбирается 1. Этой последовательностью для DPI максимально усложняется задача -реконструкции начального сообщения, по которому принимается решение о блокировке. Некоторым DPI хватит и tcp сегментов в -неправильном порядке, поддельные части сделаны для дополнительной надежности и более сложных алгоритмов реконструкции. -Режим `disorder2` отключает отправку поддельных частей. +* **Абсолютный положительный маркер** - числовое смещение внутри пакета или группы пакетов от начала. +* **Абсолютный отрицательный маркер** - числовое смещение внутри пакета или группы пакетов от следующего за концом байта. -1 указывает на последний байт. +* **Относительный маркер** - положительное или отрицательное смещение относительно логической позиции внутри пакета или группы пакетов. -Режим `split` очень похож на disorder, только нет изменения порядка следования сегментов : +Относительные позиции : -1. поддельная 1-я часть пакета, поле данных заполнено нулями -2. 1-я часть пакета -3. поддельная 1-я часть пакета, поле данных заполнено нулями. отсылка 2-й раз. -4. 2-я часть пакета Режим split2 отключает отправку поддельных частей. Он может быть использован как более быстрая - альтернатива --wsize. +* **method** - начало метода HTTP ('GET', 'POST', 'HEAD', ...). Метод обычно всегда находится на позиции 0, но поддерживается и нахождение метода после дурение методом `--methodeol` от tpws. Тогда позиция может стать 1 или 2. +* **host** - начало имени хоста в известном протоколе (http, TLS) +* **endhost** - конец имени хоста +* **sld** - начало домена 2 уровня в имени хоста +* **endsld** - конец домена 2 уровня в имени хоста +* **midsld** - середина домена 2 уровня в имени хоста +* **sniext** - начало поля данных SNI extension в TLS. Любой extension состоит из 2-байтовых полей type и length, за ними идет поле данных. -`disorder2` и `split2` не предполагают отсылку фейк пакетов, поэтому опции ttl и fooling неактуальны. +Пример списка маркеров : `100,midsld,sniext+1,endhost-2,-10`. -`seqovl` добавляет в начало первой отсылаемой части оригинального пакета (1 часть для split и 2 часть для disorder) -`seqovl` байт со смещенным в минус sequence number на величину seqovl. В случае `split2` расчет идет на то, что предыдущий -отсыл, если он был, уже попал в сокет серверного приложения, поэтому новая пришедшая часть лишь частично находится в +При разбиении пакета первым делом происходит ресолвинг маркеров - нахождение всех указанных относительных позиций и применение смещений. +Если относительная позиция отсутствует в текущем протоколе, такие позиции не применяются и отбрасываются. +Дальше происходит нормализация позиций относительно смещения текущего пакета в группе пакетов (многопакетные запросы TLS с kyber, например). +Выкидываются все позиции, выходящие за пределы текущего пакета. Оставшиеся сортируются в порядке возрастания и удаляются дубли. +В вариантах `multisplit` и `multidisorder` если не осталось ни одной позиции, разбиение не происходит. + +Варианты `fakedsplit` и `fakeddisorder` применяют только одну позицию сплита. Ее поиск среди списка `--dpi-desync-split-pos` осуществляется особым образом. +Сначала сверяются все относительные маркеры. Если среди них найден подходящий, применяется он. В противном случае сверяются все абсолютные маркеры. +Если и среди них ничего не найдено, применяется позиция 1. + +Например, можно написать `--dpi-desync-split-pos=method+2,midsld,5`. Если протокол http, разбиение будет на позиции `method+2`. +Если протокол TLS - на позиции `midsld`. Если протокол неизвестен и включено `--dpi-desync-any-protocol`, разбиение будет на позиции 5. +Чтобы все было однозначнее, можно использовать разные профили для разных протоколов и указывать только одну позицию, которая точно есть в этом протоколе. + +### ПЕРЕКРЫТИЕ SEQUENCE NUMBERS + +`seqovl` добавляет в начало одного из TCP сегментов `seqovl` байт со смещенным в минус sequence number на величину `seqovl`. +Для `split` - в начало первого сегмента, для `disorder` - в начало предпоследнего отсылаемого сегмента (второго в оригинальном порядке следования). + +В случае `split` расчет идет на то, что предыдущий отсыл, если он был, уже попал в сокет серверного приложения, поэтому новая пришедшая часть лишь частично находится в пределах текущего окна (in-window). Спереди фейковая часть отбрасывается, а оставшаяся часть содержит оригинал и начинается с начала window, поэтому попадает в сокет. Серверное приложение получает все, что реально отсылает клиент, -отбрасывая фейковую out-of-window часть. Но DPI не может этого понять, поэтому у него происходит sequence -десинхронизация. +отбрасывая фейковую out-of-window часть. Но DPI не может этого понять, поэтому у него происходит sequence десинхронизация. +Обязательно, чтобы первый сегмент вместе с `seqovl` не превысили длину MTU. Эта ситуация распознается автоматически в Linux, и `seqovl` отменяется. +В остальных системах ситуация не распознается, и это приведет к поломке соединения. Поэтому выбирайте первую позицию сплита и `seqovl` таким образом, чтобы MTU не был превышен в любом случае. +Иначе дурение может не работать или работать хаотично. -Для `disorder2` overlap идет на 2-ю часть пакета. Обязательно, чтобы `seqovl` был меньше `split_pos`, иначе -все отосланное будет передано в сокет сразу же, включая фейк, ломая протокол прикладного уровня. -При соблюдении этого условия 2-я часть пакета является полностью in-window, -поэтому серверная ОС принимает ее целиком, включая фейк. Но поскольку начальная часть данных из 1 пакета -еще не принята, то фейк и реальные данные остаются в памяти ядра, не отправляясь в серверное приложение. +Для `disorder` overlap идет на предпоследнюю отсылаемую часть пакета. +Для простоты будем считать, что разбиение идет на 2 части, шлются они в порядке "2 1" при оригинальном порядке "1 2". +Обязательно, чтобы `seqovl` был меньше позиции первого сплита, иначе все отосланное будет передано в сокет сразу же, включая фейк, ломая протокол прикладного уровня. +Такая ситуация легко обнаруживается программой, и `seqovl` отменяется. Увеличение размера пакета невозможно в принципе. +При соблюдении условия 2-я часть пакета является полностью in-window, поэтому серверная ОС принимает ее целиком, включая фейк. +Но поскольку начальная часть данных из 1 пакета еще не принята, то фейк и реальные данные остаются в памяти ядра, не отправляясь в серверное приложение. Как только приходит 1-я часть пакета, она переписывает фейковую часть в памяти ядра. Ядро получает данные из 1 и 2 части, поэтому далее идет отправка в сокет приложения. Таково поведение всех unix ОС, кроме solaris - оставлять последние принятые данные. @@ -441,7 +472,13 @@ Windows оставляет старые данные, поэтому disorder с при работе с Windows серверами. Solaris практически мертв, windows серверов очень немного. Можно использовать листы при необходимости. Метод позволяет обойтись без fooling и TTL. Фейки перемешаны с реальным данными. -`split/disorder` вместо `split2/disorder2` по-прежнему добавляют дополнительные отдельные фейки. +`fakedsplit/fakeddisorder` по-прежнему добавляют дополнительные отдельные фейки. + +`seqovl` в варианте `split` может быть только абсолютным положительным значением, поскольку применяется только в первому пакету. +В варианте `disorder` допустимо применение всех вариантов маркеров. +Они автоматически нормализуются к текущему пакету в серии. Можно сплитать на `midsld` и делать seqovl на `midsld-1`. + +### СПЕЦИФИЧЕСКИЕ РЕЖИМЫ IPV6 Режимы десинхронизации `hopbyhop`, `destopt` и `ipfrag1` (не путать с fooling !) относятся только к `ipv6` и заключается в добавлении хедера `hop-by-hop options`, `destination options` или `fragment` во все пакеты, попадающие под десинхронизацию. @@ -452,10 +489,22 @@ Windows оставляет старые данные, поэтому disorder с extension хедерам в поисках транспортного хедера. Таким образом не поймет, что это tcp или udp, и пропустит пакет без анализа. Возможно, какие-то DPI на это купятся. Может сочетаться с любыми режимами 2-й фазы, кроме варианта `ipfrag1+ipfrag2`. -Например, `hopbyhop,split2` означает разбить tcp пакет на 2 сегмента, в каждый из них добавить hop-by-hop. +Например, `hopbyhop,multisplit` означает разбить tcp пакет на несколько сегментов, в каждый из них добавить hop-by-hop. При `hopbyhop,ipfrag2` последовательность хедеров будет : `ipv6,hop-by-hop`,`fragment`,`tcp/udp`. Режим `ipfrag1` может срабатывать не всегда без специальной подготовки. См. раздел `IP фрагментация`. +### КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ + +В параметре dpi-desync можно указать до 3 режимов через запятую. + +* 0 фаза - предполагает работу на этапе установления соединения : `synack`, `syndata` `--wsize`, `--wssize`. +* 1 фаза - отсылка чего-либо до оригинального пакета данных : `fake`, `rst`, `rstack`. +* 2 фаза - отсылка в модифицированном виде оригинального пакета данных (например, `fakedsplit` или `ipfrag2`). + +Режимы требуют указания в порядке возрастания номеров фаз. + +### РЕАКЦИЯ DPI НА ОТВЕТ СЕРВЕРА + Есть DPI, которые анализируют ответы от сервера, в частности сертификат из ServerHello, где прописаны домены. Подтверждением доставки ClientHello является ACK пакет от сервера с номером ACK sequence, соответствующим длине ClientHello+1. В варианте disorder обычно приходит сперва частичное подтверждение (SACK), потом полный ACK. @@ -470,8 +519,7 @@ DPI может отстать от потока, если ClientHello его у Лучшее решение - включить на сервере поддержку TLS 1.3. В нем сертификат сервера передается в зашифрованном виде. Это рекомендация ко всем админам блокируемых сайтов. Включайте TLS 1.3. Так вы дадите больше возможностей преодолеть DPI. -Хосты извлекаются из Host: хедера обычных http запросов и из SNI в TLS ClientHello. -Субдомены учитываются автоматически. Поддерживаются листы gzip. +### IPTABLES ДЛЯ NFQWS iptables для задействования атаки на первый пакет данных : @@ -494,19 +542,10 @@ mark нужен, чтобы сгенерированный поддельный Процесс может зависнуть. Поэтому наличие фильтра по mark в ip/nf tables можно считать обязательным. Почему --connbytes 1:6 : -1 - для работы методов десинхронизации 0-й фазы и wssize -2 - иногда данные идут в 3-м пакете 3-way handshake -3 - стандартная ситуация приема одного пакета запроса -4-6 - на случай ретрансмиссии или запроса длиной в несколько пакетов (TLSClientHello с kyber, например) - -### КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ - -В параметре dpi-desync можно указать до 3 режимов через запятую. -0 фаза предполагает работу на этапе установления соединения. Может быть `synack` или `syndata`. -На 0 фазу не действует фильтр по hostlist. -Последующие режимы отрабатывают на пакетах с данными. -Режим 1-й фазы может быть `fake`, `rst`, `rstack`. Режим 2-й фазы может быть `disorder`, `disorder2`, `split`, `split2`, `ipfrag2`. -Может быть полезно, когда у провайдера стоит не один DPI. +* 1 - для работы методов десинхронизации 0-й фазы и корректной работы conntrack +* 2 - иногда данные идут в 3-м пакете 3-way handshake +* 3 - стандартная ситуация приема одного пакета запроса +* 4-6 - на случай ретрансмиссии или запроса длиной в несколько пакетов (TLSClientHello с kyber, например) ### РЕЖИМ SYNACK @@ -617,10 +656,10 @@ chrome рандомизирует фингерпринт TLS. SNI может о При любой ошибке в процессе сборки задержанные пакеты немедленно отсылаются в сеть, а десинхронизация отменяется. Есть специальная поддержка всех вариантов tcp сплита для многосегментного TLS. -Если указать позицию сплита больше длины первого пакета или использовать --dpi-desync-split-tls, -то разбивка происходит не обязательно первого пакета, а того, на который пришлась итоговая позиция. -Если, допустим, клиент послал TLS ClientHello длиной 2000, а SNI начинается с 1700, -и заданы опции fake,split2, то перед первым пакетом идет fake, затем первый пакет в оригинале, +Если указать позицию сплита больше длины первого пакета, то разбивка происходит не обязательно первого пакета, а того, +на который пришлась итоговая позиция. +Если, допустим, клиент послал TLS ClientHello длиной 2000, SNI начинается с 1700, +и заданы опции `fake,multisplit`, то перед первым пакетом идет fake, затем первый пакет в оригинале, а последний пакет разбивается на 2 сегмента. В итоге имеем фейк в начале и 3 реальных сегмента. ### ПОДДЕРЖКА UDP @@ -741,99 +780,197 @@ L7 протокол становится известен обычно посл tpws - это transparent proxy. ``` -@|$ ; читать конфигурацию из файла. опция должна быть первой. остальные опции игнорируются. +@|$ ; читать конфигурацию из файла. опция должна быть первой. остальные опции игнорируются. ---debug=0|1|2|syslog|@ ; 0,1,2 = логирование на косоль : 0=тихо, 1(default)=подробно, 2=отладка. ---debug-level=0|1|2 ; указать уровень логирования для syslog и @ ---daemon ; демонизировать прогу ---pidfile= ; сохранить PID в файл ---user= ; менять uid процесса ---uid=uid[:gid] ; менять uid процесса ---bind-addr ; на каком адресе слушать. может быть ipv4 или ipv6 адрес - ; если указан ipv6 link local, то требуется указать с какого он интерфейса : fe80::1%br-lan +--debug=0|1|2|syslog|@ ; 0,1,2 = логирование на косоль : 0=тихо, 1(default)=подробно, 2=отладка. +--debug-level=0|1|2 ; указать уровень логирования для syslog и @ +--daemon ; демонизировать прогу +--pidfile= ; сохранить PID в файл +--user= ; менять uid процесса +--uid=uid[:gid] ; менять uid процесса +--bind-addr ; на каком адресе слушать. может быть ipv4 или ipv6 адрес + ; если указан ipv6 link local, то требуется указать с какого он интерфейса : fe80::1%br-lan --bind-linklocal=no|unwanted|prefer|force - ; no : биндаться только на global ipv6 - ; unwanted (default) : предпочтительно global, если нет - LL - ; prefer : предпочтительно LL, если нет - global - ; force : биндаться только на LL ---bind-iface4= ; слушать на первом ipv4 интерфейса iface ---bind-iface6= ; слушать на первом ipv6 интерфейса iface ---bind-wait-ifup= ; ждать до N секунд получения IP адреса (если задан --bind-wait-ifup - время идет после поднятия интерфейса) + ; no : биндаться только на global ipv6 + ; unwanted (default) : предпочтительно global, если нет - LL + ; prefer : предпочтительно LL, если нет - global + ; force : биндаться только на LL +--bind-iface4= ; слушать на первом ipv4 интерфейса iface +--bind-iface6= ; слушать на первом ipv6 интерфейса iface +--bind-wait-ifup= ; ждать до N секунд получения IP адреса (если задан --bind-wait-ifup - время идет после поднятия интерфейса) --bind-wait-ip-linklocal= - ; имеет смысл только при задании --bind-wait-ip - ; --bind-linklocal=unwanted : согласиться на LL после N секунд - ; --bind-linklocal=prefer : согласиться на global address после N секунд ---bind-wait-only ; подождать все бинды и выйти. результат 0 в случае успеха, иначе не 0. ---connect-bind-addr ; с какого адреса подключаться во внешнюю сеть. может быть ipv4 или ipv6 адрес - ; если указан ipv6 link local, то требуется указать с какого он интерфейса : fe80::1%br-lan - ; опция может повторяться для v4 и v6 адресов - ; опция не отменяет правил маршрутизации ! выбор интерфейса определяется лишь правилами маршрутизации, кроме случая v6 link local. ---socks ; вместо прозрачного прокси реализовать socks4/5 proxy ---no-resolve ; запретить ресолвинг имен через socks5 ---resolve-threads ; количество потоков ресолвера ---port= ; на каком порту слушать ---maxconn= ; максимальное количество соединений от клиентов к прокси ---maxfiles= ; макс количество файловых дескрипторов (setrlimit). мин требование (X*connections+16), где X=6 в tcp proxy mode, X=4 в режиме тамперинга. - ; стоит сделать запас с коэффициентом как минимум 1.5. по умолчанию maxfiles (X*connections)*1.5+16 ---max-orphan-time= ; если вы запускаете через tpws торрент-клиент с множеством раздач, он пытается установить очень много исходящих соединений, - ; большая часть из которых отваливается по таймауту (юзера сидят за NAT, firewall, ...) - ; установление соединения в linux может длиться очень долго. локальный конец отвалился, перед этим послав блок данных, - ; tpws ждет подключения удаленного конца, чтобы отослать ему этот блок, и зависает надолго. - ; настройка позволяет сбрасывать такие подключения через N секунд, теряя блок данных. по умолчанию 5 сек. 0 означает отключить функцию - ; эта функция не действует на успешно подключенные ранее соединения + ; имеет смысл только при задании --bind-wait-ip + ; --bind-linklocal=unwanted : согласиться на LL после N секунд + ; --bind-linklocal=prefer : согласиться на global address после N секунд +--bind-wait-only ; подождать все бинды и выйти. результат 0 в случае успеха, иначе не 0. +--connect-bind-addr ; с какого адреса подключаться во внешнюю сеть. может быть ipv4 или ipv6 адрес + ; если указан ipv6 link local, то требуется указать с какого он интерфейса : fe80::1%br-lan + ; опция может повторяться для v4 и v6 адресов + ; опция не отменяет правил маршрутизации ! выбор интерфейса определяется лишь правилами маршрутизации, кроме случая v6 link local. +--socks ; вместо прозрачного прокси реализовать socks4/5 proxy +--no-resolve ; запретить ресолвинг имен через socks5 +--resolve-threads ; количество потоков ресолвера +--port= ; на каком порту слушать +--maxconn= ; максимальное количество соединений от клиентов к прокси +--maxfiles= ; макс количество файловых дескрипторов (setrlimit). мин требование (X*connections+16), где X=6 в tcp proxy mode, X=4 в режиме тамперинга. + ; стоит сделать запас с коэффициентом как минимум 1.5. по умолчанию maxfiles (X*connections)*1.5+16 +--max-orphan-time= ; если вы запускаете через tpws торрент-клиент с множеством раздач, он пытается установить очень много исходящих соединений, + ; большая часть из которых отваливается по таймауту (юзера сидят за NAT, firewall, ...) + ; установление соединения в linux может длиться очень долго. локальный конец отвалился, перед этим послав блок данных, + ; tpws ждет подключения удаленного конца, чтобы отослать ему этот блок, и зависает надолго. + ; настройка позволяет сбрасывать такие подключения через N секунд, теряя блок данных. по умолчанию 5 сек. 0 означает отключить функцию + ; эта функция не действует на успешно подключенные ранее соединения ---local-rcvbuf= ; SO_RCVBUF для соединений client-proxy ---local-sndbuf= ; SO_SNDBUF для соединений client-proxy ---remote-rcvbuf= ; SO_RCVBUF для соединений proxy-target ---remote-sndbuf= ; SO_SNDBUF для соединений proxy-target ---nosplice ; не использовать splice на linux системах ---skip-nodelay ; не устанавливать в исходящих соединения TCP_NODELAY. несовместимо со split. ---local-tcp-user-timeout= ; таймаут соединений client-proxy (по умолчанию : 10 сек, 0 = оставить системное значение) ---remote-tcp-user-timeout= ; таймаут соединений proxy-target (по умолчанию : 20 сек, 0 = оставить системное значение) +--local-rcvbuf= ; SO_RCVBUF для соединений client-proxy +--local-sndbuf= ; SO_SNDBUF для соединений client-proxy +--remote-rcvbuf= ; SO_RCVBUF для соединений proxy-target +--remote-sndbuf= ; SO_SNDBUF для соединений proxy-target +--nosplice ; не использовать splice на linux системах +--skip-nodelay ; не устанавливать в исходящих соединения TCP_NODELAY. несовместимо со split. +--local-tcp-user-timeout= ; таймаут соединений client-proxy (по умолчанию : 10 сек, 0 = оставить системное значение) +--remote-tcp-user-timeout= ; таймаут соединений proxy-target (по умолчанию : 20 сек, 0 = оставить системное значение) ---split-http-req=method|host ; способ разделения http запросов на сегменты : около метода (GET,POST) или около заголовка Host ---split-pos= ; делить все посылы на сегменты в указанной позиции. единственная опция, работающая на не-http. при указании split-http-req он имеет преимущество на http. ---split-any-protocol ; применять split-pos к любым пакетам. по умолчанию - только к http и TLS ClientHello ---disorder[=http|tls] ; путем манипуляций с сокетом вынуждает отправлять первым второй сегмент разделенного запроса ---oob[=http|tls] ; отправить байт out-of-band data (OOB) в конце первой части сплита ---oob-data=|0xHEX ; переопределить байт OOB. по умолчанию 0x00. ---hostcase ; менять регистр заголовка "Host:". по умолчанию на "host:". ---hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase ---hostdot ; добавление точки после имени хоста : "Host: kinozal.tv." ---hosttab ; добавление табуляции после имени хоста : "Host: kinozal.tv\t" ---hostnospace ; убрать пробел после "Host:" ---hostpad= ; добавить паддинг-хедеров общей длиной перед Host: ---domcase ; домен после Host: сделать таким : TeSt.cOm ---methodspace ; добавить пробел после метода : "GET /" => "GET /" ---methodeol ; добавить перевод строки перед методом : "GET /" => "\r\nGET /" ---unixeol ; конвертировать 0D0A в 0A и использовать везде 0A ---tlsrec=sni|sniext ; разбивка TLS ClientHello на 2 TLS records. режем между 1 и 2 символами hostname в SNI или между байтами длины SNI extension. Если SNI нет - отмена. ---tlsrec-pos= ; разбивка TLS ClientHello на 2 TLS records. режем на указанной позиции, если длина слишком мелкая - на позиции 1. ---mss= ; установить MSS для клиента. может заставить сервер разбивать ответы, но существенно снижает скорость ---mss-pf=[~]port1[-port2] ; применять MSS только к портам назначения, подпадающим под фильтр. ~ означает инверсию ---tamper-start=[n] ; начинать дурение только с указанной байтовой позиции или номера блока исходяшего потока (считается позиция начала принятого блока) ---tamper-cutoff=[n] ; закончить дурение на указанной байтовой позиции или номере блока исходящего потока (считается позиция начала принятого блока) ---hostlist= ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются. - ; в файле должен быть хост на каждой строке. - ; список читается при старте и хранится в памяти в виде иерархической структуры для быстрого поиска. - ; при изменении времени модификации файла он перечитывается автоматически по необходимости - ; список может быть запакован в gzip. формат автоматически распознается и разжимается - ; списков может быть множество. пустой общий лист = его отсутствие - ; хосты извлекаются из Host: хедера обычных http запросов и из SNI в TLS ClientHello. ---hostlist-exclude= ; не применять дурение к доменам из листа. может быть множество листов. схема аналогична include листам. ---hostlist-auto= ; обнаруживать автоматически блокировки и заполнять автоматический hostlist (требует перенаправления входящего трафика) ---hostlist-auto-fail-threshold= ; сколько раз нужно обнаружить ситуацию, похожую на блокировку, чтобы добавить хост в лист (по умолчанию: 3) ---hostlist-auto-fail-time= ; все эти ситуации должны быть в пределах указанного количества секунд (по умолчанию: 60) ---hostlist-auto-debug= ; лог положительных решений по autohostlist. позволяет разобраться почему там появляются хосты. ---new ; начало новой стратегии (новый профиль) ---filter-l3=ipv4|ipv6 ; фильтр версии ip для текущей стратегии ---filter-tcp=[~]port1[-port2]|* ; фильтр портов tcp для текущей стратегии. ~ означает инверсию. поддерживается список через запятую. +--split-pos=N|-N|marker+N|marker-N ; список через запятую маркеров для tcp сегментации +--split-any-protocol ; применять сегментацию к любым пакетам. по умолчанию - только к известным протоколам (http, TLS) +--disorder[=http|tls] ; путем манипуляций с сокетом вынуждает отправлять первым второй сегмент разделенного запроса +--oob[=http|tls] ; отправить байт out-of-band data (OOB) в конце первой части сплита +--oob-data=|0xHEX ; переопределить байт OOB. по умолчанию 0x00. +--hostcase ; менять регистр заголовка "Host:". по умолчанию на "host:". +--hostspell=HoST ; точное написание заголовка Host (можно "HOST" или "HoSt"). автоматом включает --hostcase +--hostdot ; добавление точки после имени хоста : "Host: kinozal.tv." +--hosttab ; добавление табуляции после имени хоста : "Host: kinozal.tv\t" +--hostnospace ; убрать пробел после "Host:" +--hostpad= ; добавить паддинг-хедеров общей длиной перед Host: +--domcase ; домен после Host: сделать таким : TeSt.cOm +--methodspace ; добавить пробел после метода : "GET /" => "GET /" +--methodeol ; добавить перевод строки перед методом : "GET /" => "\r\nGET /" +--unixeol ; конвертировать 0D0A в 0A и использовать везде 0A +--tlsrec=N|-N|marker+N|marker-N ; разбивка TLS ClientHello на 2 TLS records на указанной позиции. Минимальное смещение - 6. +--mss= ; установить MSS для клиента. может заставить сервер разбивать ответы, но существенно снижает скорость +--mss-pf=[~]port1[-port2] ; применять MSS только к портам назначения, подпадающим под фильтр. ~ означает инверсию +--tamper-start=[n] ; начинать дурение только с указанной байтовой позиции или номера блока исходяшего потока (считается позиция начала принятого блока) +--tamper-cutoff=[n] ; закончить дурение на указанной байтовой позиции или номере блока исходящего потока (считается позиция начала принятого блока) +--hostlist= ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются. + ; в файле должен быть хост на каждой строке. + ; список читается при старте и хранится в памяти в виде иерархической структуры для быстрого поиска. + ; при изменении времени модификации файла он перечитывается автоматически по необходимости + ; список может быть запакован в gzip. формат автоматически распознается и разжимается + ; списков может быть множество. пустой общий лист = его отсутствие + ; хосты извлекаются из Host: хедера обычных http запросов и из SNI в TLS ClientHello. +--hostlist-exclude= ; не применять дурение к доменам из листа. может быть множество листов. схема аналогична include листам. +--hostlist-auto= ; обнаруживать автоматически блокировки и заполнять автоматический hostlist (требует перенаправления входящего трафика) +--hostlist-auto-fail-threshold= ; сколько раз нужно обнаружить ситуацию, похожую на блокировку, чтобы добавить хост в лист (по умолчанию: 3) +--hostlist-auto-fail-time= ; все эти ситуации должны быть в пределах указанного количества секунд (по умолчанию: 60) +--hostlist-auto-debug= ; лог положительных решений по autohostlist. позволяет разобраться почему там появляются хосты. +--new ; начало новой стратегии (новый профиль) +--filter-l3=ipv4|ipv6 ; фильтр версии ip для текущей стратегии +--filter-tcp=[~]port1[-port2]|* ; фильтр портов tcp для текущей стратегии. ~ означает инверсию. поддерживается список через запятую. --filter-l7=[http|tls|quic|wireguard|dht|unknown] ; фильтр протокола L6-L7. поддерживается несколько значений через запятую. ---ipset= ; включающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая. ---ipset-exclude= ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая. +--ipset= ; включающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая. +--ipset-exclude= ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая. ``` +### TCP СЕГМЕНТАЦИЯ В TPWS + +tpws, как и nfqws, поддерживает множественную сегментацию запросов. Сплит позиции задаются в `--split-pos`. +Указываются маркеры через запятую. Описание маркеров см в разделе [nfqws](#tcp-сегментация). + +На прикладном уровне в общем случае нет гарантированного средства заставить ядро выплюнуть +блок данных, порезанным в определенном месте. ОС держит буфер отсылки (SNDBUF) у каждого сокета. +Если у сокета включена опция TCP_NODELAY и буфер пуст, то каждый send приводит к отсылке +отдельного ip пакета или группы пакетов, если блок не вмещается в один ip пакет. +Однако, если в момент send уже имеется неотосланный буфер, то ОС присоединит данные к нему, +никакой отсылки отдельным пакетом не будет. Но в этом случае и так нет никакой гарантии, +что какой-то блок сообщения пойдет в начале пакета, на что собственно и заточены DPI. +Разбиение будет производится согласно MSS, который зависит от MTU исходящего интерфейса. +Таким образом DPI, смотрящие в начало поля данных TCP пакета, будут поломаны в любом случае. +Протокол http относится к запрос-ответным протоколам. Новое сообщение посылается только тогда, +когда сервер получил запрос и полностью вернул ответ. Значит запрос фактически был не только отослан, +но и принят другой стороной, а следовательно буфер отсылки пуст, и следующие 2 send приведут +к отсылке сегментов данных разными ip пакетами. + +Таким образом tpws обеспечивает сплит только за счет раздельных вызовов send, и это обычно работает надежно, +если разбивать не на слишком много частей и не на слишком мелкие подряд следующие части. +В последнем случае Linux все же может обьединить некоторые части, что приведет к несоответствию реальной сегментации +указанным сплит позициям. Другие ОС в этом вопросе ведут себя более предсказуемо. Спонтанного обьединения замечено не было. +Поэтому не стоит злоупотреблять сплитами и в особенности мелкими соседними пакетами. + +tpws работает на уровне сокетов, поэтому длинный запрос, не вмещающийся в 1 пакет (TLS с kyber), он получает целым блоком. +На каждую сплит часть он делает отдельный вызов `send()`. Но ОС не сможет отослать данные в одном пакете, если размер превысит MTU. +В случае слишком большого сегмента ОС дополнительно его порежет на более мелкие. Результат должен быть аналогичен nfqws. + +`--disorder` заставляет слать каждый 2-й пакет с TTL=1, начиная с первого. +К серверу приходят все четные пакеты сразу. На остальные ОС делает ретрансмиссию, и они приходят потом. +Это само по себе создает дополнительную задержку (200 мс в linux для первой ретрансмиссии). +Иным способом сделать disorder в сокет варианте не представляется возможным. +Итоговый порядок для 6 сегментов получается `2 4 6 1 3 5`. + +`--oob` высылает 1 байт out-of-band data после первого сплит сегмента. `oob` в каждом сегменте сплита показал себя ненадежным. +Сервер получает oob в сокет. + +Сочетание `oob` и `disorder` возможно только в Linux. Остальные ОС не умеют с таким справляться. Флаг URG теряется при ретрансмиссиях. +Сервер получает oob в сокет. Сочетание этих параметров в ос, кроме Linux, вызывает ошибку на этапе запуска. + +### TLSREC + +`--tlsrec` позволяют внутри одного tcp сегмента разрезать TLS ClientHello на 2 TLS records. Можно использовать стандартный +механизм маркеров для задания относительных позиций. + +`--tlsrec` ломает значительное количество сайтов. Криптобиблиотеки (openssl, ...) на оконечных http серверах +без проблем принимают разделенные tls сегменты, но мидлбоксы - не всегда. К мидлбоксам можно отнести CDN +или системы ddos-защиты. Поэтому применение `--tlsrec` без ограничителей вряд ли целесообразно. +В РФ `--tlsrec` обычно не работает с TLS 1.2, потому что цензор парсит сертификат сервера из ServerHello. +Работает только с TLS 1.3, поскольку там эта информация шифруется. +Впрочем, сейчас сайтов, не поддерживающих TLS 1.3, осталось немного. + +### MSS + +`--mss` устанавливает опцию сокета TCP_MAXSEG. Клиент выдает это значение в tcp опциях SYN пакета. +Сервер в ответ в SYN,ACK выдает свой MSS. На практике сервера обычно снижают размеры отсылаемых ими пакетов, но они +все равно не вписываются в низкий MSS, указанный клиентом. Обычно чем больше указал клиент, тем больше +шлет сервер. На TLS 1.2 если сервер разбил заброс так, чтобы домен из сертификата не попал в первый пакет, +это может обмануть DPI, секущий ответ сервера. +Схема может значительно снизить скорость и сработать не на всех сайтах. +С фильтром по hostlist совместимо только в режиме socks при включенном удаленном ресолвинге хостов. +(firefox network.proxy.socks_remote_dns). Это единственный вариант, когда tpws может узнать имя хоста +еще на этапе установления соединения. +Применяя данную опцию к сайтам TLS1.3, если броузер тоже поддерживает TLS1.3, то вы делаете только хуже. +Но нет способа автоматически узнать когда надо применять, когда нет, поскольку MSS идет только в +3-way handshake еще до обмена данными, а версию TLS можно узнать только по ответу сервера, который +может привести к реакции DPI. +Использовать только когда нет ничего лучше или для отдельных ресурсов. +Для http использовать смысла нет, поэтому заводите отдельный desync profile с фильтром по порту 443. +Работает только на Linux, не работает на BSD и MacOS. + +### ДРУГИЕ ПАРАМЕТРЫ ДУРЕНИЯ + +Параметр `--hostpad=` добавляет паддинг-хедеров перед `Host:` на указанное количество байтов. +Если размер `` слишком большой, то идет разбивка на разные хедеры по 2K. +Общий буфер приема http запроса - 64K, больший паддинг не поддерживается, да и http сервера +такое уже не принимают. +Полезно против DPI, выполняющих реассемблинг TCP с ограниченным буфером. +Если техника работает, то после некоторого количества bytes http запрос начнет проходить до сайта. +Если при этом критический размер padding около MTU, значит скорее всего DPI не выполняет реассемблинг пакетов, и лучше будет использовать обычные опции TCP сегментации. +Если все же реассемблинг выполняется, то критический размер будет около размера буфера DPI. Он может быть 4K или 8K, возможны и другие значения. + +### МНОЖЕСТВЕННЫЕ СТРАТЕГИИ + +Работают аналогично `nfqws`, кроме некоторых моментов. +Нет параметра `--filter-udp`, поскольку `tpws` udp не поддерживает. +Методы нулевой фазы (`--mss`) могут работать по хостлисту в одном единственном случае: +если используется режим socks и удаленный ресолвинг хостов через прокси. +То есть работоспособность вашей настройки в одном и том же режиме может зависеть от того, +применяет ли клиент удаленный ресолвинг. Это может быть неочевидно. +В одной программе работает, в другой - нет. +Если вы используете профиль с хостлистом , и вам нужен mss, укажите mss в профиле с хостлистом, +создайте еще один профиль без хостлиста, если его еще нет, и в нем еще раз укажите mss. +Тогда при любом раскладе будет выполняться mss. +Используйте `curl --socks5` и `curl --socks5-hostname` для проверки вашей стратегии. +Смотрите вывод `--debug`, чтобы убедиться в правильности настроек. + +### СЛУЖЕБНЫЕ ПАРАМЕТРЫ + `--debug` позволяет выводить подробный лог действий на консоль, в syslog или в файл. Может быть важен порядок следования опций. `--debug` лучше всего указывать в самом начале. Опции анализируются последовательно. Если ошибка будет при проверке опции, а до анализа `--debug` еще дело не дошло, @@ -850,28 +987,6 @@ tpws - это transparent proxy. Вместо удаления лучше использовать truncate. В шелле это можно сделать через команду ": >filename" -Параметры манипуляции могут сочетаться в любых комбинациях. - -В случае http запроса `split-http-req` имеет преимущество над split-pos. -split-pos по умолчанию работает только на http и TLS ClientHello. -Чтобы он работал на любых пакетах, укажите `--split-any-protocol`. - -На прикладном уровне в общем случае нет гарантированного средства заставить ядро выплюнуть -блок данных, порезанным в определенном месте. ОС держит буфер отсылки (SNDBUF) у каждого сокета. -Если у сокета включена опция TCP_NODELAY и буфер пуст, то каждый send приводит к отсылке -отдельного ip пакета или группы пакетов, если блок не вмещается в один ip пакет. -Однако, если в момент send уже имеется неотосланный буфер, то ОС присоединит данные к нему, -никакой отсылки отдельным пакетом не будет. Но в этом случае и так нет никакой гарантии, -что какой-то блок сообщения пойдет в начале пакета, на что собственно и заточены DPI. -Разбиение будет производится согласно MSS, который зависит от MTU исходящего интерфейса. -Таким образом DPI, смотрящие в начало поля данных TCP пакета, будут поломаны в любом случае. -Протокол http относится к запрос-ответным протоколам. Новое сообщение посылается только тогда, -когда сервер получил запрос и полностью вернул ответ. Значит запрос фактически был не только отослан, -но и принят другой стороной, а следовательно буфер отсылки пуст, и следующие 2 send приведут -к отсылке сегментов данных разными ip пакетами. -Резюме : tpws гарантирует сплит только за счет раздельных вызовов send, что на практике -вполне достаточно для протоколов http(s). - tpws может биндаться на множество интерфейсов и IP адресов (до 32 шт). Порт всегда только один. Параметры `--bind-iface*` и `--bind-addr` создают новый бинд. @@ -895,19 +1010,17 @@ tpws может биндаться на множество интерфейсо Параметры rcvbuf и sndbuf позволяют установить setsockopt SO_RCVBUF SO_SNDBUF для локального и удаленного соединения. -Если не указан ни один из параметров модификации содержимого, tpws работает в режиме `tcp proxy mode`. -Он отличается тем, что в оба конца применяется splice для переброски данных из одного сокета в другой -без копирования в память процесса. Практически - это то же самое, но может быть чуть побыстрее. -TCP проксирование может быть полезно для обхода блокировок, когда DPI спотыкается на экзотических -хедерах IP или TCP. Вы вряд ли сможете поправить хедеры, исходящие от айфончиков и гаджетиков, -но на linux сможете влиять на них в какой-то степени через `sysctl`. -Когда соединение проходит через tpws, фактически прокси-сервер сам устанавливает подключение к удаленному -узлу от своего имени, и на это распространяются настройки системы, на которой работает прокси. -tpws можно использовать на мобильном устройстве, раздающем интернет на тарифе сотового оператора, -где раздача запрещена, в socks режиме даже без рута. Соединения от tpws неотличимы от соединений -с самого раздающего устройства. Отличить можно только по содержанию (типа обновлений windows). -Заодно можно и обойти блокировки. 2 зайца одним выстрелом. -Более подробную информацию по вопросу обхода ограничений операторов гуглите на 4pda.ru. +`--skip-nodelay` может быть полезен, когда tpws используется без дурения, чтобы привести MTU к MTU системы, на которой работает tpws. +Это может быть полезно для скрытия факта использования VPN. Пониженный MTU - 1 из способов обнаружения +подозрительного подключения. С tcp proxy ваши соединения неотличимы от тех, что сделал бы сам шлюз. + +`--local-tcp-user-timeout` и `--remote-tcp-user-timeout` устанавливают значение таймаута в секундах +для соединений клиент-прокси и прокси-сервер. Этот таймаут соответствует опции сокета linux +TCP_USER_TIMEOUT. Под таймаутом подразумевается время, в течение которого буферизированные данные +не переданы или на переданные данные не получено подтверждение (ACK) от другой стороны. +Этот таймаут никак не касается времени отсутствия какой-либо передачи через сокет лишь потому, +что данных для передачи нет. Полезно для сокращения время закрытия подвисших соединений. +Поддерживается только на Linux и MacOS. Режим `--socks` не требует повышенных привилегий (кроме бинда на привилегированные порты 1..1023). Поддерживаются версии socks 4 и 5 без авторизации. Версия протокола распознается автоматически. @@ -921,82 +1034,6 @@ tpws поддерживает эту возможность асинхронно Если задан параметр `--no-resolve`, то подключения по именам хостов запрещаются, а пул ресолверов не создается. Тем самым экономятся ресурсы. -Параметр `--hostpad=` добавляет паддинг-хедеров перед Host: на указанное количество байтов. -Если размер `` слишком большой, то идет разбивка на разные хедеры по 2K. -Общий буфер приема http запроса - 64K, больший паддинг не поддерживается, да и http сервера -такое уже не принимают. -Полезно против DPI, выполняющих реассемблинг TCP с ограниченным буфером. -Если техника работает, то после некоторого количества bytes http запрос начнет проходить до сайта. -Если при этом критический размер padding около MTU, значит скорее всего DPI не выполняет реассемблинг пакетов, и лучше будет использовать обычные опции `--split-…` -Если все же реассемблинг выполняется, то критический размер будет около размера буфера DPI. Он может быть 4K или 8K, возможны и другие значения. - -`--disorder` - это попытка симулировать режим `disorder2 nfqws`, используя особенности ОС по реализации stream сокетов. -Однако, в отличие от nfqws, здесь не требуются повышенные привилегии. -Реализовано это следующим образом. У сокета есть возможность выставить TTL. Все пакеты будут отправляться с ним. -Перед отправкой первого сегмента ставим TTL=1. Пакет будет дропнут на первом же роутере, он не дойдет ни до DPI, ни до сервера. -Затем возвращаем TTL в значение по умолчанию. ОС отсылает второй сегмент, и он уже доходит до сервера. -Сервер возвращает SACK, потому что не получил первый кусок, и ОС его отправляет повторно, но здесь уже мы ничего не делаем. -Этот режим работает как ожидается на Linux и MacOS. Однако, на FreeBSD и OpenBSD он работает не так хорошо. -Ядро этих ОС отсылает ретрансмиссию в виде полного пакета. Потому выходит, что до сервера идет сначала второй кусок, -а потом полный запрос без сплита. На него может отреагировать DPI штатным образом. -`--disorder` является дополнительным флагом к любому сплиту. Сам по себе он не делает ничего. - -`--tlsrec` и `--tlsrec-pos` позволяют внутри одного tcp сегмента разрезать TLS ClientHello на 2 TLS records. -`--tlsrec=sni` режет между 1 и 2 символами hostname в SNI, делая невозможным бинарный поиск паттерна без анализа -структуры данных. В случае отсутствия SNI разбиение отменяется. -`--tlsrec-pos` режет на указанной позиции. Если длина блока данных TLS меньше указанной позиции, режем на позиции 1. -Параметр сочетается с `--split-pos`. В этом случае происходит сначала разделение на уровне TLS record layer, потом на уровне TCP. -Самая изощрённая атака `--tlsrec`, `--split-pos` и `--disorder` вместе. -`--tlsrec` ломает значительное количество сайтов. Криптобиблиотеки (openssl, ...) на оконечных http серверах -без проблем принимают разделенные tls сегменты, но мидлбоксы - не всегда. К мидлбоксам можно отнести CDN -или системы ddos-защиты. Поэтому применение `--tlsrec` без ограничителей вряд ли целесообразно. -В РФ `--tlsrec` обычно не работает с TLS 1.2, потому что цензор парсит сертификат сервера из ServerHello. -Работает только с TLS 1.3, поскольку там эта информация шифруется. -Впрочем, сейчас сайтов, не поддерживающих TLS 1.3, осталось немного. - -`--mss` устанавливает опцию сокета TCP_MAXSEG. Клиент выдает это значение в tcp опциях SYN пакета. -Сервер в ответ в SYN,ACK выдает свой MSS. На практике сервера обычно снижают размеры отсылаемых ими пакетов, но они -все равно не вписываются в низкий MSS, указанный клиентом. Обычно чем больше указал клиент, тем больше -шлет сервер. На TLS 1.2 если сервер разбил заброс так, чтобы домен из сертификата не попал в первый пакет, -это может обмануть DPI, секущий ответ сервера. -Схема может значительно снизить скорость и сработать не на всех сайтах. -С фильтром по hostlist совместимо только в режиме socks при включенном удаленном ресолвинге хостов. -(firefox network.proxy.socks_remote_dns). Это единственный вариант, когда tpws может узнать имя хоста -еще на этапе установления соединения. -Применяя данную опцию к сайтам TLS1.3, если броузер тоже поддерживает TLS1.3, то вы делаете только хуже. -Но нет способа автоматически узнать когда надо применять, когда нет, поскольку MSS идет только в -3-way handshake еще до обмена данными, а версию TLS можно узнать только по ответу сервера, который -может привести к реакции DPI. -Использовать только когда нет ничего лучше или для отдельных ресурсов. -Для http использовать смысла нет, поэтому заводите отдельный desync profile с фильтром по порту 443. -Работает только на linux, не работает на BSD и MacOS. - -`--skip-nodelay` может быть полезен, чтобы привести MTU к MTU системы, на которой работает tpws. -Это может быть полезно для скрытия факта использования VPN. Пониженный MTU - 1 из способов обнаружения -подозрительного подключения. С tcp proxy ваши соединения неотличимы от тех, что сделал бы сам шлюз. - -`--local-tcp-user-timeout` и `--remote-tcp-user-timeout` устанавливают значение таймаута в секундах -для соединений клиент-прокси и прокси-сервер. Этот таймаут соответствует опции сокета linux -TCP_USER_TIMEOUT. Под таймаутом подразумевается время, в течение которого буферизированные данные -не переданы или на переданные данные не получено подтверждение (ACK) от другой стороны. -Этот таймаут никак не касается времени отсутствия какой-либо передачи через сокет лишь потому, -что данных для передачи нет. Полезно для сокращения время закрытия подвисших соединений. -Поддерживается только на Linux и MacOS. - -### МНОЖЕСТВЕННЫЕ СТРАТЕГИИ - -Работают аналогично `nfqws`, кроме некоторых моментов. -Нет параметра `--filter-udp`, поскольку `tpws` udp не поддерживает. -Методы нулевой фазы (`--mss`) могут работать по хостлисту в одном единственном случае: -если используется режим socks и удаленный ресолвинг хостов через прокси. -То есть работоспособность вашей настройки в одном и том же режиме может зависеть от того, -применяет ли клиент удаленный ресолвинг. Это может быть неочевидно. -В одной программе работает, в другой - нет. -Если вы используете профиль с хостлистом , и вам нужен mss, укажите mss в профиле с хостлистом, -создайте еще один профиль без хостлиста, если его еще нет, и в нем еще раз укажите mss. -Тогда при любом раскладе будет выполняться mss. -Используйте `curl --socks5` и `curl --socks5-hostname` для проверки вашей стратегии. -Смотрите вывод `--debug`, чтобы убедиться в правильности настроек. ## Способы получения списка заблокированных IP @@ -1516,7 +1553,7 @@ nfqws начнет получать адреса пакетов из локал ``` TPWS_SOCKS_OPT=" --filter-tcp=80 --methodeol --new ---filter-tcp=443 --split-tls=sni --disorder " +--filter-tcp=443 --split-pos=1,midsld --disorder " ``` ***Включение стандартной опции tpws в прозрачном режиме***\ @@ -1529,7 +1566,7 @@ TPWS_SOCKS_OPT=" ``` TPWS_OPT=" --filter-tcp=80 --methodeol --new ---filter-tcp=443 --split-tls=sni --disorder " +--filter-tcp=443 --split-pos=1,midsld --disorder " ``` ***Включение стандартной опции nfqws***\ @@ -1566,9 +1603,9 @@ NFQWS_PORTS_UDP_KEEPALIVE= ***Параметры nfqws*** ``` NFQWS_OPT=" ---filter-tcp=80 --dpi-desync=fake,split2 --dpi-desync-fooling=md5sig --new ---filter-tcp=443 --dpi-desync=fake,disorder2 --dpi-desync-fooling=md5sig --new ---filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=6 " +--filter-tcp=80 --dpi-desync=fake,multisplit --dpi-desync-split-pos=method+2 --dpi-desync-fooling=md5sig --new +--filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-fooling=badseq,md5sig --new +--filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=6 ``` ***Режим фильтрации хостов:*** @@ -1943,8 +1980,9 @@ zapret_custom_firewall_nft поднимает правила nftables. "не просто" до "почти невозможно". Если только вы не найдете готовое собранное ядро под ваш девайс. tpws будет работать в любом случае, он не требует чего-либо особенного. -В android нет /etc/passwd, потому опция `--user` не будет работать. Вместо нее можно -пользоваться числовыми user id и опцией `--uid`. + +Хотя linux варианты под Android работают, рекомендуется использовать специально собранные под bionic бинарники. +У них не будет проблем с DNS, с локальным временем и именами юзеров и групп. Рекомендую использовать gid 3003 (AID_INET). Иначе можете получить permission denied на создание сокета. Например: `--uid 1:3003`\ В iptables укажите: "! --uid-owner 1" вместо "! --uid-owner tpws".\ @@ -1956,10 +1994,6 @@ supersu: /system/su.d `nfqws` может иметь такой глюк. При запуске с uid по умолчанию (0x7FFFFFFF) при условии работы на сотовом интерфейсе и отключенном кабеле внешнего питания система может частично виснуть. Перестает работать тач и кнопки, но анимация на экране может продолжаться. Если экран был погашен, то включить его кнопкой power невозможно. -Это, видимо, связано с переводом в suspend процессов с определенным UID. UID соответствует приложению или -системному сервису. По UID android определяет политику power saving. -Так же возможно, что глюк связан с кривым драйвером сотового интерфейса от китайцев, поскольку при использовании -wifi такого не наблюдается. suspend обработчика nfqueue на обычном linux не вызывает подобных фатальных последствий. Изменение UID на низкий (--uid 1 подойдет) позволяет решить эту проблему. Глюк был замечен на android 8.1 на девайсе, основанном на платформе mediatek. From db5c60e19f67a97d7a9d9698a66d1fbd5acc7cd2 Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 18 Nov 2024 20:24:17 +0300 Subject: [PATCH 2/9] doc works --- docs/readme.md | 329 ++++++++++++++++++++++++++++--------------------- 1 file changed, 191 insertions(+), 138 deletions(-) diff --git a/docs/readme.md b/docs/readme.md index 5042fa7..8dbb289 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -17,8 +17,6 @@ zapret является свободным и open source. - [Как это работает](#как-это-работает) - [Что сейчас происходит в России](#что-сейчас-происходит-в-россии) - [Как это реализовать на практике в системе linux](#как-это-реализовать-на-практике-в-системе-linux) - - [Особенности применения ip6tables](#особенности-применения-ip6tables) - - [Особенности применения nftables](#особенности-применения-nftables) - [Когда это работать не будет](#когда-это-работать-не-будет) - [nfqws](#nfqws) - [АТАКА ДЕСИНХРОНИЗАЦИИ DPI](#атака-десинхронизации-dpi) @@ -28,7 +26,6 @@ zapret является свободным и open source. - [СПЕЦИФИЧЕСКИЕ РЕЖИМЫ IPV6](#специфические-режимы-ipv6) - [КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ](#комбинирование-методов-десинхронизации) - [РЕАКЦИЯ DPI НА ОТВЕТ СЕРВЕРА](#реакция-dpi-на-ответ-сервера) - - [IPTABLES ДЛЯ NFQWS](#iptables-для-nfqws) - [РЕЖИМ SYNACK](#режим-synack) - [РЕЖИМ SYNDATA](#режим-syndata) - [ВИРТУАЛЬНЫЕ МАШИНЫ](#виртуальные-машины) @@ -37,6 +34,9 @@ zapret является свободным и open source. - [ПОДДЕРЖКА UDP](#поддержка-udp) - [IP ФРАГМЕНТАЦИЯ](#ip-фрагментация) - [МНОЖЕСТВЕННЫЕ СТРАТЕГИИ](#множественные-стратегии) + - [IPTABLES ДЛЯ NFQWS](#iptables-для-nfqws) + - [NFTABLES ДЛЯ NFQWS](#nftables-для-nfqws) + - [FLOW OFFLOADING](#flow-offloading) - [tpws](#tpws) - [TCP СЕГМЕНТАЦИЯ В TPWS](#tcp-сегментация-в-tpws) - [TLSREC](#tlsrec) @@ -44,6 +44,8 @@ zapret является свободным и open source. - [ДРУГИЕ ПАРАМЕТРЫ ДУРЕНИЯ](#другие-параметры-дурения) - [МНОЖЕСТВЕННЫЕ СТРАТЕГИИ](#множественные-стратегии-1) - [СЛУЖЕБНЫЕ ПАРАМЕТРЫ](#служебные-параметры) + - [IPTABLES ДЛЯ TPWS](#iptables-для-nfqws) + - [NFTABLES ДЛЯ TPWS](#nftables-для-tpws) - [Способы получения списка заблокированных IP](#способы-получения-списка-заблокированных-ip) - [ip2net](#ip2net) - [mdig](#mdig) @@ -137,107 +139,6 @@ DPI. Все больше становится внереестровых бло Для вариантов 2 и 3 реализованы программы tpws и nfqws соответственно. Чтобы они работали, необходимо их запустить с нужными параметрами и перенаправить на них определенный трафик средствами iptables или nftables. -Для перенаправления tcp соединения на transparent proxy используются команды следующего вида : - -проходящий трафик: -`iptables -t nat -I PREROUTING -i <внутренний_интерфейс> -p tcp --dport 80 -j DNAT --to 127.0.0.127:988` - -исходящий трафик: -`iptables -t nat -I OUTPUT -o <внешний_интерфейс> -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.127:988` - -DNAT на localhost работает в цепочке OUTPUT, но не работает в цепочке PREROUTING без включения параметра -route_localnet : - -`sysctl -w net.ipv4.conf.<внутренний_интерфейс>.route_localnet=1` - -Можно использовать `-j REDIRECT --to-port 988` вместо DNAT, однако в этом случае процесс transparent proxy должен -слушать на ip адресе входящего интерфейса или на всех адресах. Слушать на всех - не есть хорошо с точки зрения -безопасности. Слушать на одном (локальном) можно, но в случае автоматизированного скрипта придется его узнавать, потом -динамически вписывать в команду. В любом случае требуются дополнительные усилия. Использование route_localnet тоже имеет -потенциальные проблемы с безопасностью. Вы делаете доступным все, что висит на `127.0.0.0/8` для локальной подсети < -внутренний_интерфейс>. Службы обычно привязываются к `127.0.0.1`, поэтому можно средствами iptables запретить входящие -на `127.0.0.1` не с интерфейса lo, либо повесить tpws на любой другой IP из из `127.0.0.0/8`, например на `127.0.0.127`, -и разрешить входящие не с lo только на этот IP. - -``` -iptables -A INPUT ! -i lo -d 127.0.0.127 -j ACCEPT -iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j DROP -``` - -Фильтр по owner необходим для исключения рекурсивного перенаправления соединений от самого tpws. tpws запускается под -пользователем `tpws`, для него задается исключающее правило. - -tpws может использоваться в режиме socks proxy. В этом случае iptables не нужны, а нужно прописать socks в настройки -программы (например, броузера), с которой будем обходить блокировки. transparent proxy отличается от socks именно тем, -что в варианте transparent настраивать клиентские программы не нужно. - -Для перенаправления на очередь NFQUEUE исходящего и проходящего в сторону внешнего интерфейса трафика используются -команды следующего вида : - -`iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -j NFQUEUE --queue-num 200 --queue-bypass` - -Чтобы не трогать трафик на незаблокированные адреса, можно взять список заблокированных хостов, заресолвить его в IP -адреса и загнать в ipset zapret, затем добавить фильтр в команду : - -`iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass` - -DPI может ловить только первый http запрос, игнорируя последующие запросы в keep-alive сессии. Тогда можем уменьшить -нагрузку на проц, отказавшись от процессинга ненужных пакетов. - -`iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -m set --match-set zapret dst -j NFQUEUE --queue-num 200 --queue-bypass` - -Фильтр по mark нужен для отсечения от очереди пакетов, сгенерированных внутри nfqws. Если применяется фильтр по -connbytes 1:6, то обязательно добавлять в iptables и фильтр по mark. Иначе возможно перепутывание порядка следования -пакетов, что приведет к неработоспособности метода. Так же возможно зависание nfqws по deadlock. - -Для некоторых атак на DPI требуется перенаправлять один или несколько входящих пакетов от соединения : - -`iptables -t mangle -I PREROUTING -i <внешний_интерфейс> -p tcp --sport 80 -m connbytes --connbytes-dir=reply --connbytes-mode=packets --connbytes 1:6 -m set --match-set zapret src -j NFQUEUE --queue-num 200 --queue-bypass` - -Получаемые пакеты будут фильтроваться по входящему интерфейсу, порту и IP источника, то есть наоборот прямому правилу. - -Некоторые техники, ломающие NAT, не всегда можно реализовать через iptables. Требуются nftables. - -Если ваше устройство поддерживает аппаратное ускорение (flow offloading, hardware nat, hardware acceleration), то -iptables могут не работать. При включенном offloading пакет не проходит по обычному пути netfilter. Необходимо или его -отключить, или выборочно им управлять. - -В новых ядрах (и в более старых, openwrt портировал изменение на 4.14) присутствует software flow offloading (SFO). -Пакеты, проходящие через SFO, так же проходят мимо большей части механизмов iptables. При включенном SFO работает -DNAT/REDIRECT (tpws). Эти соединения исключаются из offloading. Однако, остальные соединения идут через SFO, потому -NFQUEUE будет срабатывать только до помещения соединения в flowtable. Практически это означает, что nfqws будет работать -на window size changing, но не будут работать опции по модификации содержимого пакетов. Offload включается через -специальный target в iptables `FLOWOFFLOAD`. Не обязательно пропускать весь трафик через offload. Можно исключить из -offload соединения, которые должны попасть на tpws или nfqws. openwrt не предусматривает выборочного управления offload. -Поэтому скрипты zapret поддерживают свою систему выборочного управления offload в openwrt. - -## Особенности применения ip6tables - -ip6tables работают почти точно так же, как и ipv4, но есть ряд важных нюансов. В DNAT следует брать адрес --to в -квадратные скобки. Например : - -`ip6tables -t nat -I OUTPUT -o <внешний_интерфейс> -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to [::1]:988` - -Параметра route_localnet не существует для ipv6. DNAT на localhost (::1) возможен только в цепочке OUTPUT. В цепочке -PREROUTING DNAT возможен на любой global address или на link local address того же интерфейса, откуда пришел пакет. -NFQUEUE работает без изменений. - -## Особенности применения nftables - -Более подробно преимущества и недостатки nftables применительно к данной системе описаны в [docs/nftables_notes.txt](./nftables_notes.txt) Если -коротко, то в nftables невозможно работать с большими ip листами на системах с малым количеством RAM. Остальные -рассматриваемые здесь функции могут быть перенесены на nftables. - -Рекомендуется версия nft `1.0.2` или выше. Но чем выше версия, тем лучше. В nft регулярно находят баги. - -Относительно старые версии ядра и/или утилиты nft могут вызывать ошибки. В частности, на ubuntu 18.04 с ядром 4.15 будут -проблемы. В 20.04 - работает. - -Некоторые техники можно полноценно использовать только с nftables. Например, в iptables невозможно перенаправить пакеты -на nfqws после NAT. Следовательно, при использовании NAT невозможно произвести атаку, не совместимую с NAT. В nftables -этой проблемы не существует, потому что приоритеты хука выставляете вы сами, а не привязаны к фиксированным значениям, -соответствующим разным таблицам iptables. В iptables нет таблицы, способной перехватить пакеты после MASQUERDADE. - ## Когда это работать не будет * Если подменяется DNS. С этой проблемой легко справиться. @@ -497,7 +398,7 @@ extension хедерам в поисках транспортного хедер В параметре dpi-desync можно указать до 3 режимов через запятую. -* 0 фаза - предполагает работу на этапе установления соединения : `synack`, `syndata` `--wsize`, `--wssize`. +* 0 фаза - предполагает работу на этапе установления соединения : `synack`, `syndata` `--wsize`, `--wssize`. На эту фазу не действуют фильтры по [hostlist](((#множественные-стратегии))). * 1 фаза - отсылка чего-либо до оригинального пакета данных : `fake`, `rst`, `rstack`. * 2 фаза - отсылка в модифицированном виде оригинального пакета данных (например, `fakedsplit` или `ipfrag2`). @@ -519,34 +420,6 @@ DPI может отстать от потока, если ClientHello его у Лучшее решение - включить на сервере поддержку TLS 1.3. В нем сертификат сервера передается в зашифрованном виде. Это рекомендация ко всем админам блокируемых сайтов. Включайте TLS 1.3. Так вы дадите больше возможностей преодолеть DPI. -### IPTABLES ДЛЯ NFQWS - -iptables для задействования атаки на первый пакет данных : - -`iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp -m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass` - -Этот вариант применяем, когда DPI не следит за всеми запросами http внутри keep-alive сессии. -Если следит, направляем только первый пакет от https и все пакеты от http : - -``` -iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass -iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass -``` - -mark нужен, чтобы сгенерированный поддельный пакет не попал опять к нам на обработку. nfqws выставляет fwmark при его отсылке. -хотя nfqws способен самостоятельно различать помеченные пакеты, фильтр в iptables по mark нужен при использовании connbytes, -чтобы не допустить изменения порядка следования пакетов. Процессинг очереди - процесс отложенный. -Если ядро имеет пакеты на отсылку вне очереди - оно их отправляет незамедлительно. -Изменение правильного порядка следования пакетов при десинхронизации ломает всю идею. -Так же были замечены дедлоки при достаточно большой отсылке пакетов из nfqws и отсутствии mark фильтра. -Процесс может зависнуть. Поэтому наличие фильтра по mark в ip/nf tables можно считать обязательным. - -Почему --connbytes 1:6 : -* 1 - для работы методов десинхронизации 0-й фазы и корректной работы conntrack -* 2 - иногда данные идут в 3-м пакете 3-way handshake -* 3 - стандартная ситуация приема одного пакета запроса -* 4-6 - на случай ретрансмиссии или запроса длиной в несколько пакетов (TLSClientHello с kyber, например) - ### РЕЖИМ SYNACK В документации по geneva это называется "TCB turnaround". Попытка ввести DPI в заблуждение относительно @@ -776,6 +649,115 @@ L7 протокол становится известен обычно посл > Вариант в ядре работает гораздо эффективнее. Это создавалось для систем без подержки ipset в ядре. > Конкретно - Windows и ядра Linux, собранные без nftables и ipset модулей ядра. Например, в android нет ipset. +### IPTABLES ДЛЯ NFQWS + +iptables для задействования атаки на первые пакеты данных в tcp соединении : + +``` +iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp -m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass +``` + +Этот вариант применяем, когда DPI не следит за всеми запросами http внутри keep-alive сессии. +Если следит, направляем только первый пакет от https и все пакеты от http : + +``` +iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass +iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp --dport 80 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass +``` + +mark нужен, чтобы сгенерированный поддельный пакет не попал опять к нам на обработку. nfqws выставляет fwmark при его отсылке. +хотя nfqws способен самостоятельно различать помеченные пакеты, фильтр в iptables по mark нужен при использовании connbytes, +чтобы не допустить изменения порядка следования пакетов. Процессинг очереди - процесс отложенный. +Если ядро имеет пакеты на отсылку вне очереди - оно их отправляет незамедлительно. +Изменение правильного порядка следования пакетов при десинхронизации ломает всю идею. +Так же были замечены дедлоки при достаточно большой отсылке пакетов из nfqws и отсутствии mark фильтра. +Процесс может зависнуть. Поэтому наличие фильтра по mark в ip/nf tables можно считать обязательным. + +Почему `--connbytes 1:6` : +* 1 - для работы методов десинхронизации 0-й фазы и корректной работы conntrack +* 2 - иногда данные идут в 3-м пакете 3-way handshake +* 3 - стандартная ситуация приема одного пакета запроса +* 4-6 - на случай ретрансмиссии или запроса длиной в несколько пакетов (TLSClientHello с kyber, например) + +Для режима autottl необходимо перенаправление входящего `SYN,ACK` пакета или первого пакета соединения (что обычно есть тоже самое). +Можно построить фильтр на tcp flags и модуле u32 для поиска характерных паттернов http redirect, но проще использовать connbytes. + +Для режима autohostlist необходимо перенаправление нескольких входящих пакетов, чтобы засечь RST или http redirect. +Так же стоит увеличить лимит исходящих пакетов в connbytes, чтобы в него вошли все возможные ретрансмиссии, после которых идет реакция по autoostlist. + +` +iptables -t mangle -I PREROUTING -i <внешний интерфейс> -p tcp -m multiport --sports 80,443 -m connbytes --connbytes-dir=reply --connbytes-mode=packets --connbytes 1:3 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass +` + +Для quic : + +``` +iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p udp --dport 443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass +``` + +6 пакетов берется, чтобы покрыть случаи возможных ретрансмиссий quic initial в случае плохой связи или если сервер плохо себя чувствует, а приложение настаивает именно на quic, не переходя на tcp. +А так же для работы autohostlist по quic. Однако, autohostlist для quic не рекомендуется. + +### NFTABLES ДЛЯ NFQWS + +Можно начать с базовой конфигурации. + +``` +IFACE_WAN=wan + +nft create table inet ztest + +nft add chain inet ztest post "{type filter hook postrouting priority mangle;}" +nft add rule inet ztest post oifname $IFACE_WAN meta mark and 0x40000000 == 0 tcp dport "{80,443}" ct original packets 1-6 queue num 200 bypass +nft add rule inet ztest post oifname $IFACE_WAN meta mark and 0x40000000 == 0 udp dport 443 ct original packets 1-6 queue num 200 bypass + +# auto hostlist with avoiding wrong ACK numbers in RST,ACK packets sent by russian DPI +sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1 +nft add chain inet ztest pre "{type filter hook prerouting priority filter;}" +nft add rule inet ztest pre iifname $IFACE_WAN tcp sport "{80,443}" ct reply packets 1-3 queue num 200 bypass +``` + +Для задействования IP фрагментации и `datanoack` на проходящие пакеты требуется особая конфигурация цепочек, перенаправляющая пакеты после NAT. +В скриптах zapret эта схема называется `POSTNAT`, и она возможна только на nftables. +Сгенерированные nfqws пакеты требуется на раннем этапе помечать как **notrack**, чтобы они не были испорчены NAT. + +``` +IFACE_WAN=wan + +nft create table inet ztest + +nft add chain inet ztest postnat "{type filter hook postrouting priority srcnat+1;}" +nft add rule inet ztest postnat oifname $IFACE_WAN meta mark and 0x40000000 == 0 tcp dport "{80,443}" ct original packets 1-6 queue num 200 bypass +nft add rule inet ztest postnat oifname $IFACE_WAN meta mark and 0x40000000 == 0 udp dport 443 ct original packets 1-6 queue num 200 bypass + +nft add chain inet ztest predefrag "{type filter hook output priority -401;}" +nft add rule inet ztest predefrag "mark & 0x40000000 != 0x00000000 notrack" +``` + +Удаление тестовой таблицы : + +``` +nft delete table inet ztest +``` + +### FLOW OFFLOADING + +Если ваше устройство поддерживает аппаратное ускорение (flow offloading, hardware nat, hardware acceleration), то +iptables могут не работать. При включенном offloading пакет не проходит по обычному пути netfilter. Необходимо или его +отключить, или выборочно им управлять. + +В новых ядрах присутствует software flow offloading (SFO). +Пакеты, проходящие через SFO, так же проходят мимо большей части механизмов iptables. При включенном SFO работает +DNAT/REDIRECT (tpws). Эти соединения исключаются из offloading. Однако, остальные соединения идут через SFO, потому +NFQUEUE будет срабатывать только до помещения соединения в flowtable. Практически это означает, что почти весь функционал nfqws работать не будет. +Offload включается через специальный target в iptables `FLOWOFFLOAD`. Не обязательно пропускать весь трафик через offload. Можно исключить из +offload соединения, которые должны попасть на tpws или nfqws. openwrt не предусматривает выборочного управления offload. +Поэтому скрипты zapret поддерживают свою систему выборочного управления offload в openwrt. + +iptables target `FLOWOFFLOAD` - это проприетарное изобретение openwrt. +Управление offload в nftables реализовано в базовом ядре linux без патчей. + + ## tpws tpws - это transparent proxy. @@ -1034,6 +1016,77 @@ tpws поддерживает эту возможность асинхронно Если задан параметр `--no-resolve`, то подключения по именам хостов запрещаются, а пул ресолверов не создается. Тем самым экономятся ресурсы. +### IPTABLES ДЛЯ TPWS + +Для перенаправления tcp соединения на transparent proxy используются команды следующего вида : + +``` +iptables -t nat -I OUTPUT -o <внешний_интерфейс> -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.127:988 +iptables -t nat -I PREROUTING -i <внутренний_интерфейс> -p tcp --dport 80 -j DNAT --to 127.0.0.127:988 +``` + +Первая команда для соединений с самой системы, вторая - для проходящих через роутер соединений. + +DNAT на localhost работает в цепочке OUTPUT, но не работает в цепочке PREROUTING без включения параметра +route_localnet : + +`sysctl -w net.ipv4.conf.<внутренний_интерфейс>.route_localnet=1` + +Можно использовать `-j REDIRECT --to-port 988` вместо DNAT, однако в этом случае процесс transparent proxy должен +слушать на ip адресе входящего интерфейса или на всех адресах. Слушать на всех - не есть хорошо с точки зрения +безопасности. Слушать на одном (локальном) можно, но в случае автоматизированного скрипта придется его узнавать, потом +динамически вписывать в команду. В любом случае требуются дополнительные усилия. Использование route_localnet тоже имеет +потенциальные проблемы с безопасностью. Вы делаете доступным все, что висит на `127.0.0.0/8` для локальной подсети < +внутренний_интерфейс>. Службы обычно привязываются к `127.0.0.1`, поэтому можно средствами iptables запретить входящие +на `127.0.0.1` не с интерфейса lo, либо повесить tpws на любой другой IP из из `127.0.0.0/8`, например на `127.0.0.127`, +и разрешить входящие не с lo только на этот IP. + +``` +iptables -A INPUT ! -i lo -d 127.0.0.127 -j ACCEPT +iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j DROP +``` + +Фильтр по owner необходим для исключения рекурсивного перенаправления соединений от самого tpws. tpws запускается под +пользователем `tpws`, для него задается исключающее правило. + +ip6tables работают почти точно так же, как и ipv4, но есть ряд важных нюансов. В DNAT следует брать адрес --to в +квадратные скобки. Например : + +`ip6tables -t nat -I OUTPUT -o <внешний_интерфейс> -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to [::1]:988` + +Параметра route_localnet не существует для ipv6. DNAT на localhost (::1) возможен только в цепочке OUTPUT. В цепочке +PREROUTING DNAT возможен на любой global address или на link local address того же интерфейса, откуда пришел пакет. +NFQUEUE работает без изменений. + +### NFTABLES ДЛЯ TPWS + +Базовая конфигурация : + +``` +IFACE_WAN=wan +IFACE_LAN=br-lan + +sysctl -w net.ipv4.conf.$IFACE_LAN.route_localnet=1 + +nft create table inet ztest + +nft create chain inet ztest localnet_protect +nft add rule inet ztest localnet_protect ip daddr 127.0.0.127 return +nft add rule inet ztest localnet_protect ip daddr 127.0.0.0/8 drop +nft create chain inet ztest input "{type filter hook input priority filter - 1;}" +nft add rule inet ztest input iif != "lo" jump localnet_protect + +nft create chain inet ztest dnat_output "{type nat hook output priority dstnat;}" +nft add rule inet ztest dnat_output meta skuid != tpws oifname $IFACE_WAN tcp dport { 80, 443 } dnat ip to 127.0.0.127:988 +nft create chain inet ztest dnat_pre "{type nat hook prerouting priority dstnat;}" +nft add rule inet ztest dnat_pre meta iifname $IFACE_LAN tcp dport { 80, 443 } dnat ip to 127.0.0.127:988 +``` + +Удаление таблицы : +``` +nft delete table inet ztest +``` + ## Способы получения списка заблокированных IP @@ -1115,17 +1168,17 @@ Cкрипты с названием `get_antifilter_*` оперируют спи в отдельный ipset `ipban`. Он может использоваться для принудительного завертывания всех соединений на прозрачный proxy `redsocks` или на VPN. -IPV6 : если включен ipv6, то дополнительно создаются листы с таким же именем, но с "6" на конце перед расширением. +**IPV6** : если включен ipv6, то дополнительно создаются листы с таким же именем, но с "6" на конце перед расширением. `zapret-ip.txt` => `zapret-ip6.txt` Создаются ipset-ы zapret6 и ipban6. Листы с antifilter не содержат список ipv6 адресов. -СИСТЕМА ИСКЛЮЧЕНИЯ IP. Все скрипты ресолвят файл `zapret-hosts-user-exclude.txt`, создавая `zapret-ip-exclude.txt` и `zapret-ip-exclude6.txt`. +**СИСТЕМА ИСКЛЮЧЕНИЯ IP**. Все скрипты ресолвят файл `zapret-hosts-user-exclude.txt`, создавая `zapret-ip-exclude.txt` и `zapret-ip-exclude6.txt`. Они загоняются в ipset-ы nozapret и nozapret6. Все правила, создаваемые init скриптами, создаются с учетом этих ipset. Помещенные в них IP не участвуют в процессе. `zapret-hosts-user-exclude.txt` может содержать домены, ipv4 и ipv6 адреса или подсети. -FreeBSD. Скрипты ipset/*.sh работают так же на FreeBSD. Вместо ipset они создают lookup таблицы ipfw с аналогичными именами. +**FreeBSD**. Скрипты ipset/*.sh работают так же на FreeBSD. Вместо ipset они создают lookup таблицы ipfw с аналогичными именами. ipfw таблицы в отличие от ipset могут содержать как ipv4, так и ipv6 адреса и подсети в одной таблице, поэтому разделения нет. Параметр конфига LISTS_RELOAD задает произвольную команду для перезагрузки листов. @@ -1982,10 +2035,10 @@ zapret_custom_firewall_nft поднимает правила nftables. tpws будет работать в любом случае, он не требует чего-либо особенного. Хотя linux варианты под Android работают, рекомендуется использовать специально собранные под bionic бинарники. -У них не будет проблем с DNS, с локальным временем и именами юзеров и групп. +У них не будет проблем с DNS, с локальным временем и именами юзеров и групп.\ Рекомендую использовать gid 3003 (AID_INET). Иначе можете получить permission denied на создание сокета. Например: `--uid 1:3003`\ -В iptables укажите: "! --uid-owner 1" вместо "! --uid-owner tpws".\ +В iptables укажите: `! --uid-owner 1` вместо `! --uid-owner tpws`.\ Напишите шелл скрипт с iptables и tpws, запускайте его средствами вашего рут менеджера. Скрипты автозапуска лежат тут:\ magisk : /data/adb/service.d\ From 827a8387152cc93d1ba17ef35ad55a7c75370d6d Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 18 Nov 2024 20:25:52 +0300 Subject: [PATCH 3/9] doc works --- docs/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/readme.md b/docs/readme.md index 8dbb289..1710597 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -44,7 +44,7 @@ zapret является свободным и open source. - [ДРУГИЕ ПАРАМЕТРЫ ДУРЕНИЯ](#другие-параметры-дурения) - [МНОЖЕСТВЕННЫЕ СТРАТЕГИИ](#множественные-стратегии-1) - [СЛУЖЕБНЫЕ ПАРАМЕТРЫ](#служебные-параметры) - - [IPTABLES ДЛЯ TPWS](#iptables-для-nfqws) + - [IPTABLES ДЛЯ TPWS](#iptables-для-tpws) - [NFTABLES ДЛЯ TPWS](#nftables-для-tpws) - [Способы получения списка заблокированных IP](#способы-получения-списка-заблокированных-ip) - [ip2net](#ip2net) From f7ae5eaae51b898489a1ccf84122b840b85159c9 Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 18 Nov 2024 20:33:37 +0300 Subject: [PATCH 4/9] doc works --- docs/readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/readme.md b/docs/readme.md index 1710597..712f968 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -830,7 +830,6 @@ tpws - это transparent proxy. --unixeol ; конвертировать 0D0A в 0A и использовать везде 0A --tlsrec=N|-N|marker+N|marker-N ; разбивка TLS ClientHello на 2 TLS records на указанной позиции. Минимальное смещение - 6. --mss= ; установить MSS для клиента. может заставить сервер разбивать ответы, но существенно снижает скорость ---mss-pf=[~]port1[-port2] ; применять MSS только к портам назначения, подпадающим под фильтр. ~ означает инверсию --tamper-start=[n] ; начинать дурение только с указанной байтовой позиции или номера блока исходяшего потока (считается позиция начала принятого блока) --tamper-cutoff=[n] ; закончить дурение на указанной байтовой позиции или номере блока исходящего потока (считается позиция начала принятого блока) --hostlist= ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются. From 840617a0c39b66852daa90bac5b1e8919274bedd Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 18 Nov 2024 20:42:55 +0300 Subject: [PATCH 5/9] install_easy: copy systemd units instead of linking --- install_easy.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install_easy.sh b/install_easy.sh index d001051..a6aca99 100755 --- a/install_easy.sh +++ b/install_easy.sh @@ -549,7 +549,7 @@ service_install_systemd() if [ -w "$SYSTEMD_SYSTEM_DIR" ] ; then rm -f "$INIT_SCRIPT" - ln -fs "$EXEDIR/init.d/systemd/zapret.service" "$SYSTEMD_SYSTEM_DIR" + cp -f "$EXEDIR/init.d/systemd/zapret.service" "$SYSTEMD_SYSTEM_DIR" "$SYSTEMCTL" daemon-reload "$SYSTEMCTL" enable zapret || { echo could not enable systemd service @@ -567,8 +567,8 @@ timer_install_systemd() if [ -w "$SYSTEMD_SYSTEM_DIR" ] ; then "$SYSTEMCTL" disable zapret-list-update.timer "$SYSTEMCTL" stop zapret-list-update.timer - ln -fs "$EXEDIR/init.d/systemd/zapret-list-update.service" "$SYSTEMD_SYSTEM_DIR" - ln -fs "$EXEDIR/init.d/systemd/zapret-list-update.timer" "$SYSTEMD_SYSTEM_DIR" + cp -f "$EXEDIR/init.d/systemd/zapret-list-update.service" "$SYSTEMD_SYSTEM_DIR" + cp -f "$EXEDIR/init.d/systemd/zapret-list-update.timer" "$SYSTEMD_SYSTEM_DIR" "$SYSTEMCTL" daemon-reload "$SYSTEMCTL" enable zapret-list-update.timer || { echo could not enable zapret-list-update.timer From 46eb30a897d9f73d6b62ffc4f12fdb3b16b94bc3 Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 18 Nov 2024 21:00:25 +0300 Subject: [PATCH 6/9] build docs for unix and windows --- docs/compile/build_howto_unix.txt | 16 +++++++++++++++ docs/compile/build_howto_windows.txt | 29 ++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 docs/compile/build_howto_unix.txt create mode 100644 docs/compile/build_howto_windows.txt diff --git a/docs/compile/build_howto_unix.txt b/docs/compile/build_howto_unix.txt new file mode 100644 index 0000000..d4ca099 --- /dev/null +++ b/docs/compile/build_howto_unix.txt @@ -0,0 +1,16 @@ +debian,ubuntu : + +apt install make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev +make -C /opt/zapret + +FreeBSD : + +make -C /opt/zapret + +OpenBSD : + +make -C /opt/zapret bsd + +MacOS : + +make -C /opt/zapret mac diff --git a/docs/compile/build_howto_windows.txt b/docs/compile/build_howto_windows.txt new file mode 100644 index 0000000..6b43b37 --- /dev/null +++ b/docs/compile/build_howto_windows.txt @@ -0,0 +1,29 @@ +Windows x64 + +1) Download latest cygwin for windows 7 + +curl -O https://www.cygwin.com/setup-x86_64.exe +setup-x86_64.exe --allow-unsupported-windows --no-verify --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215 + +2) During setup install packages : make gcc-core zlib-devel + +3) Run Cygwin.bat + +4) cd to %ZAPRET_BASE%/nfq + +cd C:/Users/user/Downloads/zapret/nfq + +5) Compile + +make cygwin64 + +use winws.exe + +6) Take windivert.dll and windivert64.sys here : https://reqrypt.org/download +Choose version 2.2.2 for Windows 10 and 2.2.0 for Windows 7. + +7) Copy cygwin1.dll, winws.exe, windivert.dll and windivert64.sys to one folder. + +8) Run winws.exe from cmd.exe running as administrator. +winws will not run from cygwin shell with cygwin1.dll copy in it's folder. +winws will not run without cygwin1.dll outside of cygwin shell. From 06147836d0f3d01309e4722599bd73566b7edfed Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 18 Nov 2024 23:05:25 +0300 Subject: [PATCH 7/9] tpws: segmentation failure warning and fix --- tpws/helpers.c | 254 +++++++++++++++++++++++++++++------------------ tpws/helpers.h | 6 ++ tpws/params.h | 1 + tpws/tpws.c | 11 +- tpws/tpws_conn.c | 16 ++- 5 files changed, 186 insertions(+), 102 deletions(-) diff --git a/tpws/helpers.c b/tpws/helpers.c index c25fd4f..801b213 100644 --- a/tpws/helpers.c +++ b/tpws/helpers.c @@ -11,6 +11,10 @@ #include #include +#ifdef __linux__ +#include +#endif + #ifdef __ANDROID__ #include "andr/ifaddrs.h" #else @@ -34,9 +38,9 @@ static int cmp_size_t(const void * a, const void * b) { return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b; } -void qsort_size_t(size_t *array,size_t ct) +void qsort_size_t(size_t *array, size_t ct) { - qsort(array,ct,sizeof(*array),cmp_size_t); + qsort(array, ct, sizeof(*array), cmp_size_t); } @@ -48,10 +52,10 @@ void rtrim(char *s) void replace_char(char *s, char from, char to) { - for(;*s;s++) if (*s==from) *s=to; + for (; *s; s++) if (*s == from) *s = to; } -char *strncasestr(const char *s,const char *find, size_t slen) +char *strncasestr(const char *s, const char *find, size_t slen) { char c, sc; size_t len; @@ -92,9 +96,9 @@ bool load_file(const char *filename, void *buffer, size_t *buffer_size) bool append_to_list_file(const char *filename, const char *s) { - FILE *F = fopen(filename,"at"); + FILE *F = fopen(filename, "at"); if (!F) return false; - bool bOK = fprintf(F,"%s\n",s)>0; + bool bOK = fprintf(F, "%s\n", s) > 0; fclose(F); return bOK; } @@ -102,7 +106,7 @@ bool append_to_list_file(const char *filename, const char *s) void ntop46(const struct sockaddr *sa, char *str, size_t len) { if (!len) return; - *str=0; + *str = 0; switch (sa->sa_family) { case AF_INET: @@ -112,50 +116,50 @@ void ntop46(const struct sockaddr *sa, char *str, size_t len) inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, str, len); break; default: - snprintf(str,len,"UNKNOWN_FAMILY_%d",sa->sa_family); + snprintf(str, len, "UNKNOWN_FAMILY_%d", sa->sa_family); } } void ntop46_port(const struct sockaddr *sa, char *str, size_t len) { char ip[40]; - ntop46(sa,ip,sizeof(ip)); + ntop46(sa, ip, sizeof(ip)); switch (sa->sa_family) { case AF_INET: - snprintf(str,len,"%s:%u",ip,ntohs(((struct sockaddr_in*)sa)->sin_port)); + snprintf(str, len, "%s:%u", ip, ntohs(((struct sockaddr_in*)sa)->sin_port)); break; case AF_INET6: - snprintf(str,len,"[%s]:%u",ip,ntohs(((struct sockaddr_in6*)sa)->sin6_port)); + snprintf(str, len, "[%s]:%u", ip, ntohs(((struct sockaddr_in6*)sa)->sin6_port)); break; default: - snprintf(str,len,"%s",ip); + snprintf(str, len, "%s", ip); } } void print_sockaddr(const struct sockaddr *sa) { char ip_port[48]; - ntop46_port(sa,ip_port,sizeof(ip_port)); - printf("%s",ip_port); + ntop46_port(sa, ip_port, sizeof(ip_port)); + printf("%s", ip_port); } // -1 = error, 0 = not local, 1 = local bool check_local_ip(const struct sockaddr *saddr) { - struct ifaddrs *addrs,*a; + struct ifaddrs *addrs, *a; if (is_localnet(saddr)) return true; - if (getifaddrs(&addrs)<0) return false; - a = addrs; + if (getifaddrs(&addrs) < 0) return false; + a = addrs; - bool bres=false; + bool bres = false; while (a) { - if (a->ifa_addr && sacmp(a->ifa_addr,saddr)) + if (a->ifa_addr && sacmp(a->ifa_addr, saddr)) { - bres=true; + bres = true; break; } a = a->ifa_next; @@ -177,7 +181,7 @@ void print_addrinfo(const struct addrinfo *ai) break; case AF_INET6: if (inet_ntop(ai->ai_family, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, str, sizeof(str))) - printf( "%s\n", str); + printf("%s\n", str); break; } ai = ai->ai_next; @@ -189,23 +193,23 @@ void print_addrinfo(const struct addrinfo *ai) bool saismapped(const struct sockaddr_in6 *sa) { // ::ffff:1.2.3.4 - return !memcmp(sa->sin6_addr.s6_addr,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff",12); + return !memcmp(sa->sin6_addr.s6_addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff", 12); } -bool samappedcmp(const struct sockaddr_in *sa1,const struct sockaddr_in6 *sa2) +bool samappedcmp(const struct sockaddr_in *sa1, const struct sockaddr_in6 *sa2) { - return saismapped(sa2) && !memcmp(sa2->sin6_addr.s6_addr+12,&sa1->sin_addr.s_addr,4); + return saismapped(sa2) && !memcmp(sa2->sin6_addr.s6_addr + 12, &sa1->sin_addr.s_addr, 4); } -bool sacmp(const struct sockaddr *sa1,const struct sockaddr *sa2) +bool sacmp(const struct sockaddr *sa1, const struct sockaddr *sa2) { - return (sa1->sa_family==AF_INET && sa2->sa_family==AF_INET && !memcmp(&((struct sockaddr_in*)sa1)->sin_addr,&((struct sockaddr_in*)sa2)->sin_addr,sizeof(struct in_addr))) || - (sa1->sa_family==AF_INET6 && sa2->sa_family==AF_INET6 && !memcmp(&((struct sockaddr_in6*)sa1)->sin6_addr,&((struct sockaddr_in6*)sa2)->sin6_addr,sizeof(struct in6_addr))) || - (sa1->sa_family==AF_INET && sa2->sa_family==AF_INET6 && samappedcmp((struct sockaddr_in*)sa1,(struct sockaddr_in6*)sa2)) || - (sa1->sa_family==AF_INET6 && sa2->sa_family==AF_INET && samappedcmp((struct sockaddr_in*)sa2,(struct sockaddr_in6*)sa1)); + return (sa1->sa_family == AF_INET && sa2->sa_family == AF_INET && !memcmp(&((struct sockaddr_in*)sa1)->sin_addr, &((struct sockaddr_in*)sa2)->sin_addr, sizeof(struct in_addr))) || + (sa1->sa_family == AF_INET6 && sa2->sa_family == AF_INET6 && !memcmp(&((struct sockaddr_in6*)sa1)->sin6_addr, &((struct sockaddr_in6*)sa2)->sin6_addr, sizeof(struct in6_addr))) || + (sa1->sa_family == AF_INET && sa2->sa_family == AF_INET6 && samappedcmp((struct sockaddr_in*)sa1, (struct sockaddr_in6*)sa2)) || + (sa1->sa_family == AF_INET6 && sa2->sa_family == AF_INET && samappedcmp((struct sockaddr_in*)sa2, (struct sockaddr_in6*)sa1)); } uint16_t saport(const struct sockaddr *sa) { - return htons(sa->sa_family==AF_INET ? ((struct sockaddr_in*)sa)->sin_port : - sa->sa_family==AF_INET6 ? ((struct sockaddr_in6*)sa)->sin6_port : 0); + return htons(sa->sa_family == AF_INET ? ((struct sockaddr_in*)sa)->sin_port : + sa->sa_family == AF_INET6 ? ((struct sockaddr_in6*)sa)->sin6_port : 0); } bool saconvmapped(struct sockaddr_storage *a) { @@ -223,16 +227,16 @@ bool saconvmapped(struct sockaddr_storage *a) void sacopy(struct sockaddr_storage *sa_dest, const struct sockaddr *sa) { - switch(sa->sa_family) + switch (sa->sa_family) { - case AF_INET: - memcpy(sa_dest,sa,sizeof(struct sockaddr_in)); - break; - case AF_INET6: - memcpy(sa_dest,sa,sizeof(struct sockaddr_in6)); - break; - default: - sa_dest->ss_family = 0; + case AF_INET: + memcpy(sa_dest, sa, sizeof(struct sockaddr_in)); + break; + case AF_INET6: + memcpy(sa_dest, sa, sizeof(struct sockaddr_in6)); + break; + default: + sa_dest->ss_family = 0; } } void sa46copy(sockaddr_in46 *sa_dest, const struct sockaddr *sa) @@ -243,17 +247,17 @@ void sa46copy(sockaddr_in46 *sa_dest, const struct sockaddr *sa) bool is_localnet(const struct sockaddr *a) { // match 127.0.0.0/8, 0.0.0.0, ::1, ::0, :ffff:127.0.0.0/104, :ffff:0.0.0.0 - return (a->sa_family==AF_INET && (IN_LOOPBACK(ntohl(((struct sockaddr_in *)a)->sin_addr.s_addr)) || - INADDR_ANY == ntohl((((struct sockaddr_in *)a)->sin_addr.s_addr)))) || - (a->sa_family==AF_INET6 && (IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6 *)a)->sin6_addr) || - IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)a)->sin6_addr) || - (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)a)->sin6_addr) && (IN_LOOPBACK(ntohl(IN6_EXTRACT_MAP4(((struct sockaddr_in6*)a)->sin6_addr.s6_addr))) || - INADDR_ANY == ntohl(IN6_EXTRACT_MAP4(((struct sockaddr_in6*)a)->sin6_addr.s6_addr)))))); + return (a->sa_family == AF_INET && (IN_LOOPBACK(ntohl(((struct sockaddr_in *)a)->sin_addr.s_addr)) || + INADDR_ANY == ntohl((((struct sockaddr_in *)a)->sin_addr.s_addr)))) || + (a->sa_family == AF_INET6 && (IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6 *)a)->sin6_addr) || + IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)a)->sin6_addr) || + (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)a)->sin6_addr) && (IN_LOOPBACK(ntohl(IN6_EXTRACT_MAP4(((struct sockaddr_in6*)a)->sin6_addr.s6_addr))) || + INADDR_ANY == ntohl(IN6_EXTRACT_MAP4(((struct sockaddr_in6*)a)->sin6_addr.s6_addr)))))); } bool is_linklocal(const struct sockaddr_in6 *a) { // fe80::/10 - return a->sin6_addr.s6_addr[0]==0xFE && (a->sin6_addr.s6_addr[1] & 0xC0)==0x80; + return a->sin6_addr.s6_addr[0] == 0xFE && (a->sin6_addr.s6_addr[1] & 0xC0) == 0x80; } bool is_private6(const struct sockaddr_in6* a) { @@ -265,23 +269,23 @@ bool is_private6(const struct sockaddr_in6* a) bool set_keepalive(int fd) { - int yes=1; - return setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(int))!=-1; + int yes = 1; + return setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(int)) != -1; } bool set_ttl(int fd, int ttl) { - return setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl))!=-1; + return setsockopt(fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)) != -1; } bool set_hl(int fd, int hl) { - return setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hl, sizeof(hl))!=-1; + return setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hl, sizeof(hl)) != -1; } bool set_ttl_hl(int fd, int ttl) { - bool b1,b2; + bool b1, b2; // try to set both but one may fail if family is wrong - b1=set_ttl(fd, ttl); - b2=set_hl(fd, ttl); + b1 = set_ttl(fd, ttl); + b2 = set_hl(fd, ttl); return b1 || b2; } int get_so_error(int fd) @@ -289,8 +293,8 @@ int get_so_error(int fd) // getsockopt(SO_ERROR) clears error int errn; socklen_t optlen = sizeof(errn); - if(getsockopt(fd, SOL_SOCKET, SO_ERROR, &errn, &optlen) == -1) - errn=errno; + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &errn, &optlen) == -1) + errn = errno; return errn; } @@ -300,53 +304,53 @@ int fprint_localtime(FILE *F) time_t now; time(&now); - localtime_r(&now,&t); + localtime_r(&now, &t); return fprintf(F, "%02d.%02d.%04d %02d:%02d:%02d", t.tm_mday, t.tm_mon + 1, t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec); } time_t file_mod_time(const char *filename) { struct stat st; - return stat(filename,&st)==-1 ? 0 : st.st_mtime; + return stat(filename, &st) == -1 ? 0 : st.st_mtime; } bool pf_in_range(uint16_t port, const port_filter *pf) { - return port && (((!pf->from && !pf->to) || (port>=pf->from && port<=pf->to)) ^ pf->neg); + return port && (((!pf->from && !pf->to) || (port >= pf->from && port <= pf->to)) ^ pf->neg); } bool pf_parse(const char *s, port_filter *pf) { - unsigned int v1,v2; + unsigned int v1, v2; char c; if (!s) return false; - if (*s=='*' && s[1]==0) + if (*s == '*' && s[1] == 0) { - pf->from=1; pf->to=0xFFFF; + pf->from = 1; pf->to = 0xFFFF; return true; } - if (*s=='~') + if (*s == '~') { - pf->neg=true; + pf->neg = true; s++; } else - pf->neg=false; - if (sscanf(s,"%u-%u%c",&v1,&v2,&c)==2) + pf->neg = false; + if (sscanf(s, "%u-%u%c", &v1, &v2, &c) == 2) { - if (v1>65535 || v2>65535 || v1>v2) return false; - pf->from=(uint16_t)v1; - pf->to=(uint16_t)v2; + if (v1 > 65535 || v2 > 65535 || v1 > v2) return false; + pf->from = (uint16_t)v1; + pf->to = (uint16_t)v2; } - else if (sscanf(s,"%u%c",&v1,&c)==1) + else if (sscanf(s, "%u%c", &v1, &c) == 1) { - if (v1>65535) return false; - pf->to=pf->from=(uint16_t)v1; + if (v1 > 65535) return false; + pf->to = pf->from = (uint16_t)v1; } else return false; // deny all case - if (!pf->from && !pf->to) pf->neg=true; + if (!pf->from && !pf->to) pf->neg = true; return true; } bool pf_is_empty(const port_filter *pf) @@ -357,12 +361,12 @@ bool pf_is_empty(const port_filter *pf) bool set_env_exedir(const char *argv0) { - char *s,*d; - bool bOK=false; + char *s, *d; + bool bOK = false; if ((s = strdup(argv0))) { if ((d = dirname(s))) - setenv("EXEDIR",s,1); + setenv("EXEDIR", s, 1); free(s); } return bOK; @@ -372,23 +376,23 @@ bool set_env_exedir(const char *argv0) static void mask_from_preflen6_make(uint8_t plen, struct in6_addr *a) { if (plen >= 128) - memset(a->s6_addr,0xFF,16); + memset(a->s6_addr, 0xFF, 16); else { uint8_t n = plen >> 3; - memset(a->s6_addr,0xFF,n); - memset(a->s6_addr+n,0x00,16-n); + memset(a->s6_addr, 0xFF, n); + memset(a->s6_addr + n, 0x00, 16 - n); a->s6_addr[n] = (uint8_t)(0xFF00 >> (plen & 7)); } } struct in6_addr ip6_mask[129]; void mask_from_preflen6_prepare(void) { - for (int plen=0;plen<=128;plen++) mask_from_preflen6_make(plen, ip6_mask+plen); + for (int plen = 0; plen <= 128; plen++) mask_from_preflen6_make(plen, ip6_mask + plen); } #if defined(__GNUC__) && !defined(__llvm__) -__attribute__((optimize ("no-strict-aliasing"))) +__attribute__((optimize("no-strict-aliasing"))) #endif void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restrict b, struct in6_addr * restrict result) { @@ -400,64 +404,116 @@ void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restric void str_cidr4(char *s, size_t s_len, const struct cidr4 *cidr) { char s_ip[16]; - *s_ip=0; + *s_ip = 0; inet_ntop(AF_INET, &cidr->addr, s_ip, sizeof(s_ip)); - snprintf(s,s_len,cidr->preflen<32 ? "%s/%u" : "%s", s_ip, cidr->preflen); + snprintf(s, s_len, cidr->preflen < 32 ? "%s/%u" : "%s", s_ip, cidr->preflen); } void print_cidr4(const struct cidr4 *cidr) { char s[19]; - str_cidr4(s,sizeof(s),cidr); - printf("%s",s); + str_cidr4(s, sizeof(s), cidr); + printf("%s", s); } void str_cidr6(char *s, size_t s_len, const struct cidr6 *cidr) { char s_ip[40]; - *s_ip=0; + *s_ip = 0; inet_ntop(AF_INET6, &cidr->addr, s_ip, sizeof(s_ip)); - snprintf(s,s_len,cidr->preflen<128 ? "%s/%u" : "%s", s_ip, cidr->preflen); + snprintf(s, s_len, cidr->preflen < 128 ? "%s/%u" : "%s", s_ip, cidr->preflen); } void print_cidr6(const struct cidr6 *cidr) { char s[44]; - str_cidr6(s,sizeof(s),cidr); - printf("%s",s); + str_cidr6(s, sizeof(s), cidr); + printf("%s", s); } bool parse_cidr4(char *s, struct cidr4 *cidr) { - char *p,d; + char *p, d; bool b; unsigned int plen; if ((p = strchr(s, '/'))) { - if (sscanf(p + 1, "%u", &plen)!=1 || plen>32) + if (sscanf(p + 1, "%u", &plen) != 1 || plen > 32) return false; cidr->preflen = (uint8_t)plen; - d=*p; *p=0; // backup char + d = *p; *p = 0; // backup char } else cidr->preflen = 32; - b = (inet_pton(AF_INET, s, &cidr->addr)==1); - if (p) *p=d; // restore char + b = (inet_pton(AF_INET, s, &cidr->addr) == 1); + if (p) *p = d; // restore char return b; } bool parse_cidr6(char *s, struct cidr6 *cidr) { - char *p,d; + char *p, d; bool b; unsigned int plen; if ((p = strchr(s, '/'))) { - if (sscanf(p + 1, "%u", &plen)!=1 || plen>128) + if (sscanf(p + 1, "%u", &plen) != 1 || plen > 128) return false; cidr->preflen = (uint8_t)plen; - d=*p; *p=0; // backup char + d = *p; *p = 0; // backup char } else cidr->preflen = 128; - b = (inet_pton(AF_INET6, s, &cidr->addr)==1); - if (p) *p=d; // restore char + b = (inet_pton(AF_INET6, s, &cidr->addr) == 1); + if (p) *p = d; // restore char return b; } + + +void msleep(unsigned int ms) +{ + struct timespec time = { + .tv_nsec = (ms % 1000) * 1000000, + .tv_sec = ms / 1000 + }; + nanosleep(&time, 0); +} + +#ifdef __linux__ +bool socket_has_notsent(int sfd) +{ + struct tcp_info tcpi; + socklen_t ts = sizeof(tcpi); + + if (getsockopt(sfd, IPPROTO_TCP, TCP_INFO, (char *)&tcpi, &ts) < 0) + return false; + if (tcpi.tcpi_state != 1) + return false; + size_t s = (char *)&tcpi.tcpi_notsent_bytes - (char *)&tcpi.tcpi_state; + if (ts < s) + return false; + return !!tcpi.tcpi_notsent_bytes; +} +bool socket_wait_notsent(int sfd, unsigned int delay_ms, unsigned int *wasted_ms) +{ + struct timespec tres; + unsigned int mtick; + + if (wasted_ms) *wasted_ms=0; + if (!socket_has_notsent(sfd)) return true; + + if (clock_getres(CLOCK_MONOTONIC,&tres)) + { + tres.tv_nsec = 10000000; + tres.tv_sec = 0; + } + mtick = (unsigned int)(tres.tv_sec*1000) + (unsigned int)(tres.tv_nsec/1000000); + if (mtick<1) mtick=1; + for(;;) + { + msleep(mtick); + if (wasted_ms) *wasted_ms+=mtick; + if (!socket_has_notsent(sfd)) return true; + if (delay_ms<=mtick) break; + delay_ms-=mtick; + } + return false; +} +#endif diff --git a/tpws/helpers.h b/tpws/helpers.h index ed84f5b..f8b0e3a 100644 --- a/tpws/helpers.h +++ b/tpws/helpers.h @@ -117,3 +117,9 @@ static inline const struct in6_addr *mask_from_preflen6(uint8_t preflen) { return ip6_mask+preflen; } + +void msleep(unsigned int ms); +#ifdef __linux__ +bool socket_has_notsent(int sfd); +bool socket_wait_notsent(int sfd, unsigned int delay_ms, unsigned int *wasted_ms); +#endif diff --git a/tpws/params.h b/tpws/params.h index acf9f4a..3cbcb81 100644 --- a/tpws/params.h +++ b/tpws/params.h @@ -104,6 +104,7 @@ struct params_s uint8_t proxy_type; bool no_resolve; bool skip_nodelay; + bool fix_seg; bool droproot; uid_t uid; gid_t gid; diff --git a/tpws/tpws.c b/tpws/tpws.c index 8d30561..eae386f 100644 --- a/tpws/tpws.c +++ b/tpws/tpws.c @@ -169,6 +169,9 @@ static void exithelp(void) " --uid=uid[:gid]\t\t\t; drop root privs\n" #if defined(__FreeBSD__) " --enable-pf\t\t\t\t; enable PF redirector support. required in FreeBSD when used with PF firewall.\n" +#endif +#if defined(__linux__) + " --fix-seg\t\t\t\t; fix segmentation failures at the cost of possible slowdown\n" #endif " --debug=0|1|2|syslog|@\t; 1 and 2 means log to console and set debug level. for other targets use --debug-level.\n" " --debug-level=0|1|2\t\t\t; specify debug level\n" @@ -635,8 +638,9 @@ void parse_params(int argc, char *argv[]) { "local-tcp-user-timeout",required_argument,0,0 }, // optidx=62 { "remote-tcp-user-timeout",required_argument,0,0 }, // optidx=63 { "mss",required_argument,0,0 }, // optidx=64 + { "fix-seg",no_argument,0,0 }, // optidx=65 #ifdef SPLICE_PRESENT - { "nosplice",no_argument,0,0 }, // optidx=65 + { "nosplice",no_argument,0,0 }, // optidx=66 #endif #endif { "hostlist-auto-retrans-threshold",optional_argument,0,0}, // ignored. for nfqws command line compatibility @@ -1228,8 +1232,11 @@ void parse_params(int argc, char *argv[]) exit_clean(1); } break; + case 65: /* fix-seg */ + params.fix_seg = true; + break; #ifdef SPLICE_PRESENT - case 65: /* nosplice */ + case 66: /* nosplice */ params.nosplice = true; break; #endif diff --git a/tpws/tpws_conn.c b/tpws/tpws_conn.c index a979bf0..daeb644 100644 --- a/tpws/tpws_conn.c +++ b/tpws/tpws_conn.c @@ -1244,7 +1244,21 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32 if (wr>0) conn->partner->twr += wr; break; } - +#ifdef __linux__ + if (params.fix_seg) + { + unsigned int wasted; + if (!socket_wait_notsent(conn->partner->fd, 20, &wasted)) + DLOG_ERR("WARNING ! segmentation failed\n"); + if (wasted) + VPRINT("WARNING ! wasted %u ms to fix segmenation\n", wasted); + } + else + { + if (socket_has_notsent(conn->partner->fd)) + DLOG_ERR("WARNING ! segmentation failed\n"); + } +#endif from = to; } } From 5207104c06d2977a735eca015e0abe9aaeb01080 Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 18 Nov 2024 23:19:11 +0300 Subject: [PATCH 8/9] readme: fix-seg info --- docs/readme.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/readme.md b/docs/readme.md index 712f968..d4c7255 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -812,6 +812,7 @@ tpws - это transparent proxy. --skip-nodelay ; не устанавливать в исходящих соединения TCP_NODELAY. несовместимо со split. --local-tcp-user-timeout= ; таймаут соединений client-proxy (по умолчанию : 10 сек, 0 = оставить системное значение) --remote-tcp-user-timeout= ; таймаут соединений proxy-target (по умолчанию : 20 сек, 0 = оставить системное значение) +--fix-seg ; исправлять неудачи tcp сегментации ценой задержек для всех клиентов и замедления --split-pos=N|-N|marker+N|marker-N ; список через запятую маркеров для tcp сегментации --split-any-protocol ; применять сегментацию к любым пакетам. по умолчанию - только к известным протоколам (http, TLS) @@ -877,6 +878,15 @@ tpws, как и nfqws, поддерживает множественную се указанным сплит позициям. Другие ОС в этом вопросе ведут себя более предсказуемо. Спонтанного обьединения замечено не было. Поэтому не стоит злоупотреблять сплитами и в особенности мелкими соседними пакетами. +Как показывается практика, проблемы могут начаться , если количество сплит позиций превышает 8. +При неудаче сегментации будет выводиться сообщение `WARNING ! segmentation failed`. +Если вы его видите, это повод снизить количество сплит позиций. +Если это не вариант, есть параметр `--fix-seg`. Он позволяет подождать завершение отсылки перед отправкой следующей части. +Но этот вариант ломает модель асинхронной обработки событий. Пока идет ожидание, все остальные соединения не обрабатываются +и кратковременно подвисают. На практике это может быть совсем небольшое ожидание - менее 10 мс. +И производится оно только , если происходит split, и в ожидании есть реальная необходимость. +В высоконагруженных системах данный вариант не рекомендуется. Но для домашнего использования может подойти, и вы эти задержки даже не заметите. + tpws работает на уровне сокетов, поэтому длинный запрос, не вмещающийся в 1 пакет (TLS с kyber), он получает целым блоком. На каждую сплит часть он делает отдельный вызов `send()`. Но ОС не сможет отослать данные в одном пакете, если размер превысит MTU. В случае слишком большого сегмента ОС дополнительно его порежет на более мелкие. Результат должен быть аналогичен nfqws. From fc2d511d78fc929c2fa58074a781349ac079b749 Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 18 Nov 2024 23:19:51 +0300 Subject: [PATCH 9/9] update changes.txt --- docs/changes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changes.txt b/docs/changes.txt index 8721ddf..d0dbed3 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -380,6 +380,7 @@ nfqws,tpws: set EXEDIR env var to use in @config (won't work for stadalone winws dvtws: set random/increasing ip_id value in generated packets mdig: fixed parsing of DNS reply in windows (stdin is opened as text, not binary) tpws: support compile for android NDK api level >= 21 (Android 5.0) +tpws: --fix-seg segmentation fixer repo: build for android NDK api level 21 (Android 5.0) install_easy: support for APK package manager in openwrt blockcheck: removed ignore CA question