| 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 == firewall queue 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 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 | 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 (default 0x40000000). Все 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).
- Перехват 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.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. Чеклист «не находит ни одной рабочей стратегии»
Проверять В ЭТОМ ПОРЯДКЕ:
- Сайт доступен без обхода → всегда 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 ≠ firewall queue 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)
return VERDICT_PASS
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)
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
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 — слепая работа.