todo/Zapret2/структура desync и диссекта.md
loop-uh 08b4491545
Some checks failed
Published content check / validate (push) Failing after 3s
Завершить переезд базы знаний на Forgejo
Обновить правила репозитория и подписи исходников в 99 заметках, не затрагивая пользовательские незакоммиченные файлы. Сделать Forgejo Actions содержательным: проверять опубликованный commit, а не пустое рабочее дерево после checkout.
2026-08-07 08:27:28 +03:00

358 lines
35 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
date: 2026-07-17
tags:
- zapret
- zapret2
- nfqws2
- lua
- lua-desync
- desync
- dissect
- conntrack
- reference
link: https://github.com/bol-van/zapret2/blob/master/docs/manual.md
aliases:
- Структура таблицы desync
- Прототип desync-функции
- Диссект nfqws2
- desync API
- Структура track
- Поля desync
---
# 🧬 Прототип desync-функции и структура таблицы `desync`
> [!info] О чём заметка
> Подробный справочник по **данным**, с которыми работает Lua desync-функция в [[Zapret2/Zapret2|Zapret 2]]: как объявляется функция, что она получает в таблице `desync`, из чего состоит диссект пакета (`dis`), запись потока (`track`), как приходят собранные из нескольких пакетов пейлоады (reasm/decrypt), и в чём особенности ICMP и raw IP. Сам механизм вызова `--lua-desync` (как и зачем) описан в [[desync]]; где вызов стоит в общем конвейере — в [[схема обработки трафика]]; из чего состоит проект — в [[структура проекта]]. Эта заметка — про **устройство входных данных**, то есть то, что нужно, чтобы писать или читать desync-функции.
## TL;DR
- Прототип: `function desync_f(ctx, desync)`. `ctx` — ручка для вызова C-функций (отправка, cutoff), `desync` — таблица со всеми данными пакета.
- Функция возвращает **вердикт**: `VERDICT_PASS` (не трогать), `VERDICT_MODIFY` (отправить изменённый диссект), `VERDICT_DROP` (выбросить); ничего не вернуть = `PASS`. Отдельный бит `VERDICT_PRESERVE_NEXT` сохраняет поля «next protocol» в IPv6.
- Вердикты инстансов **агрегируются**: `MODIFY` перебивает `PASS`, `DROP` перебивает оба; `PRESERVE_NEXT` — если его вернул хоть один инстанс.
- Изучать содержимое `desync` удобнее всего готовым инстансом **`pktdebug`**.
- `desync.dis` — разобранный пакет (поля повторяют C-структуры из `netinet/*.h`; числа уже в machine byte order). `desync.track` — данные потока из conntrack (может отсутствовать — всегда проверяйте!).
- Многобайтовые числа переведены в порядок машины автоматически, **но sequence numbers 32-битные беззнаковые** — для арифметики используйте `u32add`/`bitand`, а не обычное сложение.
---
## Прототип Lua desync-функции
Любая desync-функция объявляется с двумя параметрами:
```lua
function desync_f(ctx, desync)
-- ... тело ...
end
```
- **`ctx`** — контекст для вызова некоторых C-функций. Сам по себе он не предназначен для чтения: его передают обратно в C (при отправке пакетов, при cutoff), чтобы ядро понимало, к какому пакету и очереди относится вызов.
- **`desync`** — таблица, содержащая **все** передаваемые в функцию значения: аргументы инстанса, диссект текущего пакета, данные потока и многое другое. Это главный вход функции.
> [!note] Проще говоря
> `desync` — это «вот пакет и всё, что мы про него знаем». `ctx` — «вот линия связи обратно в C-ядро, чтобы что-то отправить или отключиться». Первое читают, второе используют как ручку для команд.
### Вердикты — что функция возвращает
Функция возвращает **вердикт по текущему пакету**. Можно не возвращать ничего — тогда результат приравнивается к `VERDICT_PASS`.
| Вердикт | Что делает |
|:--------|:-----------|
| `VERDICT_PASS` | передать пакет как есть, **без учёта** изменений диссекта |
| `VERDICT_MODIFY` | выполнить реконструкцию и отправку **текущего (изменённого) диссекта** |
| `VERDICT_DROP` | дропнуть (выбросить) текущий пакет |
| `VERDICT_PRESERVE_NEXT` | **отдельный бит**, который прибавляется к основному вердикту |
**Про `VERDICT_PRESERVE_NEXT`.** Это не самостоятельный вердикт, а флаг, добавляемый к основному (например, `VERDICT_MODIFY + VERDICT_PRESERVE_NEXT`). Он велит использовать поля «next protocol» в IPv6-заголовке и IPv6 extension headers **как есть**. Без него эти поля генерируются автоматически по содержимому диссекта. Нужен, когда вы вручную выстраиваете цепочку заголовков и не хотите, чтобы ядро её пересчитало.
> [!important] Агрегация вердиктов по цепочке инстансов
> Результат всех `--lua-desync`-инстансов профиля объединяется по приоритету: `VERDICT_MODIFY` замещает `VERDICT_PASS`, а `VERDICT_DROP` замещает их обоих. `VERDICT_PRESERVE_NEXT` применяется, если его вернул **хотя бы один** инстанс. То есть достаточно одному инстансу вернуть `DROP` — оригинал будет выброшен, что бы ни вернули остальные.
---
## Как изучать `desync` вживую
Структуру `desync` лучше всего изучать не по таблицам, а по её **реальному содержимому** на конкретном пакете. Для этого в `zapret-lib.lua` есть готовая тестовая функция-инстанс **`pktdebug`** — она выводит всё содержимое `desync` в лог. Достаточно добавить её в профиль:
```bash
--lua-desync=pktdebug
```
Дальнейшие таблицы полей — это как раз то, что вы увидите в выводе `pktdebug` (примеры ниже сняты, в частности, на HTTP-запросе по IPv6 к `http://one.one.one.one`).
---
## Структура таблицы `desync`
Верхний уровень `desync` — это «паспорт» пакета: кто его обрабатывает (какой инстанс/профиль), куда он идёт, что за протокол, и вложенные таблицы с самим пакетом (`dis`) и потоком (`track`).
| Поле | Тип | Содержание | Примечание |
|:-----|:----|:-----------|:-----------|
| `func` | string | имя desync-функции | |
| `func_n` | number | номер инстанса внутри профиля | |
| `func_instance` | string | название инстанса | производная от имени функции, номера инстанса и номера профиля |
| `profile_n` | number | номер профиля | |
| `profile_name` | string | название профиля | может отсутствовать |
| `cookie` | string | значение параметра `--cookie` для профиля | может отсутствовать |
| `outgoing` | bool | `true`, если направление исходящее | |
| `ifin` | string | имя входящего интерфейса | может отсутствовать |
| `ifout` | string | имя исходящего интерфейса | может отсутствовать |
| `fwmark` | number | fwmark текущего пакета | только в Linux |
| `target` | table | ip-адрес и порт, по которым проверяются ipset-ы и фильтры по портам | |
| `replay` | bool | идёт проигрывание задержанного пакета (replay) | |
| `replay_piece` | number | номер проигрываемой части | нумерация с 1 |
| `replay_piece_count` | number | количество проигрываемых частей | |
| `replay_piece_last` | bool | последняя проигрываемая часть | |
| `l7payload` | string | тип пейлоада текущего пакета или группы пакетов | если неизвестно — `unknown` |
| `l7proto` | string | тип протокола потока | если неизвестно — `unknown` |
| `reasm_data` | string | результат сборки многопакетного сообщения, либо сам пейлоад, если сборки не было | пока только для TCP |
| `reasm_offset` | number | смещение текущего перепроигрываемого пакета в сборке | пока только для TCP |
| `decrypt_data` | string | результат сборки и дешифровки пейлоадов нескольких пакетов | применяется для QUIC |
| `tcp_mss` | number | MSS противоположного конца TCP-соединения | присутствует всегда, только для TCP |
| `track` | table | данные, привязанные к записи conntrack | только если есть conntrack, может не быть |
| `arg` | table | все аргументы инстанса и их значения | подстановки `%` и `#` уже замещены |
| `dis` | table | диссект текущего пакета | |
> [!note] `l7payload` vs `l7proto`
> `l7proto` — тип протокола **всего потока** (ставится один раз и держится до конца). `l7payload` — тип содержимого **конкретного пакета** внутри этого потока; у разных пакетов одного потока он может отличаться, а нераспознанный помечается как `unknown`. Подробнее про распознавание — [[payload]].
---
## Структура диссекта (`desync.dis`)
**Диссект** — это разобранный на поля текущий пакет. Прежде чем читать таблицы, важно понять три общих правила устройства диссекта:
1. **Версия IP и L4-протокол определяются по наличию полей.** Есть `dis.ip` → это IPv4, есть `dis.ip6` → IPv6. Есть `dis.tcp` → транспорт TCP, есть `dis.udp` → UDP. Не «читайте номер протокола», а проверяйте наличие подтаблицы.
2. **Имена полей повторяют C-структуры.** Таблицы заголовков копируют названия полей из системных заголовков `netinet/{ip,ip6,tcp,udp}.h`. IP-адреса и IPv4 options передаются как «сырая» строка (raw string) — для перевода raw IP в текст есть C-функция `ntop` (она сама определяет версию по размеру). IPv6 extension headers и TCP options представлены таблицами.
3. **Порядок байт уже машинный.** Все многобайтовые числовые значения автоматически переведены из сетевого порядка байт (network byte order) в порядок машины (machine byte order) — читать и сравнивать их можно как обычные числа. Исключение по осторожности — sequence numbers, см. [раздел ниже](#работа-с-sequence-numbers).
### Верхний уровень диссекта
| Поле | Тип | Описание |
|:-----|:----|:---------|
| `ip` | table | заголовок IPv4 |
| `ip6` | table | заголовок IPv6 |
| `frag_off` | number | смещение IP-фрагмента; присутствует **только** в IP-фрагментах |
| `tcp` | table | заголовок TCP |
| `udp` | table | заголовок UDP |
| `icmp` | table | заголовок ICMP |
| `l4proto` | number | `IPPROTO_TCP` или `IPPROTO_UDP` |
| `transport_len` | number | длина пакета без L3-заголовков |
| `l3_len` | number | длина L3-заголовков, включая ip options и IPv6 extension headers |
| `l4_len` | number | длина L4-заголовка, включая tcp options |
| `payload` | string | L4-пейлоад (или содержимое после L3-заголовков для raw IP) |
### Заголовок IPv4 (`dis.ip`)
| Поле | Описание |
|:-----|:---------|
| `ip_v` | версия IP — 4 |
| `ip_hl` | длина IP-заголовка в блоках по 4 байта (5 без ip options) |
| `ip_tos` | type of service; содержит DSCP |
| `ip_len` | полная длина IP-пакета со всеми заголовками и пейлоадом |
| `ip_id` | идентификатор пакета для сборки из фрагментов |
| `ip_off` | offset фрагмента, флаги MF (more fragments) и DF (don't fragment) |
| `ip_ttl` | time to live — максимальное число хопов |
| `ip_p` | номер IP-протокола (как правило `IPPROTO_TCP` или `IPPROTO_UDP`) |
| `ip_sum` | контрольная сумма IP-заголовка |
| `ip_src` | IP источника |
| `ip_dst` | IP назначения |
| `options` | бинарный блок ip options (почти не используется, режется всеми) |
### Заголовок IPv6 (`dis.ip6`)
| Поле | Описание |
|:-----|:---------|
| `ip6_flow` | первые 4 байта IPv6-заголовка: version (6), traffic class, flow label |
| `ip6_plen` | длина пакета за вычетом базового заголовка IPv6 — `IP6_BASE_LEN` (40 байт) |
| `ip6_nxt` | следующий протокол; если нет exthdr — `IPPROTO_TCP` (6) или `IPPROTO_UDP` (17) |
| `ip6_hlim` | hop limit (тот же смысл, что TTL в IPv4) |
| `ip6_src` | IPv6-адрес источника |
| `ip6_dst` | IPv6-адрес приёмника |
| `exthdr` | массив таблиц расширенных заголовков (индекс с 1) |
### IPv6 extension header (`dis.ip6.exthdr[i]`)
| Поле | Описание |
|:-----|:---------|
| `type` | тип заголовка: `IPPROTO_HOPOPTS`, `IPPROTO_ROUTING`, `IPPROTO_DSTOPTS`, `IPPROTO_MH`, `IPPROTO_HIP`, `IPPROTO_SHIM6`, `IPPROTO_FRAGMENT`, `IPPROTO_AH` |
| `next` | тип следующего заголовка (аналогично `type`); для последнего может быть `IPPROTO_TCP`/`IPPROTO_UDP` |
| `data` | данные без первых двух байт (типа и длины) |
### Заголовок UDP (`dis.udp`)
| Поле | Описание |
|:-----|:---------|
| `uh_sport` | порт источника |
| `uh_dport` | порт приёмника |
| `uh_ulen` | длина UDP — `UDP_BASE_LEN` (8) + длина пейлоада |
| `uh_sum` | контрольная сумма UDP |
### Заголовок TCP (`dis.tcp`)
| Поле | Описание |
|:-----|:---------|
| `th_sport` | порт источника |
| `th_dport` | порт приёмника |
| `th_x2` | зарезервированное поле; используется для расширенных TCP-флагов |
| `th_off` | размер TCP-заголовка в блоках по 4 байта |
| `th_flags` | TCP-флаги: `TH_FIN`, `TH_SYN`, `TH_RST`, `TH_PUSH`, `TH_ACK`, `TH_URG`, `TH_ECE`, `TH_CWR` |
| `th_seq` | sequence number |
| `th_ack` | acknowledgement number |
| `th_win` | размер TCP-окна |
| `th_sum` | контрольная сумма TCP |
| `th_urp` | urgent pointer |
| `options` | массив таблиц TCP-опций (индекс с 1) |
### TCP-опция (`dis.tcp.options[i]`)
| Поле | Описание |
|:-----|:---------|
| `kind` | тип опции: `TCP_KIND_END`, `TCP_KIND_NOOP`, `TCP_KIND_MSS`, `TCP_KIND_SCALE`, `TCP_KIND_SACK_PERM`, `TCP_KIND_SACK`, `TCP_KIND_TS`, `TCP_KIND_MD5`, `TCP_KIND_AO`, `TCP_KIND_FASTOPEN` |
| `data` | данные опции без `kind` и длины; отсутствует для `TCP_KIND_END` и `TCP_KIND_NOOP` |
### Заголовок ICMP (`dis.icmp`)
Заголовком ICMP считается его постоянная часть — **первые 8 байт**. Они универсальны и для IPv4-, и для IPv6-версии ICMP. Всё остальное содержимое зависит от типа ICMP и лежит в `payload` — включая возможные специальные поля заголовка или обрезанный исходный пакет, на который был сгенерирован ICMP.
| Поле | Описание |
|:-----|:---------|
| `icmp_type` | тип ICMP |
| `icmp_code` | код ICMP |
| `icmp_cksum` | контрольная сумма ICMP |
| `icmp_data` | 32-битное поле данных со смещения 4 |
---
## Приём многопакетных пейлоадов (reasm / decrypt / replay)
Иногда одно логическое сообщение не помещается в один пакет. Его сборка называется **reasm** (reassemble). Она выполняется автоматически C-кодом, если встречен пейлоад, требующий сборки, доступен conntrack и сборка не запрещена флагом `--reasm-disable`. На данный момент таких пейлоадов два — **`tls_client_hello`** и **`quic_initial`**: оба могут содержать постквантовую криптографию Kyber, которая не влезает в один пакет.
Сборка идёт по-разному:
- **`tls_client_hello`** — обычная сборка: пейлоады последовательных TCP-сегментов объединяются в единый блок `reasm_data`.
- **`quic_initial`** — отдельные пакеты накапливаются во внутреннем буфере, затем происходит их дешифровка, объединение и **дефрагментация** частей пейлоада, разбросанных по пакетам и разным смещениям (так делает Chrome — намеренно, чтобы все реализации следовали стандарту и умели корректно собирать пейлоад по частям).
### Как это видно в Lua (replay)
Пока сборка не финализирована, пакеты копятся во внутреннем буфере **без вызовов Lua**. Как только сборка завершена, начинается **перепроигрывание** отдельных частей (replay): в Lua-инстансы приходит диссект каждого задержанного пакета, но при этом выставлены поля `desync.replay=true`, `desync.replay_piece`, `desync.replay_piece_count` и `desync.replay_piece_last`.
Различие между TCP и QUIC в том, какое поле несёт собранный результат:
- **TCP-сборка:** выставляется `desync.reasm_data` — полный блок собранных данных. При этом `desync.dis.payload` по-прежнему содержит пейлоад **отдельного** перепроигрываемого пакета. Если replay нет (одиночный пакет), для TCP `desync.reasm_data` содержит копию `desync.dis.payload`.
- **QUIC-сборка:** `desync.reasm_data` **отсутствует** — вместо него передаётся `desync.decrypt_data` с результатом дешифровки и дефрагментации всех пейлоадов сборки. (Для QUIC этот `decrypt_data` содержит `tls_client_hello` без record layer.)
> [!note] Проще говоря
> `reasm_data`/`decrypt_data` — это «целое собранное сообщение», а `dis.payload` — «кусок из конкретного пакета». Стратегии, которым нужна полная картина (например, найти домен в SNI и разрезать по нему), работают с собранным блоком; отправка же по-прежнему идёт по отдельным пакетам-частям. Как это использует конкретная функция — см. [[multisplit]].
---
## Структура `track` (данные потока из conntrack)
Таблица `track` присутствует в `desync` **только если** для текущего пакета нашлась запись в conntrack (системе [[схема обработки трафика|отслеживания потоков]]). Её может не быть, если `nfqws2` не видел пакет SYN или SYN,ACK: соединение установили до запуска `nfqws2`, вы не перехватили SYN/SYN,ACK из ядра, либо conntrack принудительно выключен через `--ctrack-disable`.
> [!danger] Всегда проверяйте наличие track
> Ваш код обязан проверять наличие `desync.track` (и опциональных полей внутри него) **перед** обращением — иначе он упадёт с ошибкой на пакетах без conntrack. Проверяйте свой код с `--ctrack-disable` и на разных протоколах — TCP и UDP.
### Поля `track`
| Поле | Тип | Описание | Примечание |
|:-----|:----|:---------|:-----------|
| `incoming_ttl` | number | TTL/hop limit первого входящего пакета потока | может не быть, если не определено |
| `l7proto` | string | протокол потока | есть всегда; если неизвестно — `unknown` |
| `hostname` | string | имя хоста (по анализу L6/L7-протоколов) | появляется только после определения |
| `hostname_is_ip` | bool | является ли hostname IP-адресом | только если есть hostname |
| `lua_state` | table | хранилище состояния, привязанного к потоку | есть всегда, передаётся с каждым пакетом потока |
| `lua_in_cutoff` | bool | отсечение Lua от входящего направления | только для чтения |
| `lua_out_cutoff` | bool | отсечение Lua от исходящего направления | только для чтения |
| `t_start` | number | unix-время первого пакета потока | с дробной частью высокой точности |
| `pos` | table | счётчики по направлениям | содержит таблицы `client`, `server`, `direct`, `reverse` |
> [!tip] `lua_state` — долгая память потока
> `track.lua_state` есть всегда (когда есть `track`) и выдаётся **одна и та же** таблица на каждый пакет потока. В ней хранят состояние между пакетами: счётчики попыток, флаги «уже отправлено» и т. п. Для временных данных в пределах одного пакета используйте саму таблицу `desync` (см. [[desync]] про два уровня памяти).
### Счётчики `track.pos` (client / server / direct / reverse)
`track.pos` содержит подтаблицы счётчиков по двум сторонам соединения: **`client`** — пакеты от клиента, **`server`** — пакеты от сервера. Ещё две подтаблицы, **`direct`** и **`reverse`**, — это просто **ссылки** на `client`/`server`. Куда они указывают, зависит от текущего направления (`desync.outgoing`) и серверного режима (`b_server`): `direct` всегда указывает на **текущее** направление, `reverse` — на **противоположное**.
Кроме того, в `track.pos` есть поле **`dt`** — время получения пакета в секундах от `t_start` (с дробной частью высокой точности).
Набор счётчиков в каждой подтаблице (подтаблица `tcp` присутствует только для TCP):
| Поле | Описание | Примечание |
|:-----|:---------|:-----------|
| `pcounter` | счётчик пакетов | |
| `pdcounter` | счётчик пакетов **с данными** | у которых размер L4-пейлоада не равен 0 |
| `pbcounter` | счётчик переданных байт | считается только размер L4-пейлоада, без заголовков |
| `ip6_flow` | последнее поле `ip6.ip6_flow` | отсутствует, если неизвестно или протокол не IPv6 |
| `tcp.seq0` | начальный sequence соединения | |
| `tcp.seq` | sequence текущего пакета | |
| `tcp.rseq` | relative sequence текущего пакета | вычисляется как `seq - seq0` |
| `tcp.rseq_over_2G` | был переход `rseq` за границу 2 ГБ | s- и p-позиции больше учитывать нельзя |
| `tcp.pos` | relative sequence верхней границы текущего пакета | вычисляется как `rseq + payload_size` |
| `tcp.uppos` | максимальный `pos` в соединении | |
| `tcp.uppos_prev` | `uppos` в предыдущем пакете с данными | полезно для определения ретрансмиссий |
| `tcp.winsize` | последнее поле `th_win` | без коррекции по scale |
| `tcp.scale` | последнее значение TCP-опции scale | |
| `tcp.winsize_calc` | winsize с учётом scale | эффективный размер TCP-окна |
| `tcp.mss` | последнее значение TCP-опции MSS | |
> [!warning] Не перепутайте сторону: MSS / winsize / scale
> Значения MSS, winsize и scale одна сторона соединения передаёт другой, чтобы та знала допустимые параметры партнёра. Если вам нужно узнать, **какого размера пакеты можно отсылать**, смотрите **противоположную** сторону — то, что она может принять. MSS дополнительно дублируется в `desync.tcp_mss` независимо от наличия conntrack, и там значение уже рассчитано под вычисление размера отправляемого пакета. Если conntrack нет или MSS не был согласован, используется значение по умолчанию `DEFAULT_MSS` (1220).
---
## Работа с sequence numbers
Общее правило «числа уже в машинном порядке байт» верно, но у TCP sequence/ack есть отдельная ловушка: они **32-битные беззнаковые** и переполняются по кругу. Если к `4294967280` (`0xFFFFFFF0`) прибавить `100`, по правилам TCP получится не `4294967380`, а `84` (`0x54`) — счётчик «завернулся».
Проблема в том, что Lua считает числа в более широком, знаковом представлении: обычное `seq1 + seq2` даст «неправильные» с точки зрения TCP `4294967380`, без заворота. Поэтому для арифметики и сравнения sequence используйте C-функции **`u32add`** и **`bitand`**. Пример — проверка `seq1 >= seq2`:
```lua
-- истина, если seq1 >= seq2 (при расхождении не более 2 ГБ)
0 == bitand(u32add(seq1, -seq2), 0x80000000)
```
Наивный вариант через обычное сложение работать корректно не будет, а этот — будет, **если** `seq1` ушёл от `seq2` не более чем на 2 ГБ. Большее расстояние через sequence в принципе не отследить, поэтому при передаче больших объёмов данных sequence не может служить надёжным счётчиком.
> [!tip] Для счёта пакетов/байт берите p*counter
> Счётчики `pcounter`/`pdcounter`/`pbcounter` в [`track.pos`](#счётчики-trackpos--client--server--direct--reverse) — **64-битные**, у них проблемы заворота нет. Если нужен именно счётчик, используйте их, а не арифметику sequence.
---
## Особенности обработки ICMP
Некоторые типы ICMP несут прикреплённый исходный пакет, на который был сгенерирован ICMP, — их называют **«related»**. `nfqws2` распознаёт такие пейлоады и ищет по прикреплённому пакету исходную запись conntrack. Если она находится:
- выбирается **кэшированный профиль** (тот, к которому относится прикреплённый пакет);
- **направление** выбирается как обратное относительно найденной записи;
- тип пейлоада ставится как `ipv4` или `ipv6`, тип протокола сеанса берётся из профиля исходного пакета;
- дальше ICMP проходит по профилю обычным образом.
> [!warning] ICMP без TCP/UDP, но с track
> В функцию может прийти диссект с `icmp`, **без** `tcp` или `udp`, но при этом **с** `desync.track` (взятым от исходной записи). Учитывайте это в коде — не считайте, что раз есть `track`, то есть и `tcp`.
Если ICMP не содержит прикреплённого пакета, он невалиден или запись conntrack не найдена — ICMP проходит самостоятельно, без `track`. Важно: conntrack ведёт учёт только для TCP и UDP; пинги и прочие ICMP он не отслеживает, и при проходе ICMP по записи conntrack **никакие счётчики не меняются**.
---
## Особенности обработки raw IP
Если IP-протокол не распознан как TCP, UDP, ICMP или ICMPv6, пакет считается **raw IP**. В диссекте тогда присутствуют поля `ip`/`ip6` и `payload`, где `payload` — это всё содержимое пакета после L3-заголовков. `desync.track` для raw IP **всегда отсутствует**.
---
## 📚 См. также
- [[desync|Механизм --lua-desync]] — как и зачем вызываются desync-функции (эта заметка — про их входные данные)
- [[жизненный цикл desync-функции|Жизненный цикл desync-функции]] — что функция делает с этими входными данными: восемь стадий, общих для всех техник дурения
- [[схема обработки трафика|Схема обработки трафика]] — где в конвейере пакета появляются `dis`, `track`, reasm и replay
- [[структура проекта|Структура проекта]] — почему логика в Lua, а разбор пакета и conntrack — в C-ядре
- [[multisplit]] — практический пример: как одна функция читает `desync` и возвращает вердикт
- [[payload|Тип payload]] — что означают `l7payload` и `l7proto`
- [[blob|Blob-данные]] — заготовки, на которые ссылаются аргументы `blob=…`
- 🔗 [Официальная документация (docs/manual.md)](https://github.com/bol-van/zapret2/blob/master/docs/manual.md) — первоисточник
---
> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование и скачивание всего репозитория одним zip-архивом доступны в Forgejo: [исходник этой заметки](https://git.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/Zapret2/%D1%81%D1%82%D1%80%D1%83%D0%BA%D1%82%D1%83%D1%80%D0%B0%20desync%20%D0%B8%20%D0%B4%D0%B8%D1%81%D1%81%D0%B5%D0%BA%D1%82%D0%B0.md) · [весь репозиторий](https://git.zapret.moe/zapretdiscordyoutube/todo/src/branch/main).