Zapret: what's new

Попал мне девайс Irbis TZ797 на android 8.1
Если запускать обработчик очереди NFQUEUE на пакеты с интерфейса с сотовой сети, при этом кабель USB не воткнут (нет зарядки), то ядро очень быстро частично виснет, так что перестают работать тач и физические кнопки. При этом в броузере может продолжать анимироваться картинка.
Выяснил, что проблема связана с UID. По умолчанию nfqws назначает себе UID 0x7FFFFFFF. Видимо, в более новых ведроидах (на 5 точно такого не было), процессы с такими UID могут ложиться в suspend, что блокирует обработку очереди , а на это завязаны вызовы ядра. И получаем то, что видим.
Лекарство : nfqws --uid 1
Перепробовал несколько вариантов из низких uid. Все не виснут.
Для nfqws не нужны специальные uid 3003,3004,3005.
Дело в том, что nfqws назначает себе привилегии cap_net_raw, cap_net_admin. Это необходимо для работы с nfqueue и raw sockets. Для android это является одним из разрешающих условий для доступа к сети, вне зависимости от uid.

Добавлена возможность усекания пакетов в режиме udplen (отрицательный increment). Более, чем до 1 байта, усечение не работает.
Может быть полезно, когда прога добавляет паддинг в пакет, который ни на что не влияет, не проверяется, однако DPI проверяет размер пакета на минимальную длину.
Усечение пакетов от стандартных программ может помочь.

Поддержка множественных листов включения и исключения.
Альтернативой ipset является использование tpws или nfqws со списком доменов.
Оба демона принимают неограниченное количество листов include (–hostlist) и exclude (–hostlist-exclude).
Все листы одного типа обьединяются, и таким образом остаются только 2 листа.
Прежде всего проверяется exclude list. При вхождении в него происходит отказ от дурения.
Далее при наличии include list проверяется домен на вхождение в него. При невхождении в список отказ от дурения.
Пустой список приравнивается к его отсутствию.
В иных случаях происходит дурение.
Нет ни одного списка - дурение всегда.
Есть только exclude список - дурение всех, кроме.
Есть только include список - дурение только их.
Есть оба - дурение только include, кроме exclude.
В системе запуска это обыграно следующим образом.
Присутствуют 2 include списка :
ipset/zapret-hosts-users.txt.gz или ipset/zapret-hosts-users.txt
ipset/zapret-hosts.txt.gz или ipset/zapret-hosts.txt
и 1 exclude список
ipset/zapret-hosts-users-exclude.txt.gz или ipset/zapret-hosts-users-exclude.txt
При режиме фильтрации MODE_FILTER=hostlist система запуска передает nfqws или tpws все листы, файлы которых присутствуют.
Если вдруг листы include присутствуют, но все они пустые, то работа аналогична отсутствию include листа.
Файл есть, но не смотря на это дурится все, кроме exclude.
Если вам нужен именно такой режим - не обязательно удалять zapret-hosts-users.txt. Достаточно сделать его пустым.
Поддомены учитываются автоматически. Например, строчка “ru” вносит в список “*.ru”. Строчка “*.ru” в списке не сработает.
Список доменов РКН может быть получен скриптами ipset/get_reestr_hostlist.sh или ipset/get_antizapret_domains.sh - кладется в ipset/zapret-hosts.txt.gz.
При фильтрации по именам доменов демон должен запускаться без фильтрации по ipset.
tpws и nfqws решают нужно ли применять дурение в зависимости от хоста, полученного из протокола прикладного уровня (http, tls, quic).

Обнаружилось, что в openwrt на базе nftables на самой типичной конфигурации реального роутера (bridge lan/wlan), не срабатывала схема включения hardware offload. Причина - проверка, чтобы все компоненты моста поддерживали hw offload. wlan его не поддерживает. Сделал, чтобы из моста выбирались только поддерживающие интерфейсы, и только они добавлялись в flowtable.

Интересная проблема обнаружилась с hardware flow offloading.
Стабильная версия openwrt 22.03 имеет ядро 5.10.
Только начиная с ядра 5.13 автоматически определяются физические интерфейсы под мостами, pppoe и vlan. До этого надо было как-то вручную определять порты моста, базовый ethernet device под vlan или pppoe, и только их совать в flowtable.
Для мостов и VLAN особых проблем нет. /sys/class/net/<iface_name> содержит ссылки lower_* на нужные интерфейсы. common/nft.sh из zapret умеет с таким работать. В случае hardware offload нижние девайсы определяются автоматом и засовываются в flowtable. Скрипт пытается туда засунуть и верхние, и нижние девайсы. Что проглотит - то и хорошо.

