# Zapret2: start/cutoff и почему `n2<n3` “вдруг” попадает на ClientHello
Эта заметка — ответ на типичную ситуацию:
```text
--filter-tcp=443 --payload=all --out-range="n1<n2" --lua-desync=send:ip_ttl=1
```
→ дюпается SYN
```text
--filter-tcp=443 --payload=all --out-range="n2<n3" --lua-desync=send:ip_ttl=1
```
→ дюпается ClientHello, хотя “по идее должен ACK”
## 1) Ключевой факт: `n` — это номер ПЕРЕХВАЧЕННОГО пакета, а не “всех пакетов TCP”
`--out-range` работает по счётчикам conntrack **внутри движка**.
Счётчик `n` увеличивается только на те исходящие пакеты, которые:
1) реально перехвачены (NFQUEUE/WinDivert), и
2) реально дошли до обработки профилем (фильтры не выкинули их раньше).
Если какой-то пакет “не попал” — он **не существует** для счётчика `n`.
## 2) Почему именно ACK часто “пропадает”
### Windows / winws2
В winws2 по умолчанию часто включено:
- `--wf-tcp-empty=0`
Это не перехватывает пустые TCP ACK (ACK без payload).
Сильный профит по CPU, но `n` перестаёт совпадать с тем, что вы ожидаете как “номер пакета в соединении”.
Итог: исходящий порядок “в движке” становится таким:
- `n1` = SYN (перехвачен)
- `n2` = ClientHello (первый перехваченный packet с данными)
А “реальный” handshake ACK существует, но не перехвачен.
### Linux / nfqws2
Если ваши правила NFQUEUE (nft/iptables) перехватывают только “пакеты с данными” или только часть фаз — ситуация аналогичная:
ACK просто не попадёт в userspace, и `n` сдвинется.
## 3) Вторая причина (реже): ACK может быть “склеен” с ClientHello
Иногда стек может отправить ACK на SYN-ACK вместе с первым сегментом данных (ACK+payload).
Тогда “второй исходящий пакет” реально будет ClientHello (и это нормально).
Проверяется только захватом трафика (Wireshark).
## 4) Эквивалент `nfqws1 --dup-start=n2 --dup-cutoff=n3` в zapret2
Если вы хотите **строго** применить действие к “2-му исходящему пакету” по счётчику `n`:
```bash
--out-range="n2<n3" --lua-desync=send:ip_ttl=1
```
Но это будет работать “как ожидаете” только если этот пакет реально перехвачен.
## 5) Как применить “дурение” именно ко 2-му исходящему ACK (если он пустой)
### Вариант A (Windows / winws2): включить перехват пустых ACK
1) включить перехват пустых TCP пакетов:
```bash
--wf-tcp-empty=1
```
2) сузить по типу payload (очень полезно, чтобы убедиться что вы бьёте именно по пустому):
```bash
--payload=empty --out-range="n2<n3" --lua-desync=send:ip_ttl=1
```
Если после этого “ничего не дюпается” — значит либо ACK не перехвачен фильтрами, либо ACK не пустой.
### Вариант B: если цель — “первый пакет с данными” (ClientHello), используйте `d`
Это устойчиво даже если пустые ACK не перехватываются:
```bash
--payload=tls_client_hello --out-range="d1<d2" --lua-desync=send:ip_ttl=1
```
## 6) Самый быстрый способ понять, что реально происходит
Добавьте на время дебага:
```bash
--lua-desync=posdebug
--lua-desync=luaexec:code="DLOG('flags '..tostring(desync.dis.tcp and desync.dis.tcp.th_flags)..' payload '..#desync.dis.payload..' l7payload '..tostring(desync.l7payload))"
```
И смотрите:
- какая длина payload у “n2”
- какой `l7payload` (empty/tls_client_hello/unknown)
- увеличивается ли `n` на пустых ACK