nfqws2-strategies

star 50

Полный справочник по nfqws2 / zapret2 в проекте zapret-gui (роутеры Keenetic на Entware). Использовать при любых запросах о: стратегиях nfqws2/zapret2, каталогах catalogs/*, сканере стратегий (strategy_scanner), сборке аргументов (nfqws_manager, strategy_builder, blob_registry, lua_manager), firewall / NFQUEUE-правилах (core/firewall.py), lua-desync функциях, blob'ах, hostlist'ах, оркестраторах (circular), blockcheck2-интеграции и диагностике «стратегия не работает / 0% успешности». Источник истины — bol-van/zapret2 (docs/manual.md, lua/zapret-antidpi.lua), привязка — наш код в core/.

avatarDD By avatarDD schedule Updated 6/8/2026

name: nfqws2-strategies description: >- Полный справочник по nfqws2 / zapret2 в проекте zapret-gui (роутеры Keenetic на Entware). Использовать при любых запросах о: стратегиях nfqws2/zapret2, каталогах catalogs/*, сканере стратегий (strategy_scanner), сборке аргументов (nfqws_manager, strategy_builder, blob_registry, lua_manager), firewall / NFQUEUE-правилах (core/firewall.py), lua-desync функциях, blob'ах, hostlist'ах, оркестраторах (circular), blockcheck2-интеграции и диагностике «стратегия не работает / 0% успешности». Источник истины — bol-van/zapret2 (docs/manual.md, lua/zapret-antidpi.lua), привязка — наш код в core/.

nfqws2 / zapret2 — справочник для zapret-gui

Этот файл — единый источник истины о том, как nfqws2 (zapret2) реально работает и как с ним правильно обращаться в проекте zapret-gui. Читать перед тем, как трогать сборку аргументов, сканер стратегий, firewall, каталоги или объяснять пользователю «почему не работает».

Источники истины (в порядке убывания авторитета):

  1. bol-van/zapret2 — движок nfqws2: docs/manual.md, lua/zapret-antidpi.lua (комментарий перед каждой функцией = спецификация её аргументов), lua/zapret-auto.lua, lua/zapret-lib.lua, lua/zapret-obfs.lua, init.d/custom.d.examples.linux/, config.default.
  2. nfqws2 -? (v0.9.5.2) — дамп опций CLI, §3 ниже.
  3. nfqws2-keenetic — поведенческий эталон под Keenetic (НЕ путать пути — у них своя раскладка S51nfqws2 / /opt/etc/nfqws2/…; у нас её НЕТ).
  4. Наш код в core/nfqws_manager.py, strategy_scanner.py, strategy_builder.py, blob_registry.py, firewall.py, diagnostics.py, blockcheck2.py, autostart_manager.py, lua_manager.py, hostlist_manager.py, catalog_loader.py, config_manager.py.

1. nfqws2 vs nfqws1 — ключевые отличия

nfqws2 не имеет хардкод-стратегий. Параметров --dpi-desync=fake,split2, --dpi-desync-split-pos=, --dpi-desync-fooling= больше нет — это синтаксис старого nfqws1 / winws. Всё дурение живёт в Lua-скриптах, а CLI лишь загружает их и вызывает функции.

Что изменилось:

  • Ядро nfqws2 (dvtws2 на BSD, winws2 на Windows) — только перехват и диссекция; «дурение» вынесено в Lua. Это даёт радикально большую гибкость.
  • Параметры --dpi-desync-* заменены на:
    • --lua-desync=<function>[:arg1[=val1]:argN[=valN]] — вызов Lua-инстанса (десинк-функции из zapret-antidpi.lua или своей);
    • --lua-init=@file.lua | <lua_code> — однократная инициализация (можно несколько раз, порядок сохраняется, поддержан .gz);
    • --blob=<name>:[+ofs]@file | 0xHEX — загрузка двоичных данных в Lua-переменную.
  • Понятие payload type (tls_client_hello, http_req, quic_initial, …) пришло на смену «протоколу соединения» в фильтрах.
  • Десинхронизации больше не «фаза 1 / фаза 2». Каждый --lua-desync — отдельный инстанс, выполняются последовательно, можно вызывать одну функцию много раз с разными параметрами.
  • Старые start/cutoff стали диапазонами: --in-range и --out-range.
  • Профили (мультистратегия) и хостлисты сохранены, разделитель — --new. Логика автохостлиста изменена: профиль с автолистом захватывает только соединения с известным hostname; до его получения — мимо.
  • --server инвертирует трактовку направлений и фильтрации.
  • Автоматическая TCP-сегментация в zapret-lib.lua: размер blob не важен, всё нарезается по MSS.
  • Поддержка многопакетных пейлоадов (kyber в tls_client_hello, многопакетный quic_initial) с авто-reasm и replay.

Состав стандартной Lua-библиотеки (/opt/zapret2/lua/)

Файл Содержимое
zapret-lib.lua Базовые примитивы. ПЕРВЫМ среди --lua-init: rawsend, tcpseg, send, drop, диссекторы TLS/QUIC, маркеры, утилиты.
zapret-antidpi.lua Готовые desync-аналоги nfqws1: fake, multisplit, multidisorder, fakedsplit, fakeddisorder, hostfakesplit, tcpseg, oob, wsize, wssize, syndata, rst, synack, synack_split, udplen, dht_dn, http_*, pktmod, pass, luaexec. Без неё --lua-desync=fake:... — вызов несуществующей функции: тихий 0%.
zapret-auto.lua Оркестраторы (circular, repeater, condition, per_instance_condition, stopif) и iff-функции (cond_random, cond_payload_str, cond_lua, …).
zapret-obfs.lua Обфускаторы: wgobfs, ippxor, udp2icmp, synhide. Надмножество zapret-wgobfs.lua — если грузим obfs, то wgobfs.lua грузить НЕ надо (двойное определение).
zapret-pcap.lua Запись pcap. Требует --writable (до 1.0 — --writeable).
zapret-tests.lua Тесты C-функций.

Наши расширения (import/lua/, разворачиваются на lua_path через core/asset_importer.py)

Файл Экспорт
zapret-multishake.lua hostfakesplit_stealth/chaos/multi/gradual/decoy/blend/soft, snifakesplit
fakemultisplit.lua fakemultisplit
fakemultidisorder.lua fakemultidisorder
zapret-16kb.lua flood_white, ttl_ladder, white_sandwich, seqovl_white
zapret-rst-flood.lua rst_flood
zapret-auto.lua circular/condition/repeater/stopif/per_instance_condition + детекторы/хосткеи (standard_*_detector, cond_*, automate_*). Триггер-загрузка по этим функциям; companion-bundle зависит от его standard_*_detector, поэтому zapret-auto до-грузится и в orchestrator-блоке (для circular_with_preload, которого нет в его экспорте)
custom_funcs.lua расширенный каталог приёмов проекта (http_*/tls_*/discord_*/multisplit_tls/tlsrec/rst_desync/…); зависит только от core
custom_diag.lua diag_once, diag_always (диагностические no-op)
Companion'ы оркестратора circular (грузятся bundle'ом, см. §10): combined-detector.lua, domain-grouping.lua, strategy-stats.lua, strategy-lock-manager.lua, silent-drop-detector.lua

Core = только zapret-lib.lua + zapret-antidpi.lua (как эталонный blockcheck2 pktws_start): грузятся при наличии любого --lua-desync. Всё остальное — включая zapret-auto/custom_funcs/custom_diag/init_vars — условно (раньше эти четыре были в core «всегда»).

