Firezone представила sanstun — open-source реализацию состояния WireGuard на Rust с sans-IO API. Библиотека отделяет сетевой ввод-вывод, время, случайность и другие side effects от протокольной логики. Такой подход упрощает тестирование и симуляцию системы, не создаёт function coloring и позволяет приложению самому выбирать способ выполнения операций. Sanstun доступна на crates.io под Apache 2.0.
Firezone более двух лет использовала boringtun и внесла в собственный форк свыше 70 pull requests, не считая обновлений зависимостей. Сначала время сделали sans-IO через API с Instant, затем стали отключать внутреннюю буферизацию. Сохранять полную API-совместимость с upstream при новых требованиях становилось всё труднее, поэтому команда оформила накопленный опыт в отдельную библиотеку, а форк объявила deprecated.
В sanstun отсутствует скрытая буферизация. Если session ещё нет, приложение само решает, как хранить исходящий пакет и когда инициировать handshake. API покрывает входящие handshake и data, encapsulate, timeout и events без требования вызывать decapsulate с пустой датаграммой для очистки внутреннего буфера.
Типы Interface и Tunnel повторяют структуру WireGuard. Interface имеет один ключ и объединяет туннели, а Tunnel связывает два interface. Все входящие datagrams проходят Interface::receive: только эта публичная точка разбирает сырой пакет, применяет rate limits, формирует cookie reply при нагрузке и возвращает типизированное сообщение. Crate-private constructors не позволяют передать в Tunnel непроверенный handshake в обход ограничителя. Session indices помогают направить сообщение нужному TunnelId.
Cookie-механизм важен для DoS-защиты: поддельные handshake вынуждали бы получателя тратить CPU на Diffie-Hellman до проверки источника. Rate limits действуют на уровне всего interface, а не отдельного туннеля.
Для параллелизма внутри одного tunnel sanstun разделяет bookkeeping и ChaCha20-Poly1305. begin_encapsulate резервирует монотонный packet counter и обновляет timers, после чего PendingEncapsulate можно передать другому потоку благодаря Send. Decapsulation аналогично разделена на begin, decrypt и commit. Общий Interface остаётся однопоточным, поэтому rate limiter не требует locks или atomics.
В benchmarks 1280-байтового пакета bookkeeping занимает около 2% времени шифрования. Теоретически это указывает на большой потенциал worker-thread offload, но заявленные 45-кратные вычисления не учитывают передачу работы и прочие накладные расходы. Библиотека не навязывает параллелизм: encrypt и decrypt можно вызывать inline.