7.5 KiB
Packet Normalizer (фрагментация/дефрагментация)
1. Назначение
Модуль адаптирует произвольные IP-пакеты к передаче через ETCP-канал с ограниченным MTU. Состоит из двух половин:
- Packer (упаковщик) — разбивает входящие пакеты на фрагменты размером
frag_sizeи помещает их вetcp->input_queue. Фрагменты собираются в общий буфер пачками для экономии накладных расходов, сброс по таймеру (call_soon) при пустой входной очереди. - Unpacker (распаковщик) — собирает фрагменты из
etcp->output_queueобратно в исходные пакеты по заголовку длины (2 байта LE), помещает результат вpn->output(который читает ETCP router/клиент).
Каждое ETCP-соединение имеет свой экземпляр PKTNORM (создаётся в etcp.c:247).
2. Как пользоваться
Создание и привязка
pn_init() вызывается автоматически при создании ETCP_CONN. Клиентский код получает нормализатор через etcp->normalizer и использует:
- Отправка: положить пакет в
pn->input(очередьpn_input). Packer сам разобьёт и отправит. - Приём: читать собранные пакеты из
pn->output(очередьpn_output). На эту очередь ETCP вешает свой callbacketcp_int_recv.
// Создание (внутри etcp_connection_create):
etcp->normalizer = pn_init(etcp);
etcp->send_input_q = etcp->normalizer->input; // единая точка входа для отправки
// Отправка пакета (пример из тестов):
struct ll_entry* entry = ll_alloc_lldgram(len);
memcpy(entry->dgram, data, len);
entry->len = len;
queue_data_put(pn->input, entry);
// Приём (через callback на pn->output):
// queue_set_callback(pn->output, etcp_int_recv, etcp);
Формат фрагментов
Каждый фрагмент, уходящий в ETCP, содержит конкатенацию пакетов, каждый предварён 2-байтовым LE-заголовком длины:
[packet1_len:2 LE][packet1_data][packet2_len:2 LE][packet2_data]...
Сброс состояния
При разрыве/переподключении вызывать pn_reset(). При ошибке сборки (некорректный заголовок) вызывается etcp_conn_reinit() — полный перезапуск соединения.
3. API
Структуры
struct PKTNORM — основная структура пары packer/unpacker.
| Поле | Описание |
|---|---|
input |
Входная очередь packer (pn_input) — сюда кладут пакеты для отправки |
output |
Выходная очередь unpacker (pn_output) — отсюда читают собранные пакеты |
frag_size |
Максимальный размер одного фрагмента ETCP (mtu - ACK_REZERV - UDP_HDR - UDP_SC_HDR) |
data / data_ptr / data_size |
Буфер-накопитель packer (из data_pool) |
recvpart |
Частично собранный пакет unpacker |
flush_timer |
Таймер сброса накопленного буфера (call_soon) |
input_waiter_handle |
Backpressure-ожидание на etcp->input_queue |
tx_wait_time |
Задержка сброса (по умолчанию 10, не используется — сброс через call_soon) |
alloc_errors / logic_errors |
Счётчики ошибок |
in_total_pkts / in_total_bytes |
Статистика входящих (в packer) |
out_total_pkts / out_total_bytes |
Статистика исходящих (из unpacker) |
Функции
pn_init(etcp) — создаёт пару PKTNORM. Выделяет input/output очереди с порогом 0 (backpressure: ждать полного опустошения). Вешает callback-и: packer_cb на input, pn_unpacker_cb на etcp->output_queue, etcp_int_recv на output. Возвращает NULL при ошибке.
pn_deinit(pn) — освобождает все ресурсы: очереди, буферы, таймеры, waiter-ы. Вызывает routing_del_conn() (deprecated, no-op). Снимает callback с etcp->output_queue для предотвращения use-after-free.
pn_reset(pn) — сбрасывает состояние packer и unpacker (буферы, pending, recvpart, таймер). Используется при переподключении.
pn_unpacker_reset_state(pn) — сбрасывает только unpacker (recvpart).
pn_packer_send(pn, data, len) — копирует данные в ll_alloc_lldgram и кладёт в pn->input. Используется только в юнит-тестах.
Внутренние callback-и
packer_cb — callback на pn->input. Ждёт освобождения etcp->input_queue через queue_waiter_wait, затем вызывает etcp_input_ready_cb.
etcp_input_ready_cb — основной цикл packer. Извлекает пакет из pn->input, записывает 2-байтовый заголовок длины, копирует данные в буфер pn->data. При заполнении буфера (или если осталось <3 байт) вызывает pn_send_to_etcp. Если входная очередь пуста — ставит flush_timer через uasync_call_soon для сброса хвоста.
pn_flush_cb — таймерный callback, вызывает pn_send_to_etcp при простое.
pn_send_to_etcp(pn) — выделяет ETCP_FRAGMENT из io_pool, передаёт в него накопленный буфер pn->data, отправляет в etcp->input_queue. Буфер после этого обнуляется (передан во владение фрагмента).
pn_unpacker_cb — callback на etcp->output_queue. В цикле извлекает фрагменты, парсит 2-байтовые заголовки длины, копирует данные в pn->recvpart. При полной сборке пакета помещает его в pn->output. При ошибке вызывает etcp_conn_reinit.
pn_buf_renew(pn) — выделяет новый буфер из data_pool если pn->data == NULL. При нехватке места (<3 байт) вызывает pn_send_to_etcp для сброса текущего буфера.
Зависимости
etcp.h/etcp_api.h— структурыETCP_CONN,ETCP_FRAGMENT,etcp_int_recvrouting.h—routing_add_conn/routing_del_conn(deprecated, no-op)utun_instance.h—UTUN_INSTANCE(data_pool, ua)ll_queue.h— очереди, waiter, thresholdu_async.h—uasync_call_soonдля flush-таймераmem.h/memory_pool— аллокация буферов и фрагментов