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,
каталоги или объяснять пользователю «почему не работает».
Источники истины (в порядке убывания авторитета):
- 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. nfqws2 -?(v0.9.5.2) — дамп опций CLI, §3 ниже.- nfqws2-keenetic — поведенческий эталон под Keenetic (НЕ путать пути —
у них своя раскладка
S51nfqws2//opt/etc/nfqws2/…; у нас её НЕТ). - Наш код в
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%)
zapret-lib.luaгрузится--lua-initПЕРВЫМ — он определяет базовые примитивы, на которые опираетсяzapret-antidpi.luaи наши расширения.zapret-antidpi.luaподключён, если стратегия вызываетfake,multisplit,multidisorder, … (см. список выше).- Extension-скрипты грузятся ТОЛЬКО при упоминании их функций — карта
_EXTENSION_LUA_FILESвnfqws_manager.py. Дедуп--lua-initвыполняется в_build_lua_init_args. 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'ом.--qnum N== firewallqueue num N. Оба значения берутся изnfqws.queue_num(дефолт 300). Расходящиеся — пакеты идут в очередь, которую никто не слушает → тихий 0%.- Все
--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_quic—0x40 + 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 (несовместимое изменение: везде WRITEABLE→
WRITABLE, т.е. флаг --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 domainsniext— поле данных SNI extension в TLSextlen— поле длины 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(или globalvar).#var→ длинаdesync.varили globalvar.- Эскейп:
\:\%\#.
Блобы:
--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 |
0D0A → 0A. |
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 Ключевые правила построения
- fwmark anti-loop: пакеты, сгенерированные nfqws2, помечены
nfqws.desync_mark(default0x40000000). Все NFQUEUE-захваты должны исключать пакеты с этой меткой (mark & 0x40000000 == 0). Иначе бесконечная рекурсия / залипание. - 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).
- TCP:
- Перехват SYN+ACK, FIN, RST на входе нужен для корректной работы conntrack и autohostlist (детект RST-блока).
- notrack для пакетов с DESYNC_MARK в output/predefrag — чтобы NAT не ломал нестандартные пакеты (только nftables-POSTNAT).
- Не указывать в фильтрах исключающий ipset (
nozapret/nozapret6) — это делает основной код, и проверкуDESYNC_FWMARK. - 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=). Сканер оборачивает её в полный профиль:
- Берёт
target(домен/порт/L7/payload изscan_targets.py). - Достаёт hostlist цели → tmp-файл →
--hostlist=<tmp>. - Добавляет
--filter-tcp/--filter-udp,--filter-l7,--payloadпод цель. - Дозаявляет блобы (
blob_registry.build_blob_declarations). - Передаёт в
compose_command.
Полный пресет (из builtin/) — НЕ оборачивается, идёт как есть.
13.2 Baseline-aware: «сайт открыт без обхода → всегда 0%»
В strategy_scanner.py:~1220 — baseline_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.txtblockcheck2.d/custom/list_https_tls12.txtblockcheck2.d/custom/list_https_tls13.txtblockcheck2.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. Чеклист «не находит ни одной рабочей стратегии»
Проверять В ЭТОМ ПОРЯДКЕ:
- Сайт доступен без обхода → всегда 0% (это НЕ баг). См. §13.2
(
baseline_open_all/BASELINE_OPEN). Подбор — на заблокированном ресурсе. - Нет 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%. - Нет blob-файлов в
bin_path(/opt/zapret2/files/fake/*.bin) — fake уходит пустым. В логеblobs: «blob '…' не найден в реестре». --qnum≠ firewallqueue num— трафик в очередь, которую не слушают. Оба берутся изnfqws.queue_num, но если правила накатаны вручную / из другого источника — расходятся.- Hostlist не матчит SNI цели. Приёмы оборачиваются tmp-hostlist'ом с
доменами цели; если домен не совпал с реальным SNI — десинк не применился.
Лечится «выключить hostlist для теста» (
MODE_FILTER=none). - Hardware offload включён / conntrack не настроен. iptables не
видит трафик, либо ядро дропает out-of-window сегменты десинка. Наш
firewall ставит
nf_conntrack_tcp_be_liberal=1иnf_conntrack_checksum=0— проверить, что применилось (sysctl -a | grep be_liberal) на роутере, не в контейнере. - Body-проба требует ≥ 64 KB (
BODY_PROBE_MIN_BYTES). TLS-only без body — считается псевдо-успехом. - NAT ломает технику (
synack, TCB-turnaround): нужно nftables-POSTNAT. На iptables — не работает. - Keenetic-UDP не уходит: см. §10.3 (MASQUERADE с проверкой mark).
--user nobodyпадает на Entware: указать пользователя из/etc/passwd(он там обычно есть).- Стратегия работает один раз, потом перестаёт: DPI «наказывает» —
рассмотри
circular(§9) или включи--ipcache-hostnameесли работаешь нулевой фазой. multidisorderне работает на Windows-сервере: Windows не переписывает буфер сокета по seqovl. Используйmultisplit.wssizeрежет скорость: применяй только в крайних случаях, дублируй в отдельный профиль до получения hostname.- autohostlist не наполняется: нужно перехватывать достаточно
входящих пакетов для детектора (
--in-rangeпошире) и/или достаточно исходящих ретрансмиссий. - VM не работает: гипервизорный NAT (VMware, VirtualBox) ломает большинство техник; используй bridge.
- Профиль не выбирается до hostname: если в нём есть
--hostlist=без autolist — это норма. Дублируй стратегию в профиль без хостлиста, если нужно действовать с первого пакета.
17. Отладка
Главный диагностический приём — поднять пер-пакетный лог nfqws2:
- Конфиг:
nfqws.debug = true→nfqws_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
--lua-init— при старте, один раз. Можно строкой или@file. Поддержан.gz.--lua-desync— на каждый пакет, проходящий профиль (после фильтров C-кода).- Таймеры (
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 — слепая работа.