NetTraceдокументация

NetTrace — архитектура

Локальный desktop-анализатор PCAP/PCAPNG. Пользователь сам исследует захват; приложение показывает факты (пакеты, поля, потоки, счётчики) и не делает выводов.

Слои

apps/desktop/            Tauri-приложение
  src-tauri/             тонкий IPC-слой: команды → engine, события прогресса
  src/                   React + TypeScript UI (не знает внутренностей парсера)
crates/
  model/                 DTO-контракт backend ↔ UI (Packet, Flow, Host, Conversation,
                         Protocol, TimelineEvent, PacketField …). Только serde.
  packet/                безопасный байтовый курсор, адреса (MAC/IPv4/IPv6), link types, время
  capture/               потоковое чтение PCAP/PCAPNG, запись PCAP/PCAPNG (экспорт), трейт PacketSource
  live/                  живой захват: Npcap/libpcap загружается в рантайме (единственный
                         модуль с unsafe — FFI), список интерфейсов, BPF-фильтр захвата
  protocol/              диссекторы и реестр (Ethernet, 802.1Q, ARP, IPv4/6, ICMP/v6,
                         TCP, UDP, DNS, DHCP, HTTP/1.x, TLS, NTP), реестр полей фильтра
  flow/                  отслеживание TCP/UDP-потоков, TCP-анализ (ретрансмиссии, dup ACK,
                         out-of-order, zero window, RTT, рукопожатие, FIN/RST)
  analysis/              агрегаты: узлы, соединения, иерархия протоколов, I/O-график,
                         длины пакетов, временная шкала, технические индикаторы
  storage/               компактный индекс метаданных пакетов + ленивое чтение байтов из файла
  query/                 язык display filter: лексер → парсер → AST → типизированная
                         компиляция по реестру полей → вычисление по трейту FieldSource
  engine/                оркестрация сессии: фоновая индексация, представления (фильтр +
                         сортировка), детали пакета, поиск, статистика
  testkit/               построители пакетов и генератор PCAP-фикстур (только для тестов)

Граф зависимостей (стрелка = «зависит от»):

model ◄── protocol ◄──┐
packet ◄── capture    │
  ▲   ◄── protocol    ├── engine ◄── desktop (src-tauri)
  ├──── flow ◄────────┤
  ├──── storage ◄─────┤
  ├──── analysis ◄────┤
  └──── live ◄────────┤
model ◄── query ◄─────┘

query не знает про индекс и диссекторы: движок реализует FieldSource. flow получает узкую структуру FlowPacket, а не результат диссекции. UI получает только типы из model.

Конвейер обработки файла

PCAP/PCAPNG ──► capture::CaptureReader (BufReader, последовательно, offset каждой записи)
             ──► protocol::dissect (режим Summary: без дерева и строк)
             ──► batch (4096 пакетов; при живом захвате — сразу) ──► write-lock индекса:
                    storage::PacketIndex.push(PacketMeta 64 байта)
                    flow::FlowTable.process  → stream id, флаги TCP-анализа, RTT
                    analysis::Accumulators   → узлы, соединения, иерархия, события
             ──► событие прогресса в UI каждые ~100 мс

В памяти хранятся только метаданные. Байты пакета читаются позиционным чтением из файла по требованию: строки видимой части списка, детали выбранного пакета, поля фильтра, которых нет в индексе.

Как устроена память (рассчитано на десятки миллионов пакетов и миллионы потоков):

  • PacketMeta — 64 байта на пакет; в нём же next_in_flow, ссылка на следующий пакет того же потока (список пакетов потока — цепочка, без отдельных векторов).
  • Адреса интернированы (AddressTable): в пакетах, узлах и соединениях — u32-номера; хэш-таблицы (hashbrown::HashTable) хранят только номера, ключ читается из вектора.
  • Поток — около 128 байт; итоги TCP-анализа (TcpFlow) — 136 байт. Состояние, нужное только пока идут пакеты (окна последовательностей, неподтверждённые сегменты), лежит отдельно и освобождается в FlowTable::finish() в конце индексации. RTT-замеры всех потоков — один общий массив в порядке пакетов.
  • Узлы (56 байт) и соединения Ethernet/IP (48 байт) — плотные массивы по номерам адресов. Лимитов на них нет: память растёт линейно с числом пакетов; предохранитель один — 8 млн потоков (MAX_FLOWS).
  • Худший случай (каждый пакет — новое TCP-соединение с нового адреса): ~0,5 КБ на пакет после индексации.