Но в случае pppoe не так все просто. Видимо нет очевидного способа определить что лежит под pppoe. В fw4 девайс определяется через вызов к netifd
“ifstatus wan” возвращает
“l3-device”: “pppoe-wan”
“device”: “eth0”
он и берется для засовывания в flow table

Пришлось и мне сделать нечто подобное. Но это работает только в openwrt.
Если вдруг каким-то образом у вас старенькая традиционная linux система с hw offload, которая не умеет засовывать в hw offload pppoe, то вам придется самому вписать добавление в flowtable физического интерфейса. Например, через hook. Впрочем, это весьма маловероятный случай
Лучшим решением будет проапгрейдить ядро.

Другое изменение : для традиционных linux и macos в конфиге теперь можно использовать отдельный список IFACE_WAN6 для исходящих ipv6 интерфейсов.

В правила nft внесен тестовый счетчик для проверки flow offload

nft list chain inet zapret  flow_offload

table inet zapret {
        chain flow_offload {
                oifname @wanif tcp dport 80 ip daddr @zapret ip daddr != @nozapret return comment "direct flow offloading exemption"
                oifname @wanif tcp dport 443 ct original packets 1-4 ip daddr @zapret ip daddr != @nozapret return comment "direct flow offloading exemption"
                meta l4proto { tcp, udp } flow add @ft
                meta l4proto { tcp, udp } counter packets 1227 bytes 75527 comment "if offload works here must not be too much traffic"
        }
}

Посмотрите текущий counter. Запустите speedtest. counter не должен увеличиваться сильно. Не должно быть никаких увеличивающихся мегабайтов.
Если они там есть - offload не работает.
Посмотрите какие интерфейсы занесены в flowtable.
Для software offload должны быть высокоуровневые интерфейсы (мосты, pppoe, vpn, …). Для hardware могут потребоваться лежащие под ними низкоуровневые интерфейсы (lan1, lan2, lan3, lan4). Общее правило - должны быть и входящие, и исходящие интерфейсы.
Если там их нет - значит что-то работает не так.

nft list flowtable inet zapret ft
table inet zapret {
        flowtable ft {
                hook ingress priority filter - 1
                devices = { br-lan, wan }
        }
}

Решил сделать свой сервис по обработке выгрузки реестра РКН.
Главная мотивация - отсутствие ipv6 в решениях от antifilter.network
Для получения листов используйте get_reestr_preresolved.sh и get_reestr_preresolved_smart.sh
Подробности тут https://github.com/bol-van/zapret/issues/130

В mdig добавлены опции --log-resolved и --log-failed
для создания логов успешных и провальных доменов

На http://list.nethub.fi появились листы проверенных доменов из реестра РКН, которые ресолвятся по ipv4, ipv6 и “ipv4 или ipv6” .
Как оказалось, примерно половина доменов из реестра не ресолвятся никак, фактические являясь трэшем

Блокировка QUIC все больше начала оформляться у провайдеров во что-то законченно-логичное вместо хаотических попыток что-то там заткнуть или заткнуть вообще весь протокол или порт.

На большинстве провайдеров сделана блокировка по SNI (идет расшифровка на DPI) как на ipv4, так и на ipv6, по списку заблокированных доменов.
Однако, модули распознавания QUIC initial видимо еще старые и кривые, потому могут сечь, например, curl с nghttp3 и chromium, но не сечь curl с quiche или firefox, потому блокировка может быть не видна с первого взгляда.
Как правило блокировки QUIC по SNI - stateful, поскольку анализировать каждый пакет в соединении чревато гигабитным флудом и залеганием DPI в bypass. Расшифровка QUIC - непростая операция. Это значит, что если DPI в сеансе UDP первым пакетом не видит ничего осудительного, то все остальное не анализируется.

Итак, QUIC внесен полностью в основные скрипты и инсталятор с поддежкой фильтрации по ipset/hostlist, аналогично http/https.
Особого смысла менять режим десинхронизации нет. Просто выбираете MODE=nfqws и отвечаете yes или no на “enable QUIC”. Дальше все само. По понятным причинам QUIC работает только с nfqws и не работает с tpws.

