# circular — оркестратор циклического переключения стратегий `circular` — Lua-оркестратор, который автоматически переключает стратегию десинхронизации при обнаружении неудач (RST, ретрансмиссии, HTTP-редирект DPI). **Требует** перехвата входящего трафика (`--in-range`) для детектирования RST и HTTP-ответов. --- ## Синтаксис ``` --lua-desync=circular[:param=val[:...]] --lua-desync=<func>:strategy=1[:final] --lua-desync=<func>:strategy=2 ... ``` Каждый подчинённый инстанс **обязан** иметь `strategy=N`, где N начинается с 1 без пропусков. --- ## Параметры circular | Параметр | Описание | По умолчанию | |---|---|---| | `fails=N` | Порог неудач для переключения стратегии | `3` | | `time=N` | Сбросить счётчик неудач, если последняя была > N секунд назад | `60` | | `failure_detector=func` | Имя кастомной Lua-функции детектора неудач | `standard_failure_detector` | | `success_detector=func` | Имя кастомной Lua-функции детектора успеха | `standard_success_detector` | | `hostkey=func` | Имя кастомной функции генератора ключа хоста | `standard_hostkey` | | `key=string` | Имя таблицы в `autostate` — изоляция состояния между несколькими `circular` | автоматически | | `nld=N` | Обрезать hostname до N-уровневого домена (`static.a.google.com` → `google.com` при `nld=2`) | без обрезки | | `reqhost` | Не работать, если hostname неизвестен (только IP) | выкл. | --- ## Параметры на подчинённых инстансах | Параметр | Описание | |---|---| | `strategy=N` | **Обязательно.** Номер стратегии, начиная с 1, без пропусков | | `final` | Финальная стратегия — ротация на ней останавливается | --- ## Параметры standard_failure_detector Передаются прямо в `circular` (не в подчинённые инстансы). ### TCP | Параметр | Описание | По умолчанию | |---|---|---| | `retrans=N` | Кол-во ретрансмиссий = неудача | `3` | | `maxseq=N` | Считать ретрансмиссии только до этой relative sequence | `32768` | | `reset` | Слать RST ретрансмиттеру (ускоряет разрыв зависшего соединения) | выкл. | | `inseq=N` | Входящий RST считается неудачей только до этой rseq | `4096` | | `no_rst` | Отключить триггер по входящему RST | выкл. | | `no_http_redirect` | Отключить триггер по HTTP-редиректу DPI | выкл. | ### UDP | Параметр | Описание | По умолчанию | |---|---|---| | `udp_out=N` | Неудача если исходящих пакетов >= N | `4` | | `udp_in=N` | Неудача если входящих пакетов <= N | `1` | --- ## Параметры standard_success_detector | Параметр | Описание | По умолчанию | |---|---|---| | `maxseq=N` | TCP: если исходящая rseq > N — соединение успешно | `32768` | | `inseq=N` | TCP: если входящая rseq > N — соединение успешно | `4096` | | `udp_in=N` | UDP: если входящих пакетов > N — успех | `1` | --- ## Стратегия из нескольких фаз Одна стратегия может включать **несколько инстансов** с одинаковым `strategy=N`. `circular` проходит весь план и выполняет **все** инстансы, у которых номер совпадает — по порядку. ```bash --lua-desync=circular:fails=1:time=300 --lua-desync=fake:blob=fake_default_http:repeats=4:strategy=1 # фаза 1 --lua-desync=multisplit:pos=2:seqovl=211:strategy=1 # фаза 2 --lua-desync=multidisorder:pos=host:strategy=2:final ``` Для стратегии 1 последовательно выполнятся `fake` → `multisplit`. ### Форматирование в config Параметры внутри `NFQWS2_OPT="..."` можно переносить на новые строки и делать отступы — shell разбирает их как обычные пробелы: ```bash NFQWS2_OPT=" --filter-tcp=443 --filter-l7=tls <HOSTLIST> --payload=tls_client_hello --out-range=-d1000 --in-range=-s5556 --lua-desync=circular:fails=1:time=300:retrans=3:nld=2 --lua-desync=fake:blob=fake_default_http:repeats=4:strategy=1 --lua-desync=multisplit:pos=2:seqovl=211:strategy=1 --lua-desync=multidisorder:pos=host:strategy=2:final --new " ``` --- ## Пример ``` --in-range=-s5556 --out-range=-d1000 --lua-desync=circular:fails=1:time=300:retrans=3:nld=2 --lua-desync=multisplit:pos=2:strategy=1 --lua-desync=fake:blob=fake_default_http:strategy=2 --lua-desync=multidisorder:pos=host:strategy=3:final ``` - `fails=1` — переключить стратегию после 1 неудачи - `time=300` — сбросить счётчик если последняя неудача была > 5 мин назад - `retrans=3` — неудача = 3 ретрансмиссии - `nld=2` — ключ хоста = 2-уровневый домен (`google.com`) - `final` на стратегии 3 — остановить ротацию на ней --- ## Как работает 1. Пакет приходит в `circular` с `ctx` 2. `circular` берёт план (все подчинённые инстансы) и отменяет нормальный цикл C 3. Проверяет детектор неудач/успеха для текущего соединения 4. Если неудач >= `fails` — переходит к следующей стратегии `(N % total) + 1` 5. Если достигнута `final`-стратегия — ротация останавливается 6. Выполняет **только** инстансы с `strategy=текущая` Состояние (номер стратегии, счётчик неудач) хранится **per-host** в глобальной таблице `autostate` и переживает отдельные соединения.