From ce30ec5fe62bf07211fa5cc832e0ddef7146f2cb Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 25 Oct 2026 22:43:43 +0100 Subject: [PATCH] Align packet dump and CRC32 contracts with implementations --- src/transport_layer/crc32.h | 11 +++-- src/transport_layer/crc32_doc.md | 17 +++++--- src/transport_layer/packet_dump_doc.md | 56 +++++++++++++++++++------- 3 files changed, 60 insertions(+), 24 deletions(-) diff --git a/src/transport_layer/crc32.h b/src/transport_layer/crc32.h index 4512de80..fbb491ab 100644 --- a/src/transport_layer/crc32.h +++ b/src/transport_layer/crc32.h @@ -14,16 +14,19 @@ extern "C" { #include #include -// Initialize CRC32 table (called automatically on first use) +/* Инициализирует общую таблицу (повторный вызов ничего не меняет). + * Вызывается автоматически из calc/calc_ex; до параллельных вызовов выполнить в одном потоке. */ void crc32_init(void); -// Calculate CRC32 checksum +/* data/len — блок байтов, выравнивание не требуется. Для непустого блока возвращает финальный CRC-32; + * при data==NULL или len==0 возвращает UINT32_MAX, а не стандартный CRC пустого блока (0). */ uint32_t crc32_calc(const uint8_t *data, size_t len); -// Calculate CRC32 with initial value +/* Обновляет initial_crc байтами data[0..len), возвращает CRC без финальной инверсии. + * При data==NULL или len==0 возвращает initial_crc. Для обычного CRC начать с UINT32_MAX. */ uint32_t crc32_calc_ex(const uint8_t *data, size_t len, uint32_t initial_crc); -// Update CRC32 incrementally +/* Эквивалент crc32_calc_ex(data, len, crc); финальная инверсия — ответственность вызывающего. */ uint32_t crc32_update(uint32_t crc, const uint8_t *data, size_t len); diff --git a/src/transport_layer/crc32_doc.md b/src/transport_layer/crc32_doc.md index 3f4e776e..1dda0294 100644 --- a/src/transport_layer/crc32_doc.md +++ b/src/transport_layer/crc32_doc.md @@ -1,13 +1,17 @@ # crc32 ## 1. Назначение -Модуль вычисления контрольной суммы **CRC-32 (IEEE 802.3)** с полиномом `0xEDB88320`. Используется в подсистемах `secure_channel` и `stcp` для контроля целостности данных при передаче — перед шифрованием к данным добавляется CRC32, при расшифровке CRC32 пересчитывается и сверяется. +Модуль вычисления контрольной суммы **CRC-32 (IEEE 802.3)** с отражённым полиномом `0xEDB88320`. Используется в подсистемах `secure_channel` и `stcp` для контроля целостности данных при передаче — перед шифрованием к данным добавляется CRC32, при расшифровке CRC32 пересчитывается и сверяется. `stun_nat` также использует результат для поля FINGERPRINT с дополнительным XOR `0x5354554e`. + +Общая изменяемая таблица инициализируется без синхронизации. Перед первым параллельным использованием +нужно вызвать `crc32_init()` в одном потоке, завершив инициализацию до запуска остальных; +после этого вычисления только читают таблицу. Динамических выделений и логирования нет. ## 2. Как пользоваться **Одноразовое вычисление (внутренне инициализирует таблицу при первом вызове):** ```c -// Исходное значение 0xFFFFFFFF, результат XOR-ится с ~0 +// Для непустого блока: исходное значение UINT32_MAX, в конце побитовая инверсия uint32_t crc = crc32_calc(data, data_len); ``` @@ -20,6 +24,9 @@ while (chunk) { crc ^= 0xFFFFFFFF; // финальный XOR ``` +Для пустого потока этот пример даёт 0. В отличие от него, `crc32_calc(NULL, len)` и +`crc32_calc(data, 0)` возвращают `UINT32_MAX`: это особенность API, а не стандартный CRC пустого блока. + **С произвольным начальным значением (без финального XOR):** ```c uint32_t crc = crc32_calc_ex(data, len, initial_crc); @@ -29,7 +36,7 @@ uint32_t crc = crc32_calc_ex(data, len, initial_crc); | Функция | Описание | |---------|----------| -| `crc32_init()` | Принудительная инициализация lookup-таблицы (256 × uint32_t). Автоматически вызывается при первом использовании, ручной вызов не обязателен. | +| `crc32_init()` | Инициализация lookup-таблицы (256 × uint32_t). Повторный вызов ничего не меняет. Для однопоточного использования достаточно автоинициализации; для многопоточного нужна предварительная инициализация. | | `crc32_calc(data, len)` | CRC32 «в один проход». Авто-инит, начальное значение `0xFFFFFFFF`, финальный `~crc`. При `data == NULL` или `len == 0` возвращает `0xFFFFFFFF`. | -| `crc32_ex(data, len, initial_crc)` | CRC32 с заданным начальным значением, **без** финального XOR. Подходит для инкрементального использования и цепочек вызовов. | -| `crc32_update(crc, data, len)` | Синоним `crc32_ex(data, len, crc)` — инкрементальное обновление. | +| `crc32_calc_ex(data, len, initial_crc)` | CRC32 с заданным начальным значением, **без** финального XOR. При `data == NULL` или `len == 0` возвращает `initial_crc`; иначе обрабатывает ровно `len` байт. | +| `crc32_update(crc, data, len)` | Эквивалент `crc32_calc_ex(data, len, crc)` — инкрементальное обновление. | diff --git a/src/transport_layer/packet_dump_doc.md b/src/transport_layer/packet_dump_doc.md index ebee75d9..7f6246d5 100644 --- a/src/transport_layer/packet_dump_doc.md +++ b/src/transport_layer/packet_dump_doc.md @@ -1,9 +1,9 @@ # Packet Dump (декодирование IP-пакетов) ## 1. Назначение -Вспомогательный модуль для диагностики: парсит сырой IPv4-пакет и формирует человекочитаемую строку с IP-адресами, протоколом, портами, флагами и hex-дампом 32 байт после IP-заголовка, включая транспортный заголовок. Вызывается из отладочных логов категорий `tun` и `traffic`. +Вспомогательный модуль для диагностики: парсит сырой IPv4-пакет и формирует человекочитаемую строку с IP-адресами, протоколом, портами, флагами и hex-дампом до 32 байт после IP-заголовка, включая транспортный заголовок. Вызывается из отладочных логов категорий `tun` и `traffic`. -**Незавершённый аудит 25.10.2026:** hex-дамп во всех трёх ветках читает фиксированные 32 байта без проверки остатка входного пакета. Это дефект, затрагивающий также распознанные TCP/UDP/ICMP-пакеты. Ограничение длины выходного буфера не защищает входные чтения. Исправление и оставшиеся проверки описаны в `doc/tasks.md`; runtime-проверка в этом продолжении не выполнялась. +Все чтения ограничены доступной длиной и полем IPv4 total length. Выравнивание входного указателя не требуется: адреса и поля читаются побайтно, независимо от порядка байтов процессора. Динамическая память не выделяется. ## 2. Как пользоваться @@ -23,10 +23,9 @@ DEBUG_DEBUG(DEBUG_CATEGORY_TUN, "[TUN->] %s", str); ### Примеры вывода ``` -IPv4 TCP 192.168.1.5:443 -> 10.0.0.2:54321 (1420 bytes) [ACK,PSH] seq=123456 ack=789012 win=64240 data: 1703012345... -IPv4 UDP 10.0.0.1:53 -> 192.168.1.3:45678 (76 bytes) len=56 data: 4500003c... -IPv4 ICMP 10.0.0.1 -> 8.8.8.8 (84 bytes) echo_request seq=1 id=1234 data: 08004d2c... -IPv4 ICMP 8.8.8.8 -> 10.0.0.1 (84 bytes) echo_reply seq=1 id=1234 data: 0000552c... +IPv4 UDP 192.168.1.2:0 -> 10.20.30.40:443 (28 bytes) len=8 data: 000001bb00080000 +IPv4 ICMP 192.168.1.2 -> 10.20.30.40 (28 bytes) echo_request seq=43981 id=4660 data: 080000001234abcd +IPv4 UNKNOWN 192.168.1.2 -> 10.20.30.40 (20 bytes) data: ``` ## 3. API @@ -36,18 +35,37 @@ IPv4 ICMP 8.8.8.8 -> 10.0.0.1 (84 bytes) echo_reply seq=1 id=1234 data: 0000552c **`dump_ip_packet_to_buffer(data, len)`** — парсит IPv4-заголовок и заголовок транспортного уровня, возвращает строку вида `IPv4 PROTO SRC:PORT -> DST:PORT (SIZE bytes) DETAILS data: HEX...`. Параметры: -- `data` — указатель на начало IPv4-пакета -- `len` — полная длина данных -- Возвращает `static char[1024]` +- `data` — указатель на начало IPv4-пакета; `NULL` даёт диагностическую строку. +- `len` — число доступных байт; может включать хвост после IPv4 total length, который игнорируется. +- Возвращает общий NUL-терминированный `static char[1024]`; не освобождать и не изменять. +- Размер в штатной строке — IPv4 total length, а не длина захваченного буфера. + +Перед разбором проверяются минимальные 20 байт IPv4, version=4, IHL>=20, +наличие всех байт IHL и условие IHL<=total length<=len. +При отказе возвращается только причина с длинами (`Missing IP packet`, `Packet too small`, +`Non-IPv4 packet`, `Invalid IP header length`, `Truncated IP header`, +`Invalid IP total length`, `Truncated IPv4 packet`); она также логируется как `dump=warn`. Обрабатываемые протоколы: -- **TCP (6):** порты, seq/ack, флаги (FIN, SYN, RST, PSH, ACK, URG, ECE, CWR), window -- **UDP (17):** порты, длина -- **ICMP (1):** тип/code, echo_request/echo_reply с seq и id +- **TCP (6):** при наличии 20 байт — порты, seq/ack, флаги (FIN, SYN, RST, PSH, ACK, URG, ECE, CWR), window. +- **UDP (17):** при наличии 8 байт — порты и поле длины UDP. +- **ICMP (1):** при наличии 8 байт — тип/code, для echo_request/echo_reply также seq и id. -Для нераспознанных протоколов выводит `src_ip:0 -> dst_ip:0` и пытается прочитать 32 байта после IP-заголовка. При коротком остатке это чтение выходит за границы входа; допустимым контрактом API такое поведение не является. +Порты выводятся также при значении 0. Все восемь TCP-флагов помещаются целиком; +при отсутствии флагов выводится `none`. Полную корректность транспорта, его длины и checksum модуль не проверяет. -Hex-дамп: 32 байта начиная с `data[ihl]`, включая транспортный заголовок, 4 группы по 8 байт. Корректное ограничение фактически доступной длиной пока отсутствует. +При ненулевом IPv4 fragment offset транспортный заголовок не разбирается; +имя протокола, адреса и hex-фрагмент сохраняются. Для всех фрагментированных пакетов +добавляется `fragment offset=N MF=0/1`, где offset измеряется в байтах. +Первый фрагмент с MF=1 разбирается, если его транспортный заголовок помещается. +При коротком заголовке выводится `partial header: available=N required=M` (`dump=debug`); +для нефрагментированного пакета — `truncated header: available=N required=M` (`dump=warn`). +В обоих случаях доступный hex-фрагмент сохраняется. + +Для нераспознанного протокола выводит `UNKNOWN`, адреса без портов и доступный hex-фрагмент. + +Hex-дамп: min(32, total length - IHL) байт начиная с `data[ihl]`, включая транспортный заголовок; +группы по 8 байт разделены пробелом. При пустом остатке после `data:` нет hex-цифр. **`tcp_flags_to_str(flags)`** (static) — преобразует байт TCP-флагов в строку вида `SYN,ACK` или `none`. @@ -56,5 +74,13 @@ Hex-дамп: 32 байта начиная с `data[ihl]`, включая тра - `routing_layer/routing.c` — логирование пакетов перед передачей в локальный TUN (категория `traffic`, уровень `debug`) ### Зависимости -- `platform_compat.h` — порядок байт (не используется в текущей реализации, IP-адреса читаются напрямую как LE) +- `debug_config.h` — диагностика ошибочного входа и частичного транспортного заголовка (категория `dump`). - Стандартная библиотека: ``, `` + +### Проверки + +`make -C tests test_packet_dump` и `./tests/test_packet_dump` вызывают настоящий форматтер: +проверяются вывод TCP/UDP/ICMP/UNKNOWN, все TCP-флаги, нулевые порты, IPv4 options, +77 длин захваченного пакета, невыровненный вход, фрагменты, диагностика неверных заголовков +и сохранность входа. Матрица из 2904 случаев проверяет точный hex для четырёх протоколов, +11 значений IHL и 0..65 байт после IP-заголовка.