К сожалению, пока внести автопроверку на QUIC в blockcheck не представляется возможным, поскольку поддержка quic экспериментальна, и стандартные курлы http3 не поддерживают.
Потому можно посмотреть по F12 в броузере на facebook.com перешел ли он на HTTP3 после включения обхода.
Или лучше всего через curl --http3-only или посмотреть в wireshark udp port 443.
Прекомпиленые бинарики с поддержкой http3 можно загуглить для linux x86_64 и windows.
Если не обходится - выключайте.

Неплохой проект ByeDPI
Работает как прокси и не требует дополнительных привилегий в системе, тем не менее реализует режимы disorder2 и fake (последний может быть ненадежно).
Тем не менее содержит очень интересные идеи, до которых я бы раньше не догадался. Спасибо разработчику.
Вариант --disorder перенесен в tpws, обновлен blockcheck.
tpws имеет преимущества : работает в MacOS и не требует привилегий в режиме socks
Режим работает не так, как хотелось бы, в FreeBSD, OpenBSD и Windows вследствие особенностей ядра.

От переноса fake в tpws пока воздержался. Необходимо тестировать как он себя поведет на разных системах, поскольку используются потенциально ненадежные хаки.

Добавил в nfqws определение wireguard handshake initiation.
По крайней мере с тем блоком, что был недавно включен на ТСПУ, больше не потребуется --dpi-desync-any-protocol.
Что касается openvpn, то глубоко не изучал особенности его блокировки. Включат опять - будут смотреть имеет ли смысл добавлять определение протокола в nfqws

Заодно проверил работает ли увеличение длины пакета с wireguard. Не работает. Wg отбрасывает эти пакеты, так что --dpi-desync=udplen для wg не подходит. А было бы здорово, если бы работал. Диссектор из NDPI четко проверяет udplen==148

Сегодня стали блочить DHT.
Блокируются исходящие udp пакеты с длиной 101…399 , начинающиеся с “d1” и заканчивающиеся на “e”, с номерами src port 1025…65536. <=1024 не блокируются.
Блокировка stateful, но только в одну сторону. После пробивки с одной стороны аналогичные пакеты с другой стороны ходить не начинают.
Возможно, это связано с несколькими ТСПУ на пути : у исходящего провайдера, у входящего провайдера и на магистрали.
Некоторые DHT все же доходят с разных IP адресов. Предполагаю, что не на всех путях установлен ТСПУ, который считает пакет исходящим. Бывает не доходит даже из-за бугра, где нет ТСПУ (но может быть на магистрали).

Создан custom script custom-nfqws-dht4all.

nftables фильтр : ct original packets 1 meta length 109-407 meta l4proto udp @th,64,16 0x6431
iptables фильтр : -p udp -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:1 -m length --length 109:407 -m u32 --u32 ‘0>>22&0x3C@8>>16=0x6431’
ip6tables фильтр : -p udp -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:1 -m length --length 109:407 -m u32 --u32 ‘48>>16=0x6431’
nfqws : --dpi-desync=fake --dpi-desync-any-protocol --dpi-desync-cutoff=d2 --dpi-desync-ttl=5

(длина считается вместе с udp заголовком 8 байт)

Возможно, ситуация связана с выборами, и скоро отключат, но надо быть готовым

Сделал поддержку параметра nfqws --dpi-desync-udplen-pattern.
Можно задать файл или hex строку, которой добиваются udp пакеты при режиме десинхронизации udplen.
Теперь везде, где nfqws раньше читал файл, можно указывать как имя файла, так и HEX строку, которая начинается с 0x

Сделал распознавание протокола DHT. Распознается как udp, начинающийся с d1, кончающийся e (соответствует фильтру ТСПУ).
Новый режим десинхронизации tamper.
Этот режим предназначен для корректной модификации известных пейлоадов, чтобы DPI их не распознавал, но пейлоад оставался корректным и нес ту же смысловую нагрузку.
Для DHT это вставление строки “2:001:x” после “d” в начале. Вставляется ничего не значащий элемент в bencode dictionary с длиной ключа 2, вместо 1. Таким образом dht начинается с d2.

Этот метод может помочь, если вдруг сделают блокировку stateless. fake против stateless не работает

Нашел средство обьединять подсети.
ip2net работает только с отдельными IP адресами, пропуская подсети как есть.
Это позволило существенно сократить размер листов с list.nethub.fi
Для роутеров с nft и обьемом памяти <256 Mb это крайне актуально