Таблицы «Узлы», «Соединения» и «Потоки» не передаются в UI целиком: движок фильтрует и сортирует номера строк (engine::tables, порядок кешируется в сессии) и собирает строки только для запрошенной страницы; UI подгружает страницы по мере прокрутки (usePagedTable, PageCache). Индикаторы и временная шкала строят подписи только для того, что попадёт в ответ.

«Содержимое потока» (engine::follow) ничего не хранит заранее: страница собирается по цепочке пакетов потока (next_in_flow), байты читаются из файла. TCP-сегменты упорядочиваются по направлениям (Reorder: повторы и перекрытия отбрасываются, сегменты после «дыры» ждут до 64 штук или 512 пакетов, затем «дыра» считается потерей). Курсор с этим состоянием запоминается каждые 1 МБ вывода или 25 тыс. пакетов, так что страница в глубине потока не проходит его с начала; один запрос смотрит не больше 100 тыс. пакетов. UI держит не больше 8 МБ потока и 200 тыс. строк, дальше — «Следующая часть» или сохранение в файл.

Пока индексация идёт, UI уже может листать, фильтровать и открывать пакеты (чтение под read-lock, индексатор берёт write-lock только на применение батча).

Живой захват

Npcap/libpcap ──► live::LiveSource ──► поток-рекордер ──► временный PCAP (%TEMP%/NetTrace)
                                                             │ (дописывается)
                                       engine::TailReader ◄──┘ ждёт новых байт вместо EOF
                                             └──► тот же индексатор, что и для файлов

Индексатор в режиме захвата применяет каждый пакет сразу, поэтому список, фильтры, потоки и статистика обновляются в реальном времени; отфильтрованное представление пересчитывается раз в секунду. После остановки захват — обычный файл: его можно сохранить (PCAP/PCAPNG); временный файл удаляется при закрытии. Без установленного Npcap приложение работает в режиме файлов и объясняет, что нужно установить.

Display filter

Поля делятся на «индексные» (frame.number, ip.addr, tcp.port, tcp.stream, флаги TCP, наличие протокола, tcp.analysis.*, frame.time_relative …) — вычисляются по PacketMeta без чтения файла, параллельно (rayon) — и «глубокие» (dns.qry.name, http.host, tls.handshake.extensions_server_name, ip.ttl …) — для них пакет читается и диссектируется в режиме Values (дерево без строк отображения).

Безопасность

PCAP — недоверенный вход. Весь разбор идёт через packet::Cursor с проверкой границ, без unsafe. Ошибка разбора слоя превращается в узел [Malformed] и не прерывает обработку файла. Дополнительно каждый вызов диссектора обёрнут в catch_unwind. Лимиты: размер записи ≤ 256 КиБ, размер блока PCAPNG ≤ 16 МиБ, глубина вложенности слоёв, число переходов при DNS-компрессии. UI никогда не интерпретирует содержимое пакетов как HTML/URL, не открывает ссылки и не делает сетевых запросов (CSP Tauri запрещает внешние источники, плагины shell/opener не подключены).

SQLite

В MVP не используется: индекс в памяти достаточно компактен для миллионов пакетов. storage скрывает хранение за типом PacketIndex, поэтому персистентный кэш индекса (SQLite) можно добавить без изменения остальных слоёв.

Расширение

  • Новый диссектор: реализовать protocol::Dissector/AppDissector, зарегистрировать в Registry::with_builtins, добавить поля в protocol::fields. UI не меняется — дерево деталей и фильтр работают по общей модели.
  • Новый источник живого захвата: реализовать live::LiveSource (как Npcap/libpcap в live::ffi или ReplaySource для тестов). Запись идёт во временный PCAP, который индексируется как обычный файл.
  • Английский язык: apps/desktop/src/i18n — словари; диссекторы отдают языконезависимые id полей, подписи переводит UI.