# TUN Interface (tun_if) TUN связывает IP-трафик ОС с ядром. Все операции с интерфейсом, очередями, пулом и публичными счётчиками выполняются в потоке его `UASYNC`. `tun_close()` вызывается вне callbacks TUN, после остановки внешних пользователей и до уничтожения uasync. ## Форматы и направления | API | Направление | Формат | |---|---|---| | `output_queue` | ОС → ядро | префикс(1) + IP | | `input_queue` | ядро → ОС | префикс(1) + IP | | `tun_write(tun, buf, len)` | ядро → ОС | чистый IP | | `tun_inject_packet(tun, buf, len)` | тест → output_queue | чистый IP | | `tun_read_packet(tun, buf, capacity)` | тест читает доставленное ОС | чистый IP | `input_queue` снимает префикс и вызывает общий `tun_write()`. NAT и TCP proxy пользуются тем же API. Платформенный `tun_platform_write()` вызывается только модулем TUN. `tun_write()` сохраняет владение `buf` у вызывающего и возвращает `len` только после полной записи, иначе `-1` с errno. Неполная запись считается ошибкой. Пакет, отклонённый `queue_data_put()`, уже освобождён очередью. ## Создание и MTU `tun_init(ua, config)` создаёт основной IPv4 TUN с MTU 1500. `tun_init_nat(ua, name, ip, mtu, test_mode)` создаёт отдельный интерфейс для NAT или TCP proxy: `ip` — IPv4 без CIDR, MTU — 68..1500. Обе функции используют общий путь создания, регистрации чтения и отката; возвращают NULL при любой ошибке запуска. Платформенный init сам освобождает ресурсы при ошибке, успешный init передаёт cleanup общему коду. Длинные имена, неверный IPv4 и неподдерживаемый MTU отклоняются. `[global] mtu` управляет ETCP, поэтому основной и NAT TUN используют `TUN_MTU_DEFAULT`. TCP proxy передаёт собственный настроенный MTU и до создания PCB вызывает `lwip_tcp_set_mtu()`: локальный предел MSS равен IPv4 MTU минус 40 байт. Он ограничивает начальный MSS, объявление в SYN и MSS, согласованный с пиром. ## Чтение и backpressure На Linux/FreeBSD fd зарегистрирован в uasync. Перед чтением регистрация приостанавливается; после передачи пакета reader ждёт пустую `output_queue` через `queue_waiter_wait()`. Затем чтение разрешается снова. Синхронный consumer допустим. `tun_inject_packet()` копирует пакет, возвращает 0 при успехе и -1 при ошибке; если output_queue занята, возвращает EAGAIN без передачи пакета. Windows использует Wintun: session, заимствованный read event, stop event, auto-reset read permit и read thread. Оба конструктора проверяют read event и создают reader до сообщения об успешном запуске. Worker ожидает данные и разрешение отдельно; stop event прерывает оба ожидания. Следующий пакет разрешается после освобождения output_queue, поэтому поток не накапливает задачи в uasync. Worker не обращается к LL_QUEUE и пулу. Один пакет передаётся через `tun_post_packet()` и `uasync_post_reserved()`. Пакет и оболочка принадлежат ожидающей передаче; `posted_task` — первый член оболочки, которую освобождает uasync. Указатель ожидающей передачи и ошибки worker защищены mutex. Callback снимает ожидающую передачу, переносит ошибки в общую статистику и передаёт пакет очереди. ## Тестовый backend `test_mode=1` поддерживается только на POSIX, прав на устройство ОС не требует. Оба конца `socketpair(AF_UNIX, SOCK_DGRAM)` неблокирующие. Заимствованный `tun_get_test_fd()` живёт до `tun_close()`; закрывать его тестам нельзя. - `send(test_fd, ip, len, 0)` моделирует исходящий IP от ОС и запускает обычный reader. - Запись через `tun_write()`/input_queue доступна через `recv(test_fd, ...)` либо helper. - `tun_read_packet()` возвращает размер чистого IP, 0 при пустом сокете, -1 при ошибке. При недостаточной ёмкости возвращает EMSGSIZE и сохраняет пакет для следующего чтения. Helper и прямой recv требуют одного последовательного consumer. Тестовые сокеты обходят платформенный TUN read/write. В частности, FreeBSD AF-заголовок в socketpair не добавляется и не снимается. ## Закрытие, статистика и диагностика Закрытие выставляет closing, останавливает и присоединяет Windows worker, освобождает пакет ожидающей передачи и обнуляет её ссылку на TUN. Её оболочку позже освобождает uasync: callback становится пустым, а destroy без callback также освобождает блок. Это безопасно и для задачи из уже снятой uasync порции; исполнение чужих задач ради очистки не требуется. Затем отменяется read waiter и регистрация fd, снимаются callbacks и освобождаются элементы очередей, очереди и пул, закрывается backend. Для test_mode закрываются оба fd обычным close, без удаления интерфейса ОС. Wintun session закрывается после join; заимствованный read event отдельно не закрывается. Внешние владельцы waiters должны отменять их перед закрытием TUN. Счётчики bytes учитывают чистый IP: read — пакеты, принятые output_queue, write — полностью записанные пакеты. Helper их не меняет. Ошибки worker переносятся при доставке следующего пакета либо при закрытии. Категория `tun`: INFO — запуск и итог закрытия, DEBUG — направление/размер/сводка IP, TRACE — разрешение чтения, WARN/ERROR — некорректные пакеты, отказы памяти/очереди и системных операций. `test_tun_if` проверяет направления, границы helper, освобождение повреждённых элементов, очередь с отказом, паузу reader, fd, откаты init и post lifetime на реальном uasync. `test_nat_transport` проверяет полный socketpair roundtrip. `test_lwip_tcp 12345 1200` и `test_lwip_tcp 12345 68` проверяют TCP-обмен с разными MTU, объявления MSS и размеры каждого выходного IP-пакета. Нативный Wintun требует отдельной проверки на Windows.