Принципиально новый способ дурения через tpws : --tlsrec
Разбиение ClientHello на уровне TLS Record. Одну TLS record нарезаем на две в одном TCP сегменте (при желании потом и это можно разделить на 2 tcp сегмента через --split-pos и перепутать порядок пакетов --disorder).
Режем или прямо по хосту в SNI, что исключает бинарный поиск паттерна без парсинга протокола, или на указанной байтовой позиции.
Говорят, работает даже на китайском фаерволе.

Метод хороший, но ломает различные ddos защиты, потому где-то 10% сайтов обломаются. gosuslugi не работают. Без фильтра использовать нецелесообразно.

В России не работает на TLS 1.2, поскольку rdp-шный DPI еще смотрит на сертификат из ответа сервера TLS ServerHello. Это можно обойти через комбо с nfqws --wssize.

Удивительно, но многие серьезные сайты все еще не поддерживают TLS 1.3
Среди них сбер, другие банки, гос услуги, mail.ru
Среди заблокированных точно не поддерживает lostfilm.tv

Вышел недавно openwrt 23.05.
Принципиальных изменение по сравнению с 22.03.05 в нем не так много.
Но есть одно, касающееся zapret.
openwrt перешел по умолчанию на mbedtls вместо wolfssl. mbedtls не поддерживает TLS1.3
Следовательно при использовании штатного curl не работают методы проверки TLS 1.3 в blockcheck. Сам blockcheck предупреждает, что libcurl не поддерживает TLS1.3.

Что можно сделать для решения этой проблемы :

  1. Взять статический бинарик с GitHub - bol-van/bins: precompiled static binaries for android
    и заменить им родной. если у вас почти нет места, можно записать его в какую-нибудь директорию в /tmp, затем добавить в PATH в начало эту директорию. временное решение только для проверки blockcheck
  2. Пересобрать openwrt с libcurl, базированном на другой tls library. Например, openssl. Заодно поискать какие пакеты зависят от wolfssl или mbedtls и заменить там preferred tls library. Так можно попытаться вообще убрать mbedtls, чтобы он не занимал места.
  3. Не пересобирать весь openwrt, а взять SDK одноименной версии, собрать libcurl с другой TLS library, взять из bin ipk пакеты только самого libcurl и его зависимости , установить на openwrt, замещая текущие. Проверить, что ничего не сломалось.
  4. Не запускать blockcheck на роутере, а вместо этого использовать виртуалку с linux, отключив предварительно zapret на роутере. Не забыть, что найденные TTL следует уменьшить на 1, чтобы стратегия была корректной для рутера.

TLS1.2 - более жесткий случай. Если какие-то стратегии работают на 1.2, они будут работать и на 1.3.
Проверка 1.3 нужна только, если не удалось найти рабочий вариант для 1.2. Чтобы хоть как-то работало.

Режим фильтрации autohostlist

Этот режим позволяет проанализировать как запросы со стороны клиента, так и ответы от сервера.
Если хост еще не находится ни в каких листах и обнаруживается ситуация, похожая на блокировку,
происходит автоматическое добавление хоста в список autohostlist как в памяти, так и в файле.
nfqws или tpws сами ведут этот файл.
Чтобы какой-то хост не смог попась в autohostlist используйте hostlist-exclude.
Если он все-же туда попал - удалите запись из файла вручную и перезапустите tpws/nfqws
или дайте им сигнал HUP, чтобы они перечитали листы.
tpws/nfqws сами назначают владельцем файла юзера, под которым они работают после сброса привилегий, чтобы иметь возможность обновлять лист.

В случае nfqws данный режим требует перенаправления в том числе и входящего трафика.
Крайне рекомендовано использовать ограничитель connbytes, чтобы nfqws не обрабатывал гигабайты.
По этой же причине не рекомендуется использование режима на BSD системах. Там нет фильтра connbytes.

Как вообще могут вести себя DPI, получив “плохой запрос” и приняв решение о блокировке :

  1. Зависание : просто отмораживается, блокируя прохождение пакетов по TCP каналу.
  2. RST : отправляет RST клиенту и/или серверу
  3. Редирект : (только для http) отправляет редирект на сайт-заглушку
  4. Подмена сертификата : (только для https) полный перехват TLS сеанса с попыткой всунуть что-то
    свое клиенту. Применяется нечасто, поскольку броузеры на такое ругаются.