init_vars.lua — value-триггер (НЕ в _EXTENSION_LUA_FILES): объявляет именованные паттерн-переменные (tls_google, tls_rnd*, bin_max, fake_inverted_tls, …), используемые как blob=/pattern=/seqovl_pattern=<NAME>. Грузится сразу после core ТОЛЬКО если стратегия ссылается на такое имя (см. _INIT_VARS_NAMES/_NAMED_PATTERN_RE в nfqws_manager.py). Встроенные блобы (fake_default_*) его не требуют. Сторож — TestInitVarsTrigger.

Инвариант: набор-триггер каждого файла (карта _EXTENSION_LUA_FILES в nfqws_manager.py:56) обязан совпадать с его глобальными функциями (grep '^function ' import/lua/<file>.lua) — иначе вызов «выпавшей» функции = тихий 0%. Сторож — tests/test_nfqws_lua_map.py.


2. Архитектура обработки пакета

ядро ОС
  └── iptables/nftables → NFQUEUE №X (= --qnum)
        └── nfqws2
              ├── dissect (ip/ip6/tcp/udp/icmp + payload)
              ├── conntrack (счётчики, направления, MSS, scale, hostname)
              ├── распознавание payload type
              ├── reasm / decrypt (для tls_client_hello, quic_initial)
              ├── выбор профиля по фильтрам (1..N → default 0)
              ├── последовательно: Lua-инстансы (--lua-desync)
              │     каждый возвращает вердикт PASS / MODIFY / DROP
              │     (агрегация: DROP > MODIFY > PASS)
              └── возврат пакета в ядро

Цикл выбора профиля повторяется при изменении L7-протокола и при получении hostname (макс. 2 «перескока»). Если все инстансы профиля вошли в cutoff или вышли за range — соединение помечается lua cutoff по направлению и больше не дёргает Lua.

Обязательные инварианты (нарушение = тихий 0%)

  1. zapret-lib.lua грузится --lua-init ПЕРВЫМ — он определяет базовые примитивы, на которые опирается zapret-antidpi.lua и наши расширения.
  2. zapret-antidpi.lua подключён, если стратегия вызывает fake, multisplit, multidisorder, … (см. список выше).
  3. Extension-скрипты грузятся ТОЛЬКО при упоминании их функций — карта _EXTENSION_LUA_FILES в nfqws_manager.py. Дедуп --lua-init выполняется в _build_lua_init_args.
  4. circular-оркестратор тащит весь bundle (_ORCHESTRATOR_LUA_FILES). Companion'ы (combined-detector, domain-grouping, strategy-stats, strategy-lock-manager, silent-drop-detector) — НЕ desync-действия, это аргументы detector=/success=/hostkey=/preload= у circular. Грузятся идемпотентно, без require(), существующие — guard'ом.
  5. --qnum N == firewall queue num N. Оба значения берутся из nfqws.queue_num (дефолт 300). Расходящиеся — пакеты идут в очередь, которую никто не слушает → тихий 0%.
  6. Все --blob=NAME:@file декларируются ДО первого --new (декларации глобальные). Иначе fake уходит пустым.

Встроенные блобы (НЕ требуют --blob)

  • fake_default_tls — TLS ClientHello от Firefox без kyber, SNI = www.microsoft.com.
  • fake_default_http — HTTP-запрос на www.iana.org.
  • fake_default_quic0x40 + 619 × 0x00.

Любые другие имена (tls_google, stun_pat, quic_vk, …) обязаны иметь --blob=NAME:@bin/file.bin. Дозаявка по имени в проекте автоматизирована — core/blob_registry.build_blob_declarations().


3. Полный CLI nfqws2 (nfqws2 -?, v0.9.5.2)

Дамп v0.9.5.2 движка bol-van/zapret2. Версия печатается: github version v0.9.5.2 (<git-hash>) lua_compat_ver 5. Опции с [0|1]/ [=val] имеют необязательный аргумент (без него — дефолт, обычно =1).

