Обновить правила репозитория и подписи исходников в 99 заметках, не затрагивая пользовательские незакоммиченные файлы. Сделать Forgejo Actions содержательным: проверять опубликованный commit, а не пустое рабочее дерево после checkout.
26 KiB
| date | tags | link | aliases | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-07-17 |
|
https://github.com/bol-van/zapret2/blob/master/docs/manual.md |
|
🔄 Схема обработки трафика в Zapret 2
[!info] О чём заметка Полный путь одного пакета внутри Zapret2/Zapret2: от перехвата из ядра ОС до финального вердикта «отправить / изменить / выбросить». Разобрана каждая стадия конвейера
nfqws2— диссекция, conntrack, распознавание протокола, реконструкция, выбор профиля, вызов Lua-инстансов и агрегация вердиктов. Изложено проще, чем в официальной документации, но без потери деталей. Про состав проекта (какие вообще есть компоненты) — соседняя заметка структура проекта; про сам механизм--lua-desync— desync.
TL;DR
- Единица обработки — IP-пакет. Ядро ОС отдаёт нужные пакеты в
nfqws2(процесс пространства пользователя), и это дорого — поэтому лишнее отсекают как можно раньше. - Каждый пакет проходит конвейер: диссекция (разбор по слоям) → conntrack (привязка к потоку) → определение типа пейлоада и протокола → при нужде реконструкция из нескольких пакетов → выбор профиля по фильтрам → вызов Lua-инстансов → итоговый вердикт.
- Профиль выбирается по фильтрам (L3/L4/L7, порты, ipset, хостлисты) один раз и кэшируется в записи потока; пересматривается максимум дважды — при распознавании L7-протокола и при обнаружении имени хоста.
- Инстанс — один вызов Lua-функции из профиля (через
--lua-desync). Инстансы выполняются строго по порядку; каждый возвращает вердиктPASS/MODIFY/DROP, и они агрегируются. - Ради экономии CPU есть механизмы cutoff (инстанс/направление/весь поток отключаются от Lua) и режим оркестратора — инстанса, который сам управляет вызовом остальных.
Стадия 0. Зачем пакет вообще передаётся в nfqws2
Сети работают с IP-пакетами, поэтому единица обработки — именно пакет. Приёмом и отправкой пакетов занимается сетевая подсистема в ядре операционной системы. А nfqws2 работает не в ядре (kernel mode), а как обычный процесс пространства пользователя (user mode). Значит, первый шаг — «вытащить» нужные пакеты из ядра и передать их в процесс nfqws2.
Все четыре средства перехвата (Linux — iptables/nftables, FreeBSD — ipfw, OpenBSD — pf, Windows — WinDivert; подробнее в структура проекта) умеют предварительно фильтровать пакеты ещё на стороне ядра. Максимальные возможности фильтрации — в Linux.
[!important] Почему ранняя фильтрация критична Передача пакета из ядра в user mode и обратно сопряжена со значительными накладными расходами (это переключение контекста между ядром и процессом на каждый пакет). Чем больше ненужного трафика отсечь прямо на перехвате, тем меньше пакетов пройдёт этот дорогой путь — и тем ниже нагрузка на процессор. Поэтому правило: отсекай лишнее как можно раньше.
Стадия 1. Диссекция — разбор пакета на части
Пакет пришёл в nfqws2. Первое, что тот делает, — разбирает его по уровням модели OSI: выделяет заголовки ip, ipv6, tcp, udp и поле данных (payload). Это называется диссекцией, а её результат — диссект (dis): представление пакета в виде структур, к полям которых можно обращаться по имени.
[!note] Проще говоря Пришедший пакет — это сплошная лента байт. Диссекция «раскладывает» её по полочкам: вот IP-заголовок, вот TCP-заголовок, вот прикладные данные. После этого Lua-код и C-ядро могут читать и менять конкретные поля (например, TCP sequence number), а не считать байты вручную. Модель OSI — стандартное деление сети на уровни: IP — сетевой уровень, TCP/UDP — транспортный, данные приложения — выше.
Стадия 2. conntrack — привязка пакета к потоку
Дальше включается встроенная в nfqws2 подсистема conntrack (connection tracking, отслеживание соединений) — система учёта потоков поверх отдельных пакетов. По данным уровней L3/L4 пакета (адреса, порты, протокол) ищется уже существующая запись о потоке; если её нет — создаётся новая. Старые записи, по которым давно нет активности, удаляются.
Что conntrack хранит и делает для каждого потока:
- отслеживает логическое направление каждого пакета (входящий / исходящий);
- ведёт подсчёт прошедших пакетов и байт в обе стороны;
- следит за sequence numbers для TCP;
- при необходимости используется для сборки сообщений, которые передаются несколькими пакетами (см. стадию 4).
[!note] Проще говоря Отдельный пакет сам по себе мало что говорит. conntrack связывает пакеты в «разговор» (поток) между двумя сторонами и помнит про него контекст: сколько всего прошло, в какую сторону, на каком месте потока мы сейчас. Этот контекст нужен почти всем дальнейшим решениям.
Стадия 3. Тип пейлоада и протокол потока
По сигнатурам определяется тип пейлоада — что за содержимое в этом конкретном пакете (или группе пакетов): TLS ClientHello, HTTP-запрос, QUIC Initial и т. д. (см. payload). На основании типа пейлоада определяется тип протокола всего потока, и он закрепляется за потоком до конца его существования.
Тонкость: в рамках одного потока могут идти разные типы пейлоадов. Например, поток протокола XMPP обычно несёт и специфические XMPP-сообщения, и сообщения, связанные с TLS. Тип протокола потока при этом остаётся XMPP, но отдельные пакеты получают разные типы пейлоада — как известные, так и неизвестные. Нераспознанный пейлоад помечается типом unknown.
[!note] Проще говоря «Протокол потока» — это ярлык на весь разговор целиком (он ставится один раз и не меняется). «Тип пейлоада» — ярлык на конкретный пакет внутри этого разговора (у разных пакетов он может отличаться). Многие приёмы обхода реагируют именно на тип пейлоада конкретного пакета.
Стадия 4. Реконструкция сообщения из нескольких пакетов (reasm / decrypt)
Иногда одно логическое сообщение не помещается в один пакет (классический пример — большой TLS ClientHello с post-quantum ключами). Если для конкретного пейлоада и протокола потока выясняется, что нужна реконструкция из нескольких пакетов, nfqws2:
- начинает накапливать эти пакеты, привязывая их к записи в conntrack;
- запрещает их немедленную отправку (держит, пока не соберёт всё);
- после приёма всех частей выполняет реконструкцию, а при необходимости — дешифровку составного пейлоада.
Дальнейшие решения принимаются уже на базе полностью собранного пейлоада — он называется reasm (reassembled, реассемблированный), а результат сборки с расшифровкой — decrypt.
[!note] Проще говоря Если сообщение «разрезано» сетью на несколько пакетов,
nfqws2придерживает их, склеивает обратно в целый пейлоад (reasm) и только тогда даёт стратегиям работать с полной картиной. Иначе, например, домен в SNI мог бы оказаться разорван между пакетами, и его нельзя было бы корректно найти или разрезать.
Стадия 5. Классификация по профилям
Когда информация о пейлоаде получена, наступает очередь системы классификации по профилям. profile — это набор из фильтров (кому он адресован) и команд-действий (что делать). Профили фильтруются по:
- L3 — версия IP-протокола, номер IP-протокола,
ipset-ы (списки IP-адресов); - L4 — порты TCP или UDP,
type/codeдля ICMP; - L6/L7 — тип протокола потока, списки доменов (хостлисты).
Правило выбора: профили сканируются строго по порядку, от первого к последнему. При первом же совпадении фильтра этот профиль выбирается, и сканирование прекращается. Если не подошёл ни один — берётся профиль по умолчанию, в котором нет никаких действий (то есть с трафиком ничего не делается).
[!important] Кэширование и переключение профиля Выбранный профиль кэшируется в записи conntrack, поэтому для каждого следующего пакета потока поиск заново не выполняется — это экономит CPU. Повторный поиск запускается только при изменении исходных данных, а таких изменений всего два: обнаружение L7-протокола и обнаружение имени хоста. В этих случаях профиль может быть пересмотрен и переключён. За всё время жизни потока таких переключений может быть не более двух — ровно потому, что изменяемых параметра всего два.
Стадия 6. Инстансы — вызовы Lua-функций внутри профиля
Профиль выбран. За его действия отвечают Lua-функции, и их в профиле может быть произвольное количество. Каждый отдельный вызов Lua-функции из профиля называется инстансом (экземпляром вызова).
Зачем нужно понятие инстанса: одна и та же функция может вызываться несколько раз с разными параметрами. Поэтому инстанс идентифицируется парой «номер профиля + порядковый номер вызова внутри профиля». Инстансы задаются флагами [[desync|--lua-desync]], и каждый получает свой набор произвольных параметров.
[!important] Порядок вызова имеет значение Инстансы выполняются строго в том порядке, в каком заданы флаги
--lua-desync. Этот порядок принципиален для логики стратегии: например, сначала отправить fake, потом нарезать реальный — и наоборот это уже другая стратегия. Подробнее про очерёдность — последовательность аргументов.
Внутрипрофильные фильтры
Кроме фильтров выбора профиля есть ещё внутрипрофильные фильтры — они сужают, какие пакеты дойдут до конкретных инстансов. Их три типа:
--payload— список типов пейлоада, которые инстанс принимает (см. payload);--in-rangeи--out-range— два диапазонных фильтра, задающих диапазон позиций внутри потока, интересный инстансу (см. out-range).
Определённый внутрипрофильный фильтр действует на все последующие инстансы — до тех пор, пока его не переопределят. Главный смысл этих фильтров — сократить число относительно медленных вызовов Lua, приняв максимум решений на быстрой стороне C-кода.
Стадия 7. Что происходит внутри Lua-инстанса
Пакет дошёл до Lua-инстанса. Функция имеет два параметра — ctx и desync:
ctx— контекст для связи с некоторыми функциями на стороне C-кода (через него, например, реально отправляются пакеты);desync— таблица со множеством параметров обрабатываемого пакета. Прежде всего это диссект (подтаблицаdis) и информация из записи conntrack (подтаблицаtrack). Есть и ряд других параметров — увидеть их все можно, выполнивvar_debug(desync)или просто подключив готовый инстансpktdebug.
Что ещё доступно инстансу:
- Информация о replay. Если идёт перепроигрывание задержанных пакетов (replay — повторная прогонка накопленных при реконструкции частей), инстанс получает номер части, общее количество частей исходного сообщения, позицию текущей части и
reasm/decryptпри наличии. - Хранение состояния между пакетами. Для данных, не относящихся к текущему пакету, доступна таблица
desync.track.lua_state— в ней можно хранить любую информацию, привязанную к записи conntrack: при каждом новом пакете потока Lua получает ту же самую таблицу. - Передача данных между инстансами. Саму таблицу
desyncможно использовать для временных данных в рамках обработки текущего пакета. Следующие инстансы получают ту жеdesyncи потому могут принять данные от предыдущих.
Механику desync, ctx и полей на конкретном примере разбирает заметка multisplit (раздел про вход и выход функции).
Стадия 8. Вердикты и их агрегация
Инстанс может создавать копии текущего диссекта, вносить в них изменения, генерировать собственные диссекты и отправлять их через вызовы C-кода. Итог работы каждого инстанса — вердикт:
VERDICT_PASS— ничего не делать с текущим диссектом;VERDICT_MODIFY— в конце всей цепочки отправить модифицированное содержимое диссекта;VERDICT_DROP— выбросить (дропнуть) текущий диссект.
Вердикты всех инстансов профиля агрегируются по правилу приоритета: MODIFY замещает PASS, а DROP замещает и PASS, и MODIFY. То есть достаточно одному инстансу вернуть DROP — и оригинальный пакет будет выброшен, что бы ни вернули остальные.
[!note] Проще говоря Почему
DROPсильнее всех: приёмы вроде multisplit уже сами отправили нужные данные отдельными пакетами, поэтому оригинал надо выбросить, иначе сервер получит данные дважды.DROPперебиваетMODIFYиPASS, чтобы такое дублирование было невозможно.
Стадия 9. Cutoff и оркестратор — экономия и динамика
Чтобы не гонять через Lua лишние пакеты, у инстанса есть несколько способов «отключиться»:
- instance cutoff — инстанс сам себя отключает от дальнейших пакетов потока по направлению in/out;
- lua cutoff — отключает направление in/out текущего потока от всей Lua-обработки;
- отмена дальнейшей цепочки — инстанс может запросить прекращение всех оставшихся вызовов Lua по текущему диссекту.
Инстанс, который берёт на себя такую отмену, становится координатором дальнейших действий — его называют оркестратором (см. оркестратор). C-код передаёт ему план дальнейшего выполнения — все фильтры профиля и параметры вызова всех оставшихся инстансов, — и оркестратор сам решает, когда и при каких условиях их вызывать, не вызывать или менять их параметры. Так реализуются динамические сценарии без переписывания основного кода стратегии — например, распознать блокировку ресурса и сменить стратегию, если предыдущая не сработала.
[!important] Признак «lua cutoff» как оптимизация Если все инстансы профиля вошли в состояние cutoff по текущему потоку, либо текущая позиция потока ушла за верхнюю границу range-фильтров, значит по этому потоку Lua-вызовов больше не будет. C-код помечает такой поток специальным признаком «lua cutoff», который проверяется максимально быстро, без единого вызова Lua. Так экономятся ресурсы процессора: «отработавшие» потоки дальше идут по быстрому пути.
Стадия 10. Итоговый вердикт и следующий пакет
После выполнения всей цепочки инстансов профиля C-код получает итоговый агрегированный вердикт — что делать с текущим диссектом: отправить как есть (PASS), отправить модифицированный вариант (MODIFY) или выбросить (DROP).
На этом обработка пакета завершается: nfqws2 переходит к ожиданию следующего пакета, и весь цикл повторяется заново.
[!summary] Весь путь пакета одной строкой Ядро ОС → перехват и ранняя фильтрация → диссекция → conntrack → тип пейлоада и протокол потока → (при нужде) реконструкция reasm/decrypt → выбор профиля по фильтрам → цепочка Lua-инстансов с их вердиктами → агрегированный вердикт → отправить / изменить / дропнуть → ждать следующий пакет.
📚 См. также
- структура проекта — из каких компонентов состоит инструмент (парная заметка: там «что есть», здесь «как работает»)
- Zapret2/Zapret2 — обзор и определение автора
- desync — как задаются и вызываются инстансы
- структура desync и диссекта — полный разбор полей
desync, диссекта,track, reasm/replay, вердиктов - profile и preset — как описываются стратегии
- filter · payload · out-range — детали классификации
- оркестратор — динамический выбор стратегии
- multisplit — конкретный пример инстанса: что он получает в
desyncи что возвращает - последовательность аргументов — почему порядок инстансов важен
- 🔗 Официальная документация (docs/manual.md) — первоисточник
[!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование и скачивание всего репозитория одним zip-архивом доступны в Forgejo: исходник этой заметки · весь репозиторий.