nfqws и tpws могут сечь варианты 1-3, 4 они не распознают.
Всилу специфики работы с отдельными пакетами или с TCP каналом tpws и nfqws распознают эти ситуации по-разному.
Что считается ситуацией, похожей на блокировку :

  1. [nfqws] Несколько ретрансмиссий первого запроса в TCP сеансе, в котором имеется hostname.
  2. [nfqws,tpws] RST, пришедший в ответ на первый запрос с хостом.
  3. [nfqws,tpws] HTTP редирект, пришедший в ответ на первый запрос с хостом, на глобальный адрес с доменом 2 уровня, не совпадающим с доменом 2 уровня оригинального запроса.
  4. [tpws] закрытие соединения клиентом после отправки первого запроса с хостом, если не было на него ответа со стороны сервера. Это обычно случается по таймауту, когда нет ответа (случай “зависание”).

Чтобы снизить вероятность ложных срабатываний, имеется счетчик ситуаций, похожих на блокировку.
Если за определенное время произойдет более определенного их количества, хост считается заблокированным и заносится в autohostlist. По нему сразу же начинает работать стратегия по обходу блокировки.

На практике работа с данным режимом выглядит так.
Первый раз пользователь заходит на сайт и получает заглушку, сброс соединения или броузер подвисает, вываливаясь по таймауту с сообщением о невозможности загрузить страницу.
Надо долбить F5, принуждая броузер повторять попытки. После некоторой попытки сайт
начинает работать, и дальше он будет работать всегда.

С этим режимом можно использовать техники обхода, ломающие значительное количество сайтов.
Если сайт не ведет себя как заблокированный, значит обход применен не будет.
В противном случае терять все равно нечего.
Однако, могут быть временные сбои сервера, приводящие к ситуации, аналогичной блокировке.
Могут происходит ложные срабатывания. Если такое произошло, стратегия может начать ломать
незаблокированный сайт. Эту ситуацию, увы, придется вам контролировать вручную.
Заносите такие домены в ipset/zapret-hosts-user-exclude.txt, чтобы избежать повторения.

Скрипты zapret ведут autohostlist в ipset/zapret-hosts-auto.txt.
install_easy.sh при апгрейде zapret сохраняет этот файл.
Режим autohostlist включает в себя режим hostlist.
Можно вести ipset/zapret-hosts-user.txt, ipset/zapret-hosts-user-exclude.txt.

Удалось ускорить blockcheck. Для этого требуется sleep с возможностью спать меньше секунды.
В openwrt этого по умолчанию нет. На новых openwrt, где есть ucode, удалось извернуться. Для более старых требуется установка coreutils-sleep. Внесено предолжение по установке coreutils-sleep в install_easy.sh в случае необходимости

Собственно, зачем нужен микро sleep.
Проверка осуществляется так. Сначала устанавливается общее правило перенаправления трафика, которое не меняется на время тестирования всего блока параметров.
При тестировании каждого параметра запускается nfqws/dvtws или tpws, параметр тестируется , затем nfqws/dvtws или tpws прибиваются.
Им требуется какое-то время для инициализации. Если послать запрос раньше, в случае tpws получим полный облом - connection refused, в случае nfqws/dvtws если система достаточно медленная, получим отброс первого пакета и ретрансмиссию. Это все ведет к задержкам при тестировании каждого параметра. На деле ждать 1 секунду избыточно. Вполне достаточно 100 мсек. Но вот беда, в openwrt бизибокс, собранный без поддержки float sleep и без usleep, и нечем вызвать syscall nanosleep().
Благо, ucode все-таки это может. Есть функция запуска процесса с таймаутом. Но ucode появился вместе с fw4 в 22-й версии openwrt. В более старых нет. Потому там надо устанавливать coreutils-sleep, который умеет спать с дробями секунды. Благо, он весит всего 40-50K. Зато блокчек в разы шустрее пробегает.
Используется 4 варианта поспать мало. Если никакой из них не сработал - спим 1 секунду

В tpws и nfqws добавлена возможность логгинга положительных решений по autohostlist.
Можно разобраться когда и по какой причине что-то попало в лист.
Через скрипты проведено как переменная AUTOHOSTLIST_DEBUGLOG.
При значении “1” ведется лог в ipset/zapret-hosts-auto-debug.log