⚠️ Совместимость lua↔бинарник (NFQWS2_COMPAT_VER). zapret2 1.0 сменил lua_compat_ver 5→6 (несовместимое изменение: везде WRITEABLEWRITABLE, т.е. флаг --writeable--writable и env WRITABLE; плюс delay в send и фикс oob при urp=b). zapret-lib.lua:1 проверяет NFQWS2_COMPAT_VER_REQUIRED и падает с «Incompatible NFQWS2_COMPAT_VER», если lua и бинарник из разных релизов. Наши bundled-lua (import/lua/) должны совпадать по compat с устанавливаемым бинарником; core/asset_importer не понижает core-lua, если на диске уже лежит более новый релиз (см. _protected_core_lua, issue #151).

3.1 Общие / служебные

Опция Назначение
@<config_file> / $<config_file> Читать опции из файла. Должен быть единственным аргументом — остальные игнорируются. Используется nfqws2@.service (systemd). У нас НЕ используется (всё inline через compose_command).
--debug=0|1|syslog|@<file> Уровень/назначение отладочного лога. Бэйр --debug=1. Файл --debug=@file можно удалить — продолжит писать с нуля.
--version Печать версии и lua_compat_ver, выход.
--dry-run Проверить параметры и выйти с кодом 0 при успехе. Lua-синтаксис НЕ проверяется (только наличие файлов и парсинг опций).
--comment=<text> No-op, для читабельности конфига.
--intercept=0|1 0 = выполнить только lua-init и выйти. У нас на этом построена валидация стратегий — NFQWSManager.dry_run(), API POST /api/strategies/<sid>/validate (см. §12.1).
--qnum=<n> Номер NFQUEUE (== firewall queue num).
--daemon Демонизироваться. GUI запускает БЕЗ него (foreground-child + Popen). С ним работает только автозапуск S99zapret (свой --pidfile).
--chdir[=path] Без аргумента — EXEDIR.
--pidfile=<file> PID-файл. У S99zapret/var/run/zapret-nfqws.pid.
--user=<name> / --uid=uid[:gid,…] Сброс root-привилегий. На Entware --user nobody работает (есть в read-only /etc/passwd); кастомных юзеров там нет.
--bind-fix4 / --bind-fix6 Фикс выбора исходящего интерфейса для генерируемых пакетов (PBR / multi-WAN). Добавляются в _build_base_args при нескольких WAN.
--fwmark=<int|0xHEX> fwmark anti-loop. Дефолт 0x40000000 (1073741824) — совпадает с нашим nfqws.desync_mark. Не путать с firewall MARK_PROCESSED / MARK_EXCLUDE.
--ctrack-timeouts=S:E:F[:U] Внутр. conntrack: TCP SYN, ESTABLISHED, FIN, UDP. Дефолт 60:300:60:60.
--ctrack-disable[=0|1] Отключить внутренний conntrack.
--payload-disable[=t1,…] Не детектировать типы payload (без аргумента — все).
--reasm-disable[=t1,…] Отключить reasm для tls_client_hello, quic_initial.
--server[=0|1] Серверный режим (инверсия направлений и фильтров).
--ipcache-lifetime=<sec> TTL hop-count / hostname-кэша (дефолт 7200, 0 = вечно).
--ipcache-hostname[=0|1] Кэшировать ip→hostname (нужно стратегиям нулевой фазы).
--filter-ssid=ssid1[,…] Wi-Fi SSID-фильтр (Linux).

3.2 DESYNC ENGINE INIT

Опция Назначение
--writable[=dir] Создать каталог для Lua (env WRITABLE). До zapret2 1.0 — --writeable/WRITEABLE.
--blob=<name>:[+ofs]@<file>|0xHEX Глобальная декларация именованного блоба. Поддержан offset +ofs. До первого --new.
--lua-init=@<file>|<lua_text> Загрузить Lua. Порядок сохраняется. Поддержаны gzip-файлы.
--lua-gc=<sec> Интервал GC Lua (дефолт 60).

3.3 MULTI-STRATEGY (профили)

Опция Назначение
--new[=<name>] Разделитель профилей.
--skip Не использовать профиль.
--name=<name> Имя профиля.
--template[=<name>] Сделать профиль шаблоном (для --import).
--cookie[=<str>] Значение desync.cookie для всех инстансов профиля.
--import=<name> Импорт настроек из шаблона. Простые параметры замещаются, списочные (--hostlist=, --filter-tcp=, --lua-desync=) добавляются в конец.
--filter-l3=ipv4|ipv6 Фильтр версии IP.
--filter-tcp=[~]p1[-p2]|* Фильтр TCP-портов; ~ = инверсия. Список через запятую.
--filter-udp=[~]p1[-p2]|* Аналогично UDP.
--filter-icmp=type[:code]|* Автоматически включает ICMPv6 (типы/коды разные).
--filter-ipp=proto|* Raw IP-протоколы (НЕ относится к tcp/udp/icmp — для них нужен свой filter).
--filter-l7=p1[,p2…] L7-протокол потока. Полный список — §3.6.
--ipset=<file> / --ipset-ip=<list> Include по IP/CIDR (ipv4+ipv6, gzip, несколько файлов).
--ipset-exclude=<file> / --ipset-exclude-ip=<list> Exclude по IP.
--hostlist=<file> / --hostlist-domains=<list> Десинк только для перечисленных хостов. Поддомены автоматически, ^ в начале — отключает учёт поддоменов, # — комментарий, gzip, несколько.
--hostlist-exclude=<file> / --hostlist-exclude-domains=<list> Исключения.
--hostlist-auto=<file> Автохостлист.
--hostlist-auto-fail-threshold=<n> Фейлов для добавления (дефолт 3).
--hostlist-auto-fail-time=<sec> В пределах N сек (дефолт 60).
--hostlist-auto-retrans-threshold=<n> Ретрансмиссий = провал (дефолт 3).
--hostlist-auto-retrans-reset=0|1 RST ретрансмиттеру (дефолт 1).
--hostlist-auto-retrans-maxseq=<n> Дефолт 32768.
--hostlist-auto-incoming-maxseq=<n> Успех если входящий rel-seq > N (дефолт 4096).
--hostlist-auto-udp-out=<n> UDP-провал (дефолт 4).
--hostlist-auto-udp-in=<n> UDP-провал (дефолт 1).
--hostlist-auto-debug=<file> Лог срабатываний (глобальный).

3.4 LUA PACKET PASS MODE (внутрипрофильные фильтры)

Действуют до следующего переопределения того же типа или до конца профиля. В новом профиле сбрасываются на default.

Опция Default Назначение
--payload=t1[,t2] all Какие payload-типы обрабатывают следующие Lua-функции.
--out-range=<spec> a (всегда) Диапазон по исходящему направлению.
--in-range=<spec> x (никогда) Диапазон по входящему направлению.

3.5 LUA DESYNC ACTION

Опция Назначение
--lua-desync=<fn>[:p1[=v1]:p2[=v2]…] Вызов Lua-инстанса. Несколько подряд — последовательно.

3.6 Справочные списки значений

--filter-l7: all unknown known http tls dtls quic wireguard dht discord stun xmpp dns mtproto bt utp_bt.

--payload (типы): all unknown empty known ipv4 ipv6 icmp http_req http_reply tls_client_hello tls_server_hello dtls_client_hello dtls_server_hello quic_initial wireguard_initiation wireguard_response wireguard_cookie wireguard_keepalive wireguard_data dht discord_ip_discovery stun xmpp_stream xmpp_starttls xmpp_proceed xmpp_features dns_query dns_response mtproto_initial bt_handshake utp_bt_handshake.

--reasm-disable: tls_client_hello, quic_initial.

3.7 Правила построения фильтров профиля

  • Профили проверяются строго слева направо, побеждает первый подошедший.
  • Профиль 0 (default) — пустой, никаких действий.
  • Группы фильтров tcp / udp / icmp / ipp объединяются OR между собой. Указание любого блокирует остальные группы — нужно явно прописать --filter-tcp=* если хочешь TCP + что-то ещё.
  • filter-ipp НЕ относится к tcp/udp/icmp.
  • Без автохостлиста профиль с хостлистами не выбирается до получения hostname. Если хост в exclude — стратегия не применяется. С автохостлистом — работает всегда при наличии hostname (счётчики через --hostlist-auto-*).
  • ipset-ы могут смешивать ipv4+ipv6.

4. Диапазоны (--in-range / --out-range)

Формат: [mode<int>](-|<)[mode<int>].

  • - — верхняя граница включительная, < — исключительная.
  • mode:
    • a — always (без числа)
    • x — never (без числа)
    • n — номер пакета
    • d — номер пакета с данными (рекомендуется на Windows из-за --wf-tcp-empty=0)
    • b — байт-позиция переданных данных
    • s — relative sequence (TCP) от начала пакета
    • p — relative sequence верхней границы пакета (s+payload)

Примеры: --out-range=-d10 (первые 10 пакетов с данными), --in-range=-s5556, --out-range=s100-s1000, --in-range=s1<d1 (только до первого с данными).


5. Распознаваемые пейлоады (payload types)

L7-протокол L4 Payload-типы
http tcp http_req, http_reply
tls tcp tls_client_hello, tls_server_hello
xmpp tcp xmpp_stream, xmpp_starttls, xmpp_proceed, xmpp_features
mtproto tcp mtproto_initial
bt tcp bt_handshake
quic udp quic_initial
wireguard udp wireguard_initiation, _response, _cookie, _keepalive, _data
dht udp dht
utp_bt udp utp_bt_handshake
discord udp discord_ip_discovery
stun udp stun
dns udp dns_query, dns_response
dtls udp dtls_client_hello, dtls_server_hello
icmp * ipv4, ipv6, icmp

Спец-типы: empty (пустой), unknown. В фильтрах поддерживаются all и known (не empty/unknown).


6. Маркеры позиции (для pos= в split/disorder/tcpseg)

  • Абсолютный положительный — число с начала пейлоада: 100.
  • Абсолютный отрицательный — от конца: -1 (последний байт), -10.
  • Относительный — относительно логических позиций в известных пейлоадах:
    • method — начало HTTP-метода
    • host, endhost — начало/конец имени хоста
    • sld, endsld, midsld — second-level domain
    • sniext — поле данных SNI extension в TLS
    • extlen — поле длины TLS extensions
  • Арифметика: method+2, endhost-2, sniext+1.
  • Список через запятую: 1,midsld,endhost-2,-10.

7. Передача параметров в Lua-инстансы и блобы

--lua-desync=fn[:arg[=value]:arg=value:…]
  • Каждый arg — строка. Если value не задано — пустая строка.
  • Подстановки C-кода в значениях:
    • %var → значение desync.var (или global var).
    • #var → длина desync.var или global var.
    • Эскейп: \: \% \#.

Блобы:

--blob=mytls:@/etc/myfake.bin            # из файла
--blob=mytls_ofs:+12@/etc/myfake.bin     # с offset
--blob=mytls_hex:0xDEADBEEF              # inline hex

Подставляются в аргументы:

--lua-desync=fake:blob=mytls
--lua-desync=tcpseg:seqovl=#mytls:seqovl_pattern=mytls

#mytls → длина, %mytls → содержимое (применяется в C-коде до Lua).


8. Библиотека стратегий zapret-antidpi.lua

Вызов: --lua-desync=fnname:arg=val:…. Главный источник правды по параметрам — комментарии перед каждой функцией в lua-файле.

8.1 Базовые

Функция Поведение
drop VERDICT_DROP. args: dir (in/out/any), payload.
send Отправить текущий диссект (без дропа оригинала). args: dir, fooling, ipid, ipfrag, reconstruct, rawsend, delay=ms.
pktmod Применить fooling/ipid к диссекту (без отсылки и вердикта).
pass No-op (для оркестраторов).
luaexec Выполнить Lua-выражение из code=… (доступ к desync).

8.2 HTTP-дурение

Функция Поведение
http_hostcase Менять регистр заголовка Host: (arg: spell="host").
http_domcase Менять регистр имени домена в Host:.
http_methodeol \r\n перед методом (only nginx).
http_unixeol 0D0A0A.

8.3 Window size (legacy)

Функция Поведение
wsize Менять tcp.th_win и scale в SYN/ACK (только уменьшение). args: wsize, scale.
wssize То же по всем пакетам потока до cutoff. args: dir, wsize, scale, forced_cutoff=<payloads>. Снижает скорость. Стратегия нулевой фазы (с хостлистами — только при --ipcache-hostname).

8.4 Фейки

Функция Поведение
fake Прямой фейк (отдельный пакет). args: dir, payload (default known), fooling, ipid, ipfrag, reconstruct, rawsend, blob=<name>, optional, tls_mod=<mods>. Сегментация автоматическая (для blob > MSS).
syndata Добавить пейлоад в SYN. arg: blob (must fit MTU), tls_mod. После не-SYN пакета — instance_cutoff. Стратегия нулевой фазы.
rst Отослать пустой RST (или RST+ACK при rstack). args: dir, payload, fooling, ipid, ipfrag, reconstruct, rawsend.
tls_client_hello_clone Подготовить blob с модифицированным TLS ClientHello. args: blob, fallback, sni_del_ext, sni_del, sni_snt, sni_snt_new, sni_first, sni_last.

8.5 TCP-сегментация

Функция Поведение
multisplit Нарезать пейлоад по списку маркеров. args: pos=m1,m2,… (default 2), seqovl=<int>, seqovl_pattern=<blob>, blob=<replace_payload>, optional, nodrop. Поддерживает reasm.
multidisorder То же, но в обратном порядке отправки. seqovl может быть маркером. Не работает с Windows-серверами. Подходит для tls_client_hello с kyber и без.
multidisorder_legacy Поведение из nfqws1 — для backward-compat (использует blockcheck2.d/standard).
fakedsplit Split с замешиванием фейков. args: pos, seqovl, seqovl_pattern, blob, optional, nodrop, nofake1..nofake4, pattern=<blob>. Требует fooling.
fakeddisorder Disorder с замешиванием фейков. Аналогично.
hostfakesplit Спец-резатель для http_req/tls_client_hello вокруг имени хоста. args: host=<random.template>, midhost=<marker>, disorder_after=<marker>, nofake, nofake2, blob, optional, nodrop.
tcpseg Отослать произвольную часть пейлоада/reasm/blob, ограниченную двумя маркерами. args: pos=m1,m2, seqovl, seqovl_pattern, blob, optional. Вердикт не выносит. Удобно с drop:payload=known для замещения.
oob Вставить 1 OOB-байт в TCP handshake. args: char или byte, urp=b|e. Требует разрешения первых входящих (--in-range=-s1). Не сочетается с multi-split/disorder.

8.6 UDP

Функция Поведение
udplen Раздуть/обрезать UDP-payload. args: dir, payload, min, max, increment, pattern, pattern_offset.
dht_dn Заменить d1/d2 в DHT на dN. arg: dn.

8.7 Прочее (требует POSTNAT)

Функция Поведение
synack Отослать SYN/ACK до SYN (TCB turnaround). Ломает NAT, требует nftables-POSTNAT.
synack_split Вариация.

8.8 Стандартные блоки опций (передаются как ключи --lua-desync=fn:opt=val)

fooling: ip_ttl=N, ip6_ttl=N, ip_autottl=<delta>,<min>-<max>, ip6_autottl=…, ip6_hopbyhop[=hex], ip6_hopbyhop2, ip6_destopt, ip6_destopt2, ip6_routing, ip6_ah, tcp_seq=<±int>, tcp_ack=<±int>, tcp_ts=<±int>, tcp_md5[=16byte_hex], tcp_flags_set=FIN,SYN,…, tcp_flags_unset=ack, tcp_ts_up, tcp_nop_del, fool=<custom_lua_fn>, badsum, badseq.

ipid: ip_id=seq|rnd|zero|none (default seq для send-функций, none для send), ip_id_conn=1.

ipfrag: ipfrag (без значения = вкл. ipfrag2 default), ipfrag_disorder, ipfrag_pos_udp=<mul8> (default 8), ipfrag_pos_tcp=<mul8> (default 32), ipfrag_next=<proto>.

reconstruct: keepsum, badsum, ip6_preserve_next, ip6_last_proto.

rawsend: repeats=N, fwmark=<int>, ifout=<name>.

tls_mod: rnd, dupsid, rndsni, sni=<domain>, padencap. Список: tls_mod=rnd,rndsni,dupsid.


9. Оркестраторы (zapret-auto.lua) и наш bundle

9.1 Из zapret-auto.lua

  • circular — крутит стратегии по кругу при неудачах. Аргументы:
    • fails, retrans, maxseq и др. (см. automate_failure_check).
    • Все последующие инстансы помечают strategy=N (с 1 непрерывно). final — финальная стратегия.
    • Требует входящих пакетов: --in-range=-s5556 или больше (детектор успеха срабатывает на s4096).
  • repeater — повторяет N последующих инстансов R раз. args: instances=N, repeats=R, stop, clear, iff=<name>, neg.
  • condition — выполняет следующие инстансы только если iff xor neg. args: iff, neg, instances=N.
  • per_instance_condition — каждый из следующих инстансов несёт cond=…, cond_neg.
  • stopif — очистить план при условии.
  • iff-функции: cond_true, cond_false, cond_random:percent=N, cond_payload_str:pattern=str, cond_tcp_has_ts, cond_lua:cond_code=….

9.2 Наш bundle для circular (companion'ы)

circular_with_preload и circular принимают аргументы detector=, success=, hostkey=, preload=. Имена резолвятся в функции из:

Файл Что даёт
combined-detector.lua Композитные детекторы успеха/провала (TCP+TLS+timing).
silent-drop-detector.lua Детект «соединение установилось, но молчит».
domain-grouping.lua hostkey= — группировка хостов в один stateful-ключ.
strategy-stats.lua Метрики (для дебага).
strategy-lock-manager.lua Защёлка стратегии после успеха (антифлап).

Эти файлы — НЕ desync-действия. Они грузятся bundle'ом (_ORCHESTRATOR_LUA_FILES, _ORCHESTRATOR_TRIGGERS в nfqws_manager.py:108), когда стратегия использует --lua-desync=circular[…] или circular_with_preload. Порядок между companion'ами не важен (только определения + идемпотентная инициализация таблиц, без require()). На обычные circular-стратегии без ссылок на эти функции лишние определения не влияют.

Пример:

--filter-tcp=80,443 --filter-l7=http,tls --out-range=-s34228 --in-range=-s5556
--lua-desync=circular
--in-range=x --payload=tls_client_hello
--lua-desync=fake:blob=fake_default_tls:badsum:strategy=1
--lua-desync=multidisorder:strategy=2
--payload=http_req
--lua-desync=fake:blob=fake_default_http:badsum:strategy=1
--lua-desync=multisplit:strategy=2

10. Firewall: NFQUEUE-перехват

В проекте — core/firewall.FirewallManager. Автоопределяет тип:

  • На Keenetic / Entware / старом OpenWrt — iptables (по умолчанию).
  • На OpenWrt 22+ — nftables.
  • Если есть оба — предпочитает iptables (совместимость с Entware).

10.1 Sysctl, обязательные на роутере

FirewallManager._apply_sysctls ставит:

  • net.netfilter.nf_conntrack_tcp_be_liberal=1 — НЕ дропать пакеты вне TCP window (десинк намеренно их шлёт).
  • net.netfilter.nf_conntrack_checksum=0 — не считать checksum дважды.

Без них iptables/conntrack дропает out-of-window сегменты десинка → тихий 0%.

Hardware offload обязательно выключен на роутере (fw3 / netifd). Иначе iptables не видит трафик. Это поведенческий эталон nfqws2-keenetic, не «наша выдумка».

10.2 Ключевые правила построения

  1. fwmark anti-loop: пакеты, сгенерированные nfqws2, помечены nfqws.desync_mark (default 0x40000000). Все NFQUEUE-захваты должны исключать пакеты с этой меткой (mark & 0x40000000 == 0). Иначе бесконечная рекурсия / залипание.
  2. conntrack ограничитель: первые N пакетов через --connbytes 1:N --connbytes-mode=packets (iptables) или ct original packets 1-N (nft) — экономия CPU. Параметры читаются из секции nfqws.*, дефолты захардкожены в firewall.py:
    • TCP: nfqws.tcp_pkt_out (20) / nfqws.tcp_pkt_in (10).
    • UDP: nfqws.udp_pkt_out (5) / nfqws.udp_pkt_in (3).
  3. Перехват SYN+ACK, FIN, RST на входе нужен для корректной работы conntrack и autohostlist (детект RST-блока).
  4. notrack для пакетов с DESYNC_MARK в output/predefrag — чтобы NAT не ломал нестандартные пакеты (только nftables-POSTNAT).
  5. Не указывать в фильтрах исключающий ipset (nozapret/nozapret6) — это делает основной код, и проверку DESYNC_FWMARK.
  6. iptables PRENAT vs nftables POSTNAT: iptables НЕ умеет POSTNAT-перехват для проходящего трафика, поэтому некоторые техники (synack, манипуляции с IP/портами) не работают на iptables в forwarded-сценарии. На современном Linux/OpenWrt — выбирай nftables. На Keenetic — iptables с поправкой ниже.

10.3 Keenetic UDP fix (специально для нашего пути)

Из-за проприетарного ndmmark родная NAT-логика Keenetic не masquerade'ит UDP-пакеты от nfqws → они уходят с LAN-IP и дропаются провайдером. Без этого UDP-стратегии (QUIC, DTLS, WireGuard) рассыпаются.

Решение — правило (выставляется при firewall.apply()):

iptables -t nat -A POSTROUTING -o $wanif -p udp \
  -m mark --mark $DESYNC_MARK/$DESYNC_MARK -j MASQUERADE

В апстриме это init.d/custom.d.examples.linux/10-keenetic-udp-fix. У нас повторный MASQUERADE для UDP-пакетов с DESYNC_MARK добавляется в firewall.py на iptables-пути (do_nat = (ipt_cmd == "iptables"), только IPv4) — как паритет с nfqws2-keenetic, а не по отдельному детекту Keenetic. На nftables-пути есть свой nat postrouting … masquerade для переписанных nfqws2-пакетов.

10.4 Порты по умолчанию

config_manager.DEFAULT_CONFIG (намеренно шире эталона keenetic, который ограничивается 443/QUIC):

  • nfqws.ports_tcp = "80,443,2053,2083,2087,2096,5222,8443" — HTTP/HTTPS + alt-порты Cloudflare + Telegram MTProto 5222.
  • nfqws.ports_udp = "443,3478:3481,5349,19294:19344,49152:65535" — QUIC + STUN/TURN + WireGuard-диапазоны + Discord voice.

Должны согласовываться с --filter-tcp= / --filter-udp= в стратегии. Если стратегия --filter-tcp=443, а в nfqws.ports_tcp нет 443 — в очередь не придёт ничего.

10.5 Сигналы nfqws2

  • SIGHUP — принудительно перечитать хостлисты и ipset-ы. У нас: kill -HUP $(cat /var/run/zapret-nfqws.pid) — после правки lists/.
  • SIGUSR1 — дамп пула conntrack.
  • SIGUSR2 — дамп autohostlist-счётчиков и ipcache.

11. Layout проекта (где что лежит)

Наш проект следует раскладке bol-van/zapret2 (ZAPRET_BASE = /opt/zapret2). Имена файлов/каталогов — как в апстриме. Дефолты — config_manager.DEFAULT_CONFIG.

11.1 Ассеты движка zapret2 (/opt/zapret2/)

Путь Конфиг Назначение
nfq2/nfqws2 zapret.nfqws_binary Бинарник nfqws2 (статический).
lua/*.lua zapret.lua_path zapret-lib.lua, zapret-antidpi.lua, … + наши import/lua/*.lua, развёрнутые core/asset_importer.py (import_all / _sync_dir). core/lua_manager.py управляет ими в рантайме (list/get/save/reset_to_bundled), bundled-исходники — import/lua/.
lists/ zapret.lists_path Hostlist'ы (*.txt).
ipset/ zapret.ipset_path IP-списки + ipban + auto-листы.
files/fake/*.bin zapret.bin_path Blob-файлы для --blob=NAME:@….
blockcheck2.sh / blockcheck.sh zapret.blockcheck2_path (иначе автопоиск в base_path) Штатный blockcheck.

⚠️ Миграция: до 0.16.3 дефолт bin_path = /opt/zapret2/bin был неверным (апстрим — files/fake), и не было ipset_path. config_manager лечит старые конфиги автоматически.

11.2 Состояние GUI (/opt/etc/zapret-gui/)

  • settings.json — конфигурация (core/config_manager.DEFAULT_CONFIG_DIR); секции zapret.*, nfqws.*, firewall.*, scan.* и т.д.;
  • Runtime firewall-конфиг и хуки персистентности (core/firewall_persistence.GUI_RUNTIME_DIR);
  • State-файлы установщиков (singbox/mihomo и пр.).

11.3 Автозапуск (/opt/etc/init.d/, Entware)

  • S99zapret — автозапуск nfqws2 с применённой стратегией + firewall. Генерируется core/autostart_manager.py (INIT_DIR / SCRIPT_NAME, шаблон _S99ZAPRET_TEMPLATE). PID-файл — /var/run/zapret-nfqws.pid.
  • S99zapret-gui — сам сервис Web-GUI (создаётся install.sh).

⚠️ Путей вида /opt/etc/nfqws2/nfqws2.conf или /opt/etc/init.d/S51nfqws2 у нас НЕТ — это раскладка стороннего упаковщика nfqws2-keenetic, не bol-van/zapret2. Из nfqws2-keenetic берём только идеи поведения (НЕ пути): режимы выбора доменов list / auto (домен добавляется после 3 фейлов за 60с) / all, отключение hardware offload, рекомендация DoT/DoH.

11.4 Каталоги стратегий (catalogs/)

Каталог Тип содержимого
builtin/winws2_presets.txt, zapret_gui_defaults.txt Полные пресеты — содержат свои --filter-*/--hostlist=/--blob=/--new. Берутся как есть, только резолвятся пути.
basic/, advanced/, direct/ «Приёмы» (tricks) — один-два --lua-desync=. Сканер сам оборачивает в шаблон цели (добавляет --filter-*, --filter-l7, --payload, --hostlist=<tmp с доменами цели>). См. StrategyScanner._wrap_trick_args.

Эвристика «полный пресет vs приём» — _is_full_preset_args() в strategy_scanner.py: наличие --filter-*/--new/--hostlist/--ipset/ --blob ⇒ полный пресет.


12. Сборка argv: NFQWSManager.compose_command()

Единый источник argv (и для live-запуска, и для автозапуска):

[binary]
  + base       (--user / --fwmark / --qnum [/--debug] [/--bind-fix4/6])
  + lua-init   (core + extension + orchestrator bundle, dedup)
  + unified    (--hostlist подмешан из активных профилей)
  + strategy_args
  • _build_base_args (nfqws_manager.py:~512) — --user, --fwmark, --qnum из конфига; --debug при nfqws.debug=true; --bind-fix4/6 при нескольких WAN.
  • _build_lua_init_args (:556) — добавляет core-lua только если в стратегии есть --lua-desync И файл существует на lua_path. Extension-lua — по используемым функциям из _EXTENSION_LUA_FILES. Orchestrator-bundle — если функция входит в _ORCHESTRATOR_TRIGGERS. Дедуп --lua-init.
  • queue_num — из nfqws.queue_num (дефолт 300); то же значение использует firewall.py для queue num. Не разводить эти числа.
  • Превью команды (build_preview_command, POST /api/strategies/preview) собирается ЧЕРЕЗ тот же compose_command — превью = реальная команда. Поэтому если в превью видны --lua-init …/combined-detector.lua и т.п. — это корректно для circular-стратегий, не «мусор».
  • Блобы по имени дозаявляются автоматически — core/blob_registry.py (build_blob_declarations), маппинг имя → @bin/*.bin.

12.1 Валидация стратегии (--intercept=0, без поднятия NFQUEUE)

  • NFQWSManager.dry_run(strategy_args) собирает argv тем же compose_command, убирает --user= (чтобы не было setuid вне рантайма) и любые --intercept/--dry-run, добавляет --intercept=0 (эталон: «выполнить только lua-init и выйти»), запускает и проверяет returncode==0. NFQUEUE не открывается, трафик не затрагивается.
  • Ловит: кривой парсинг опций CLI, отсутствующие файлы (--blob/--lua-init/--hostlist/…) И ошибки lua-init — синтаксис lua-скриптов и ошибки времени загрузки (напр. неверный порядок --lua-init, когда init_vars зовёт tls_mod до zapret-antidpi).
  • Не ловит даже так: вызов несуществующей --lua-desync функции — он происходит по-пакетно в рантайме, а не на lua-init. От такого «тихого 0%» защищает статический инвариант tests/test_nfqws_lua_map.py, а не валидация.
  • Почему не --dry-run: по эталону он Lua НЕ загружает («Lua не проверяется»). --intercept=0 — строгий супермножество: парсинг опций + файлы + lua-init.
  • API:
    • POST /api/strategies/<sid>/validate{ok, returncode, output, command}.
    • POST /api/strategies/preview с {"validate": true} → поле validation: {ok, available, returncode, output, command}. available=false — бинарника нет (dev-машина без zapret2).

13. Сканер стратегий: strategy_scanner.py

13.1 Обёртка приёмов (_wrap_trick_args)

Стратегия из basic//advanced//direct/ — это «приём» (1-2 --lua-desync=). Сканер оборачивает её в полный профиль:

  1. Берёт target (домен/порт/L7/payload из scan_targets.py).
  2. Достаёт hostlist цели → tmp-файл → --hostlist=<tmp>.
  3. Добавляет --filter-tcp/--filter-udp, --filter-l7, --payload под цель.
  4. Дозаявляет блобы (blob_registry.build_blob_declarations).
  5. Передаёт в compose_command.

Полный пресет (из builtin/) — НЕ оборачивается, идёт как есть.

13.2 Baseline-aware: «сайт открыт без обхода → всегда 0%»

В strategy_scanner.py:~1220baseline_open_all. Если до запуска стратегии цель открывается успешно по всем AF (_baseline_by_af), то любая стратегия получает принудительное success=False с ошибкой BASELINE_OPEN. Логика: стратегия не может «починить» то, что не сломано.

Следствие: подбор надо запускать на заблокированном ресурсе. На заведомо доступном — 0% это не баг, это охрана от ложных «успехов».

13.3 Body-проба (≥ 64 KB)

BODY_PROBE_MIN_BYTES = 65_536 (strategy_scanner.py:61). DPI часто пускает первые 16-20 KB ответа и потом рвёт соединение — body-проба ловит это. TLS-only без body считается псевдо-успехом (отсев «формально открылось»).

Аналог в blockcheck2.sh — env CURL_HTTPS_GET=1 (GUI-галка «Качать полное тело»): HTTPS-проба делает GET всего тела вместо HEAD (-I).

13.4 Предварительная проверка предпосылок

StrategyScanner._check_prerequisites() (:550) вызывает core.diagnostics.check_strategy_prerequisites() на старте сканирования и громко логирует блокеры. Первое, что смотреть при «0% на всём».

API: GET /api/diagnostics/prerequisites. Проверяет:

  • наличие бинарника nfqws2;
  • обязательные lua (zapret-lib.lua, zapret-antidpi.lua);
  • наличие blob-файлов в bin_path;
  • каталоги списков;
  • доступность NFQUEUE-модуля;
  • nf_conntrack_tcp_be_liberal = 1.

Возвращает issues со severity=error|warning и hint.

13.5 Дедуп

strategy_generator._norm_args() (:262) — нормализованный ключ для дедупа. Используется и при генерации стратегий «на лету», и в сканере. Не дублировать стратегии с одинаковыми нормализованными args.


14. Blockcheck2-интеграция

core/blockcheck2.Blockcheck2Runner, API /api/blockcheck2/{script,start,status,output,stop}. Запускает оригинальный blockcheck2.sh (или blockcheck.sh) из репозитория zapret2 как подпроцесс неинтерактивно (BATCH=1), стримит вывод в лог-буфер (source=blockcheck2) и в кольцевой буфер для инкрементального polling (output?offset=N).

Путь — zapret.blockcheck2_path или автопоиск в base_path. Это НЕ путать с core/blockcheck.py — наша Python-реализация проб для GUI-тестера.

14.1 Ключевые env (из шапки blockcheck2.sh)

DOMAINS=bbc.com           # домены через пробел, поддерживается "rutracker.org/forum"
TEST=standard|custom|<dir># имя теста (subdir в blockcheck2.d)
IPVS=4|6|46               # версии IP
ENABLE_HTTP=1, ENABLE_HTTPS_TLS12=1, ENABLE_HTTPS_TLS13=1, ENABLE_HTTP3=1
REPEATS=N                 # попыток на стратегию
PARALLEL=0|1              # внимание: rate-limit
SCANLEVEL=quick|standard|force
BATCH=1                   # без интерактива
HTTP_PORT, HTTPS_PORT, QUIC_PORT
SKIP_DNSCHECK=1, SKIP_IPBLOCK=1, SKIP_PKTWS=1
CURL=<path>               # заменить системный curl (для kyber/quic)
CURL_MAX_TIME[_QUIC|_DOH]
CURL_CMD=1                # печатать команды curl
CURL_HTTPS_GET=1          # GET вместо HEAD — для теста ~16K-блока
PKTWS_EXTRA_PRE[_N], PKTWS_EXTRA_POST[_N]  # доп.параметры nfqws/winws
SECURE_DNS=0|1, DOH_SERVER, DOH_SERVERS
DNSCHECK_DNS, DNSCHECK_DOM, UNBLOCKED_DOM
MIN_TTL, MAX_TTL
SIMULATE=1, SIM_SUCCESS_RATE=N    # debug сам blockcheck2

14.2 Примороженные итоги (highlights)

_HIGHLIGHT_RE в blockcheck2.py ловит ТОЛЬКО найденные рабочие стратегии (working strategy found …) и заголовки * SUMMARY / * COMMON, с дедупом и чисткой !!!!!.

НЕ примораживать AVAILABLE/UNAVAILABLE (вдобавок AVAILABLE — подстрока UNAVAILABLE, на этом горел старый фильтр).

Структурные находки (found) → бейджи в GUI. Помимо highlights, blockcheck2.py парсит строки working strategy found в структуру (parse_found_strategy/_classify_test): {ipv, test, domain, engine, strategy, proto, port, l7, payload, label} и отдаёт её в get_status() полем found. Тип теста (curl_test_http/https_tls12/tls13/http3) → proto/port/l7/payload как в scan_targets. Фронтенд (blockcheck2.js) рисует кликабельные бейджи; клик открывает редактор создания стратегии, предзаполненный фильтром из типа теста + дословным --lua-desync (реконструкция по той же конвенции, что _wrap_trick_args, см. §13.1). Сторож — tests/test_blockcheck2_found.py.

Документация GUI-помощи — web/js/components/help.js, топик blockcheck2.

14.3 Тест custom

Простой пробивщик по спискам стратегий из:

  • blockcheck2.d/custom/list_http.txt
  • blockcheck2.d/custom/list_https_tls12.txt
  • blockcheck2.d/custom/list_https_tls13.txt
  • blockcheck2.d/custom/list_quic.sh

Каждая стратегия — одна строка, поддержаны # комментарии. Параметры интерпретируются shell — нужно экранирование <, >, (, ), кавычек.

Важно: blockcheck2 проверяет один домен/URI/протокол. Браузер делает гораздо больше (DNS, ipv4/6, TLS1.2/1.3, QUIC, kyber, ECH, fingerprint). Поэтому Summary OK ≠ автоматически рабочий сайт.


15. Типовые шаблоны стратегий

Каждый профиль строится по схеме: фильтр профилявнутрипрофильные фильтры (range/payload)последовательность инстансов.

15.1 HTTP-only (порт 80)

--filter-tcp=80 --filter-l7=http
  --out-range=-d10 --payload=http_req
    --lua-desync=fake:blob=fake_default_http:ip_autottl=-2,3-20:ip6_autottl=-2,3-20:tcp_md5
    --lua-desync=fakedsplit:ip_autottl=-2,3-20:ip6_autottl=-2,3-20:tcp_md5

15.2 TLS 1.2/1.3 (порт 443) — фейк + multidisorder

--filter-tcp=443 --filter-l7=tls --hostlist=youtube.txt
  --out-range=-d10 --payload=tls_client_hello
    --lua-desync=fake:blob=fake_default_tls:tcp_md5:repeats=11:tls_mod=rnd,dupsid,sni=www.google.com
    --lua-desync=multidisorder:pos=1,midsld

15.3 TLS с seqovl (скрытый фейк, без фулинга)

--payload=tls_client_hello
  --lua-desync=multisplit:pos=1:seqovl=5:seqovl_pattern=0x1603030000

15.4 QUIC (UDP 443)

--blob=quic_google:@/opt/zapret2/files/fake/quic_initial_www_google_com.bin
--filter-udp=443 --filter-l7=quic --hostlist=youtube.txt
  --payload=quic_initial
    --lua-desync=fake:blob=quic_google:repeats=11

15.5 WireGuard / STUN / Discord (UDP)

--filter-l7=wireguard,stun,discord
  --payload=wireguard_initiation,wireguard_cookie,stun,discord_ip_discovery
    --lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2

15.6 Циклическая смена стратегий с детектором неудач

--filter-tcp=80,443 --filter-l7=http,tls
  --out-range=-s34228 --in-range=-s5556
  --lua-desync=circular
  --in-range=x
  --payload=tls_client_hello
    --lua-desync=fake:blob=fake_default_tls:badsum:strategy=1
    --lua-desync=multidisorder:strategy=2
  --payload=http_req
    --lua-desync=fake:blob=fake_default_http:badsum:strategy=1
    --lua-desync=multisplit:strategy=2

15.7 Кастомный фейк случайного размера

--lua-desync=luaexec:code='desync.rnd=brandom_az(math.random(5,10))'
--lua-desync=tcpseg:pos=0,-1:seqovl=#rnd:seqovl_pattern=rnd
--lua-desync=drop:payload=known

16. Чеклист «не находит ни одной рабочей стратегии»

Проверять В ЭТОМ ПОРЯДКЕ:

  1. Сайт доступен без обхода → всегда 0% (это НЕ баг). См. §13.2 (baseline_open_all / BASELINE_OPEN). Подбор — на заблокированном ресурсе.
  2. Нет lua-скриптов на lua_path (/opt/zapret2/lua). _build_lua_init_args не добавит то, чего нет, → --lua-desync=fake/multisplit = вызов несуществующей функции. Проверка: ls /opt/zapret2/lua/zapret-lib.lua, zapret-antidpi.lua. На dev-машине без zapret2 — тотальный 0%.
  3. Нет blob-файлов в bin_path (/opt/zapret2/files/fake/*.bin) — fake уходит пустым. В логе blobs: «blob '…' не найден в реестре».
  4. --qnum ≠ firewall queue num — трафик в очередь, которую не слушают. Оба берутся из nfqws.queue_num, но если правила накатаны вручную / из другого источника — расходятся.
  5. Hostlist не матчит SNI цели. Приёмы оборачиваются tmp-hostlist'ом с доменами цели; если домен не совпал с реальным SNI — десинк не применился. Лечится «выключить hostlist для теста» (MODE_FILTER=none).
  6. Hardware offload включён / conntrack не настроен. iptables не видит трафик, либо ядро дропает out-of-window сегменты десинка. Наш firewall ставит nf_conntrack_tcp_be_liberal=1 и nf_conntrack_checksum=0 — проверить, что применилось (sysctl -a | grep be_liberal) на роутере, не в контейнере.
  7. Body-проба требует ≥ 64 KB (BODY_PROBE_MIN_BYTES). TLS-only без body — считается псевдо-успехом.
  8. NAT ломает технику (synack, TCB-turnaround): нужно nftables-POSTNAT. На iptables — не работает.
  9. Keenetic-UDP не уходит: см. §10.3 (MASQUERADE с проверкой mark).
  10. --user nobody падает на Entware: указать пользователя из /etc/passwd (он там обычно есть).
  11. Стратегия работает один раз, потом перестаёт: DPI «наказывает» — рассмотри circular (§9) или включи --ipcache-hostname если работаешь нулевой фазой.
  12. multidisorder не работает на Windows-сервере: Windows не переписывает буфер сокета по seqovl. Используй multisplit.
  13. wssize режет скорость: применяй только в крайних случаях, дублируй в отдельный профиль до получения hostname.
  14. autohostlist не наполняется: нужно перехватывать достаточно входящих пакетов для детектора (--in-range пошире) и/или достаточно исходящих ретрансмиссий.
  15. VM не работает: гипервизорный NAT (VMware, VirtualBox) ломает большинство техник; используй bridge.
  16. Профиль не выбирается до hostname: если в нём есть --hostlist= без autolist — это норма. Дублируй стратегию в профиль без хостлиста, если нужно действовать с первого пакета.

17. Отладка

Главный диагностический приём — поднять пер-пакетный лог nfqws2:

  • Конфиг: nfqws.debug = truenfqws_manager добавляет --debug в argv, stderr nfqws2 логируется на уровне INFO, чтобы быть видимым.
  • В логе смотреть: грузятся ли lua-скрипты, объявлены ли блобы, матчится ли пакет цели по filter/hostlist, какие desync реально применяются.

Ручной прогон (эталон из zapret2, для сверки на роутере):

nfqws2 --qnum 300 --debug \
  --lua-init=@/opt/zapret2/lua/zapret-lib.lua \
  --lua-init=@/opt/zapret2/lua/zapret-antidpi.lua \
  --filter-tcp=80,443 --filter-l7=tls,http \
  --payload=tls_client_hello \
    --lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid \
  --payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5 \
  --payload=tls_client_hello,http_req \
    --lua-desync=multisplit:pos=1:seqovl=5:seqovl_pattern=0x1603030000

Запускать с уже накатанными firewall-правилами на тот же --qnum, иначе в очередь ничего не придёт и debug будет пустым.

Lua-отладчики (из zapret-lib.lua): pktdebug, argdebug, posdebug, var_debug(t) — рекурсивная печать desync-таблицы.


18. Правила при написании / правке стратегий

  • НЕ использовать синтаксис nfqws1 (--dpi-desync=, --dpi-desync-split-pos=) — другой движок. Только --lua-desync=.
  • Blob-декларации (--blob=) — один раз в начало, до первого --new.
  • zapret-lib.lua первым среди --lua-init.
  • Не дублировать --qnum; синхронизировать с firewall.
  • Для мультипрофиля — --new между профилями; фильтры/payload — внутри своего профиля.
  • При генерации стратегий «на лету» (strategy_generator) — дедуп по нормализованным args (_norm_args).
  • Тестировать всегда на заблокированном ресурсе — иначе baseline-aware даст 0%.
  • Перед сохранением — валидация (--intercept=0) через API (POST /api/strategies/<sid>/validate).
  • Кастомные .lua / .bin / .confне в /opt/zapret2 (снесёт инсталлятор при обновлении), а в отдельный каталог (например /opt/etc/zapret-gui/custom/) и подключай полными путями.

19. Lua-программирование (для расширений)

19.1 Где исполняется Lua

  1. --lua-init — при старте, один раз. Можно строкой или @file. Поддержан .gz.
  2. --lua-desync — на каждый пакет, проходящий профиль (после фильтров C-кода).
  3. Таймеры (timer_set).

19.2 Прототип desync-функции

function fnname(ctx, desync)
  -- ctx — для вызова C-функций (rawsend, instance_cutoff, ...)
  -- desync — таблица с:
  --   desync.dis        — диссект (.ip / .ip6 / .tcp / .udp / .icmp / .payload / .l4proto / ...)
  --   desync.arg        — аргументы инстанса (после подстановок %, #)
  --   desync.outgoing   — направление
  --   desync.l7payload  — payload type
  --   desync.l7proto    — protocol type
  --   desync.track      — conntrack (может отсутствовать!)
  --   desync.track.lua_state — для хранения состояния на поток
  --   desync.reasm_data — собранный многопакетный пейлоад
  --   desync.replay, desync.replay_piece, ...
  --   desync.tcp_mss    — есть всегда для TCP
  return VERDICT_PASS  -- или VERDICT_MODIFY / VERDICT_DROP (+ VERDICT_PRESERVE_NEXT)
end

19.3 Полезные C-функции (из Lua)

  • Лог: DLOG(s), DLOG_ERR(s), DLOG_CONDUP(s).
  • IP: ntop(raw), pton(str).
  • Битовые: bitand, bitor, bitxor, bitnot, bitlshift, bitrshift, bitget, bitset.
  • Беззнаковые числа: u8/u16/u24/u32, bu8/bu16/…, swap16/swap32/…, u32add, …
  • Случайные: brandom(n), bcryptorandom(n), brandom_az(n).
  • Парсинг: parse_hex(s).
  • Крипта: aes, aes_gcm, aes_ctr, hkdf, hash.
  • Сжатие: gzip(b), gunzip(b).
  • Системные: uname(), clock_gettime(), getpid(), stat(path), time().
  • Диссекция: dissect(raw), reconstruct_dissect(dis, opts), reconstruct_tcphdr / …iphdr / …ip6hdr, csum_*_fix.
  • conntrack: conntrack_feed(dis_or_raw, opts).
  • IP/iface: get_source_ip(target), get_ifaddrs().
  • Отсылка: rawsend(raw, opts), rawsend_dissect(dis, opts, recopts).
  • Управление: instance_cutoff(ctx[, outgoing]), lua_cutoff(ctx[, outgoing]), execution_plan(ctx), execution_plan_cancel(ctx).
  • Таймеры: timer_set(name, ms, fn, data), timer_del(name), timer_info(name), timer_enum().
  • Пейлоады: resolve_pos(blob, payload_type, marker), resolve_multi_pos, resolve_range, tls_mod(blob, modlist, payload).
  • Файлы: --writable создаст каталог, путь в os.getenv("WRITABLE") (до zapret2 1.0 — --writeable / WRITEABLE).

Песочница: os.execute, io.popen, package.loadlib, модуль debug — удалены.

19.4 Минимальный пример

function my_repeated_fake(ctx, desync)
  if not desync.dis.tcp then
    instance_cutoff_shim(ctx, desync)  -- хелпер из zapret-lib
    return
  end
  local rep = tonumber(desync.arg.repeats or "3")
  local dis = deepcopy(desync.dis)
  dis.payload = blob(desync, desync.arg.blob)
  apply_fooling(desync, dis)
  for i = 1, rep do
    rawsend_dissect_segmented(desync, dis, desync.tcp_mss, desync.arg)
  end
  -- VERDICT_PASS — оригинал пройдёт следом
end

Подключение:

--lua-init=@/opt/zapret2/lua/zapret-lib.lua
--lua-init=@/opt/etc/zapret-gui/custom/my-strategy.lua
--lua-desync=my_repeated_fake:blob=fake_default_tls:repeats=5:ip_autottl=-1,3-20

20. Шпаргалка по командам

Действие Команда
Версия nfqws2 /opt/zapret2/nfq2/nfqws2 --version
Валидация опций nfqws2 --dry-run <opts>
Старт GUI (Entware) /opt/etc/init.d/S99zapret-gui start
Старт автозапуска /opt/etc/init.d/S99zapret start
Стоп/рестарт … stop / … restart
Перечитать листы (без рестарта) kill -HUP $(cat /var/run/zapret-nfqws.pid)
Дамп conntrack kill -USR1 $(cat /var/run/zapret-nfqws.pid)
Дамп autohostlist + ipcache kill -USR2 $(cat /var/run/zapret-nfqws.pid)
Запуск blockcheck (через API) POST /api/blockcheck2/start
Проверка предпосылок GET /api/diagnostics/prerequisites
валидация стратегии (--intercept=0) POST /api/strategies/<sid>/validate
Превью команды POST /api/strategies/preview

Ссылки на исходные документы (bol-van/zapret2)

  • docs/manual.md — полный мануал (RU, ~5800 строк).
  • docs/manual.en.md — английская версия.
  • docs/readme.md — короткое введение / портирование стратегий nfqws1 → nfqws2.
  • docs/changes.txt, docs/changes_compat.txt — история изменений.
  • lua/zapret-antidpi.luaглавный источник правды по стратегиям (комментарии перед каждой функцией).
  • lua/zapret-lib.lua — хелперы (для написания своих desync).
  • lua/zapret-auto.lua — оркестраторы.
  • init.d/custom.d.examples.linux/ — рабочие custom-скрипты.
  • blockcheck2.d/standard/ — модули стандартного теста; blockcheck2.d/custom/list_*.txt — пресет-стратегии.
  • config.default — дефолтный config с подробными комментариями.

Главное правило при работе со стратегией: пиши --debug=1, дёргай curl-ом нужный домен, читай лог. Без debug — слепая работа.

Install via CLI
npx skills add https://github.com/avatarDD/zapret-gui --skill nfqws2-strategies
Repository Details
star Stars 50
call_split Forks 3
navigation Branch main
article Path SKILL.md
More from Creator