Пример
09.11.2023 12:42:39 : dostfilms.site : incoming RST
09.11.2023 12:42:39 : dostfilms.site : fail counter 1/2
09.11.2023 12:42:39 : dostfilms.site : incoming RST
09.11.2023 12:42:39 : dostfilms.site : fail counter 2/2
09.11.2023 12:42:39 : dostfilms.site : adding
09.11.2023 12:43:30 : sun9-33.userapi.com : incoming RST
09.11.2023 12:43:30 : sun9-33.userapi.com : fail counter 1/2
09.11.2023 12:43:31 : sun9-75.userapi.com : incoming RST
09.11.2023 12:43:31 : sun9-75.userapi.com : fail counter 1/2
09.11.2023 12:43:31 : sun9-61.userapi.com : incoming RST
09.11.2023 12:43:31 : sun9-61.userapi.com : fail counter 1/2
09.11.2023 14:20:04 : sun9-46.userapi.com : incoming RST
09.11.2023 14:20:04 : sun9-46.userapi.com : fail counter 1/2
09.11.2023 14:20:04 : sun9-28.userapi.com : incoming RST
09.11.2023 14:20:04 : sun9-28.userapi.com : fail counter 1/2
09.11.2023 14:20:04 : sun9-5.userapi.com : incoming RST
09.11.2023 14:20:04 : sun9-5.userapi.com : fail counter 1/2
09.11.2023 14:21:53 : sun9-33.userapi.com : incoming RST
09.11.2023 14:21:53 : sun9-33.userapi.com : fail counter 1/2
09.11.2023 14:25:08 : hd-rezka.pro : redirect to another domain
09.11.2023 14:25:08 : hd-rezka.pro : fail counter 1/2
09.11.2023 14:25:11 : hd-rezka.pro : redirect to another domain
09.11.2023 14:25:11 : hd-rezka.pro : fail counter 2/2
09.11.2023 14:25:11 : hd-rezka.pro : adding
09.11.2023 14:25:43 : films1080.best : redirect to another domain
09.11.2023 14:25:43 : films1080.best : fail counter 1/2
09.11.2023 14:25:44 : films1080.best : redirect to another domain
09.11.2023 14:25:44 : films1080.best : fail counter 2/2
09.11.2023 14:25:44 : films1080.best : adding
09.11.2023 14:29:48 : tv1.lordfilm.black : redirect to another domain
09.11.2023 14:29:48 : tv1.lordfilm.black : fail counter 1/2
09.11.2023 14:29:49 : tv1.lordfilm.black : redirect to another domain
09.11.2023 14:29:49 : tv1.lordfilm.black : fail counter 2/2
09.11.2023 14:29:49 : tv1.lordfilm.black : adding

Из примера видно, что разные поддомены незаблокированного userapi.com периодически дают RST. Это можно списать на перегруженность серверов. Устойчивой картины нет, поэтому порог срабатывания 2 фейла за 60 секунд не достигается. Домены не заносятся в лист.
Другие домены дают устойчивую картину, потому заносятся в лист
Порогами срабатывания можно играться

Другой пример
09.11.2023 12:41:23 : www.kinozone.online : tcp retrans threshold reached
09.11.2023 12:41:23 : www.kinozone.online : fail counter 1/2
09.11.2023 12:41:26 : www.kinozone.online : tcp retrans threshold reached
09.11.2023 12:41:26 : www.kinozone.online : fail counter 2/2
09.11.2023 12:41:26 : www.kinozone.online : adding
09.11.2023 12:42:03 : hd-rezka.pro : tcp retrans threshold reached
09.11.2023 12:42:03 : hd-rezka.pro : fail counter 1/2
09.11.2023 14:24:05 : hd-rezka.pro : tcp retrans threshold reached
09.11.2023 14:24:05 : hd-rezka.pro : fail counter 1/2
09.11.2023 14:24:06 : hd-rezka.pro : tcp retrans threshold reached
09.11.2023 14:24:06 : hd-rezka.pro : fail counter 2/2
09.11.2023 14:24:06 : hd-rezka.pro : adding
09.11.2023 14:28:59 : kinogo.io : tcp retrans threshold reached
09.11.2023 14:28:59 : kinogo.io : fail counter 1/2
09.11.2023 14:29:00 : kinogo.io : tcp retrans threshold reached
09.11.2023 14:29:00 : kinogo.io : fail counter 2/2
09.11.2023 14:29:00 : kinogo.io : adding

Из этого примера видно, что провайдер не шлет RST, а отмораживается на плохие домены. Сеансы виснут. Клиент пытается слать ретрансмиссии, не получая ACK. Достигнув порога в 3 ретрансмиссии происходит срабатывание события “похоже на блокировку”. 2 раза так подвисли за 60 секунд - занесли в лист
К hd-rezka.pro пробовали обращаться в 12:42. Страница подвисла, но передалбливаться не стали. Долбанулись только 1 раз и расслабились. Порог достигнут не был. Потом в 14:24 начали долбиться более настойчиво, что вызвало срабатывание.

Обратили мое внимание на штуку, о которой знать не знал.
chrome://flags kyber
Это постквантовая штука для TLS , которая раздувает ClientHello до 2 TCP frames.
Следовательно, nfqws ломается. Он в принципе не занимается реассемблингом TCP frames. А DPI занимается и корректно блокирует.
Если вдруг хромисты начнуть дефолтить эту фичу, придется доделывать nfqws

Кстати, это так же пинок в адрес GoodbyeDPI
У него та же проблема. Или нет ? Если вдруг он не парсит целую TLS запись, а ищет паттерн, то ему все равно.
НО. Chrome засовывает SNI в разные места ClientHello. Попадает то в 1-й пакет, то во 2-й. 2-й пакет вообще не содержит сколько нибудь вразумительного начала.

Предполагаемый способ решения проблемы в nfqws.

  1. Разрешить partial ClientHello. Это поможет проанализировать 1-й пакет, даже если он порезан на середине. Если SNI находится там, вопрос решен
  2. Если все-же SNI находится не в 1-м сегменте, то придется делать реассемблинг до тех пор, пока не обнаружится SNI. И тогда делать desync не на 1-й пакет , а на тот, где находится SNI. Ведь это и есть цель. Делать на все не есть хорошо, потому что есть фильтры hostlist, и вообще неясно есть ли SNI в TLS сообщении. Но что тогда ? Запоминать все пакеты, пока не будет возможность принять решение, и потом их выплевывать ? Можно, хотя и сложновато

в tpws этой проблемы ожидаемо нет

Сделана поддержка работы с TLS ClientHello, размазанными на несколько пакетов

РЕАССЕМБЛИНГ TCP
nfqws поддерживает реассемблинг некоторых видов tcp запросов.
На текущий момент это TLS ClientHello. Он бывает длинным, если в chrome включить пост-квантовую
криптографию tls-kyber, и занимает как правило 2 пакета.
chrome рандомизирует фингерпринт TLS. SNI может оказаться как в начале, так и в конце, то есть
попасть в 1 или 2 пакет. stateful DPI обычно реассемблирует запрос целиком, и только потом
принимает решение о блокировке.
nfqws реагирует десинхронизацией на каждый пакет из TLSClientHello, если задана опция
–dpi-desync-skip-nosni=0. В противном случае десинхронизация идет на сам пакет,
включающий SNI, и все последующие.

РЕАССЕМБЛИНГ QUIC
tls-kyber может так же размазываться по 2 пакетам QUIC Initial.
Пока их реассемблинг не реализован, поскольку русский DPI не реагирует на такие пакеты.
Идет десинхронизация полных hello в одном пакете и частичных hello, где SNI попал в 1-й пакет.
Можно только гадать как будут рубить сеанс, если вдруг блокировщики реализуют поддержку подобных hello.
Если, допустим, они станут буферизировать 2 пакета, и после 2 рубить сеанс, если им не понравился SNI, то можно выплюнуть что-то между 1 и 2. Если они будут сечь долго сеанс QUIC на предмет продолжения initial, то плеваться придется между множеством пакетов. Если же и это не сработает, значит придется дурить сразу на 1 пакете, чтобы они не распознали начало QUIC протокола и не стали ничего высекать из последующих пакетов. Но это потребует буферизации уже на стороне nfqws.
Так что писанина кода ожидается разная

Так же обновлена логика детекта tcp ретрансмиссий.
По мере реконструкции TLS запроса выясняется диапазон tcp sequence numbers, покрывающий запрос.
Ретрансмиссиями запроса считаются только повторные передачи из этого диапазона.
При обнаружении пакета вне диапазона, счетчик ретрансмиссий немедленно завершается.
Обнаружение RST или http редирект теперь жестко завязано на sequence=1 со стороны сервера.
То есть реакция только на RST сразу и молча, либо передачу http redirect сразу.
Все, что потом, не трогается, и реакции по auto hostlist нет