Продолжение статьи "Yggdrasil Network: Заря бытовых меш-сетей, или Интернет будущего".
Если вы знакомы с сетью, либо читали предыдущую статью, должно быть знаете о феномене «сетевых штормов», которые всплыли при расширении Yggdrasil и явились основным слабым местом протокола. Сетевой шторм, как это явление прозвали энтузиасты, – недочет в логике маршрутизации, над решением которого команда разработчиков трудилась много месяцев.
Далее вы увидите как борьба с одним багом вылилась в фундаментальную переделку протокола.

Релиз
Релиз Yggdrasil Network 0.4.0 состоялся 04.07.2021. Разработчики потратили больше года на новую реализацию, работа над которой велась отдельно от предыдущей ветки. Начиная с самого зарождения проекта и до появления первого релиза 0.4 (с февраля 2018 года по июль 2021) протокол обновлялся мягко - полная совместимость с предыдущими версиями. Мягкое обновление означает, что внутренние адреса IPv6 оставались неизменны (не нужно менять никакие серверные и клиентские конфиги в прикладных программах), а также нет критической зависимости от актуальности ПО других участников сети - сам обновился и ладно.
Заменив клиент сети Yggdrasil 0.3 на новый 0.4, неискушенный пользователь может не заметить разницы: тот же виртуальный сетевой адаптер и адрес IPv6 из диапазона 200::/7
; Yggdrasil по прежнему автоматически организует подключение только в пределах локальной сети, а для связи с глобальным сегментом необходимо прописать в конфигурационном файле одного или нескольких локальных узлов адрес публичного пира. Однако под капотом произошли большие перемены, самое желанное из которых - значительный прирост стабильности соединений, а тяжелое - потеря обратной совместимости с предыдущей версией сети.
Разработчики имели целью решение выявленных проблем, а также упрощение и оптимизацию кодовой базы - рефакторинг. Это логичный шаг на пути к интуитивно понятному ПО, код которого наименьшим образом подвержен нелогичным нагромождениям и трудно отлавливаемым багам. В итоге были убраны опции встроенного файрволла и туннелирования трафика, так как для этих нужд существует отдельное специализированное программное обеспечение.
Чтобы понять роковое решение разработчиков о новом релизе, который несовместим с предыдущей сетью, необходимо понять решение каких проблем легло на их плечи, в чем причина этих недочетов и как они были исправлены. В конечном счете переход на новую версию сети - единовременная операция, а качественно новый уровень работы с сетью быстро изгладит неприятный осадок. Переделка протокола происходила кропотливо, было проведено много практических опытов и теоретических изысканий, поэтому можно полагать, что в ближайшие годы новых несовместимых релизов не будет, если это вообще когда-то случится.
Сетевой шторм - недочет маршрутизации
В предыдущей статье достаточно подробно рассматривалась маршрутизация Yggdrasil 0.3. Логику можно резюмировать так: IPv6 в Yggdrasil генерируется случайным образом и не имеет потенциала для логической маршрутизации. Алгоритм нахождения узлов друг другом и маршрутизацию между ними через десятки промежуточных узлов обеспечивают графы DHT, которые для удобства называются координатами.

Природа появления сетевого шторма кроется в логике построения маршрутов между узлами, которая напрямую завязана на координатах. За точку отсчета координат берется узел с неким характерным публичным ключом. При передачи трафика в сети 0.3, узлы опирались на динамическую информацию о своих координатах и координатах соседей. Если из сети пропадал корневой узел, являющийся началом отсчета координат, сеть начинала перестраиваться. Во время выбора нового корня каждый узел сети лавинообразно менял свои координаты. В силу хаотичности координат было невозможно построить стабильный маршрут, поэтому связность сети на время перестройки резко падала. При нормальном сценарии подобный шторм длился несколько секунд, после чего все узлы приходили к консенсусу о центре, однако иногда штормы длились часами. Выбор корня в Yggdrasil 0.3 описан в предыдущей статье.
Критичность проблемы сетевых штормов особенно сильно и отчасти комично проявлялась при использовании Yggdrasil Network в локальной сети. Частный случай случай выглядит так: два компьютера стоят в метре друг от друга, между ними проложен ethernet-кабель; локальный сегмент Yggdrasil из двух участников подключен к глобальному сегменту через подключение одного из компьютеров к публичному пиру. Во время шторма физические соседи переставали видеть друг друга из-за постоянной смены координат и адекватное сообщение в локальной сети прекращалось. Стабильность налаживалась только тогда, когда все узлы сети Yggdrasil, включая два локальных компьютера, имели устойчивые координаты.
Новый подход
В Yggdrasil 0.4 маршрутизация абсолютно переработана - реализована с нуля. Логика работы в дереве, знакомая с предыдущей версии сети, теперь имеет два дополнительных понятия: маршрутизация по пространству ключей (keyspace) и маршрутизация от источника (source routing или "ленивая маршрутизация").
По большей части в современной реализации меш-сети используется ленивая маршрутизация, которая не зависит от каких-либо координат - лишь бы фактический путь следования трафика не был нарушен. За этим следит каждый из промежуточных узлов и в случае обрыва соединения на своем участке явно оповещает соседей для мобильного решения ситуации. Проще говоря: адресат и путь до него находится по внутрисетевым координатам, а в рамках сессии трафик идет по статичному оптимальному маршруту, основанному на реальной топологии (а не логической по DHT).
Вычисление точки отсчета координат также как и в Yggdrasil 0.3 завязано на ключ подписи, но алгоритм сильно упрощен. Публичный ключ - это 32 байта информации, которые можно представить в любом виде: в бинарном, шестнадцатеричном, кодировке base32 или base64, но в конечном счете любое значение является неким числом. Началом пространства координат сети Yggdrasil считается узел, чей ключ является наименьшим в математическом смысле.
Концептуально новым явлением относительно более ранних версий является маршрутизация в пространстве ключей, которая изменила прежнее представление о координатах. Новая DHT (распределенная хеш-таблица) использует тот факт, что узлы идентифицируются просто публичными ключами Ed25519, и тем, что корнем дерева является узел с минимальным ключом. Каждый узел знает путь до корня и то, что корень лежит на одном из концов пространства ключей. Таким образом, новая DHT является просто прямой из ключей, отсортированной от минимального к максимальному ключу, начиная с корня дерева.
Каждый узел обязан построить маршрут от себя к предыдущему узлу на этой прямой (к узлу с более низким ключом, расположенным по DHT ближе к корню). Предшественник использует этот путь для направления трафика вниз по DHT, то есть в сторону возрастания ключей. Все промежуточные узлы сохраняют запись в таблице маршрутизации для этого пути. Таким образом, если узел Б устанавливает маршрут к узлу А, то каждый узел на пути от А до Б тоже имеет запись в таблице маршрутизации о пути, которым пойдёт трафик в сторону Б. Если трафик по этому пути не дойдёт (получит таймаут), то узлы на любом конце этого оборванного пути шлют явное уведомление том, что путь прерван, что позволяет DHT быстро реагировать на обрывы путей и изменения топологии.
Пакеты перенаправляются в сторону максимального ключа, который не выше узла назначения. Эти решения маршрутизации принимаются не только на концах пути, а любой нодой по пути следования пакета: если А является предком узла Б (имеет более низкий ключ), А не обязан маршрутизировать трафик до Б. Трафик, проходящий через какой-нибудь узел между А и Б пойдёт к Б, не попадая к А. Корень дерева, как и все другие пиры-предки, служат лишь дополнительными каналами DHT, о которых узлы знают "просто так", так как они узнали об этом при начальном построении дерева.
Чтобы узнать о своих соседях по ключам, узлы без предков периодически шлют бутстрап-пакет. Этот бутстрап-пакет перемещается по DHT пока не достигнет максимального предка. Этот пакет содержит метку расстояния от корня дерева до отправителя, которая используется предком для отправки подтверждающего пакета. Подтверждающий пакет содержит содержит метку расстояния от корня до предка, которая используется отправителем для нахождения пути по DHT.
Вместо поиска путей к узлам, узлы просто направляют трафик по направлению к ключам узлов через DHT. Во время установки сессии узлы устанавливают исходный маршрут и прозрачно переключаются на него в фоне.
Пути через пространство ключей DHT обычно имеют большую протяженность, нежели пути через связи древовидного пространства (treespace). Дерево является понятием, соответствующим физической топологии (связи узлов). Смешение двух подходов (начальной маршрутизации по DHT и оптимальной передачи информации по реальной топологии) является фактором стабильности. В случае устойчивой сети трафик передается по дереву (ленивая маршрутизация), как это было в более ранних версиях Yggdrasil, при этом нет зависимости от изменений сети на участках, которые не входят в конкретный маршрут, так как при стабильном канале нет зависимости от DHT-координат. В обратном случае, когда устойчивый канал связи нарушается, происходит быстрое реагирование и поиск возможного пути по DHT, который постоянно поддерживается в актуальном состоянии.
IPv6
Сеть работает таким образом, чтобы обратиться к любому узлу можно было лишь по его известному IPv6-адресу из диапазона 200::/7
. В силу основательного изменения протокола сети, теперь вся маршрутизация основана на простой таблице DHT, единственно состоящей из публичных ключей подписи. В Yggdrasil 0.3 и более ранних версиях адрес IPv6 выводился из ключа шифрования, а теперь этот ключ как отдельное значение вовсе отсутствует и выводится математическим путем из ключа подписи.
Yggdrasil 0.4 использует следующий алгоритм образования IPv6-адреса из публичного ключа подписи: 1) первый байт "0x02" является константой, 2) ключ подписи побитово инвертируется, 3) количество лидирующих ненулевых битов составляет второй байт адреса, 4) первый ноль усекается, 5) последующие 14 байт ключа образуют тело адреса.

Итог
Замеры производительности переделанного протокола Yggdrasil внушают большие надежды на стабильность. В свою очередь стабильность - важнейший показатель популяризации сети.
После релиза Yggdrasil 0.4 старый список публичных сервисов и публичных пиров был обнулен. Для связности вашего узла с глобальным сегментом сети необходимо прописать в конфигурационный файл один или несколько новых публичных пиров. Администраторам ресурсов необходимо обновить клиент сети и освежить в конфигурационных файлах веб-сервера и прочего прикладного ПО используемый IPv6, после чего снова добавить свой сервис в список общедоступных.
Стартовые узлы I2P в сети Yggdrasil также обновились, изменения в исходный код уже добавлены. Возможность обращения за ресидом I2P через Yggdrasil возобновится в ближайшем релизе i2pd (2.39.0), но уже сейчас вы можете собрать текущую версию из гит-репозитория вручную, либо просто указать адрес ресида в конфигурационном файле: reseed.yggurls=http://[324:71e:281a:9ed3::ace]:7070/
.
Ознакомиться с Yggdrasil 0.4 более детально можно в официальном англоязычном блоге, а также на странице релиза.
victor_1212
не совсем понимаю цели проекта, spanning tree работает на высокоскоростных hw switches, поэтому задержки в передаче пакетов минимальны, если делать нечто подобное через host, тем более, если правильно понимаю поверх tcp, случайно выбранные «ip v6 псевдо адреса» на интерфейсах host приведут к большим непредсказуемым задержкам в доставке пакетов, далее не понимаю причин использования здесь ip v6, если настоящее routing делается совсем по другому ip адресу, вполне можно был выбрать свое собственное адресное пространство, ни как не связанное с ip, разве не так?
ps
посмотрел сайт Tapestry (DHT), все равно не понимаю, как именно достигается «маршрутизация с упором на эффективность и минимизацию задержки сообщений»
dsrk_dev
ipv6 используется чтобы существующий софт мог без проблем работать через сеть
victor_1212
что именно, какой существующий софт (разве это все не с «0» пишется), если протокол работает поверх tcp, routing использует совсем другой ip адрес именно зависящий от конфигурации host, если правильно понимаю сгенеренные ip v6 адреса вообще видны только на host, почему бы не использовать в этой связи для идентификаии host в составе Yggdrasil любой другой уникальный собственный id, понимаю, что этот вопрос возможно не к вам, тем не менее буду признателен за объяснение
pureacetone Автор
Чтобы использовать свои пространства адресов, как это делается в Tor и I2P, необходимо все запросы пользователя направлять на локальный прокси, который обработает запрос. Фишка Yggdrasil в том, что выход в сеть выходит через виртуальный сетевой интерфейс (WireGuard, принцип работы VPN), которому автоматически присваивается нужный IPv6-адрес и ваша операционная система понимает куда нужно маршрутизировать запросы на 200::/7 без всяких дополнительных настроек прокси. Очень удобно. Чтобы лучше понять - просто попробуйте: установили Yggdrasil, прописали публичные пиры для связи с глобальным сегментом сети, затем открываете веб-браузер и без всяких настроек открываете IPv6-адреса из диапазона Ygg. Соответственно все серверные (и клиентские) конфиги прикладных программ также не нуждаются в перенаправлении на прокси и прочих нечитабельных нагромождениях: просто указываете в конфиге nginx (или чего-то еще) ваш IPv6-адрес Yggdrasil и считайте, что ваш веб-ресурс уже в сети!
Пользовательская маршрутизация внутри Yggdrasil также происходит по IPv6, которые клиент сети обрабатывает внутри себя, ориентируясь на криптографические ключи узлов и прочую инкапсулированную от пользователя логику.
Можете задать уточняющий вопрос, если мой ответ не является понятным или исчерпывающим. Возможно, я вас недопонял.
victor_1212
спасибо, кажется начинаю понимать, это sw работает на host типа relay на пользовательском уровне ip v6 адресов из url, в реальные ip адреса, которые двигают пакет через сеть обычным образом, правильно?
конфигурация чисто статическая,
вероятно драйвер соответствующего созданного интерфейса,
меня заинтересовал этот проект, хочу посмотреть код сначала, плюс пару старых статей 2007 года они упоминают, далее если будут вопросы, спрошу с вашего разрешения
pureacetone Автор
Вы мыслите в верном направлении.
Тут однозначно сказать нельзя, потому что надо помнить про сквозное шифрование и прочее (свой протокол). Это высказывание верно также, как высказывание, что пакет сетей Tor и I2P идет от вас по сети обычным образом. Магии однозначно нет - идет обычным образом, но этот образ на более высоком уровне (программном) обеспечивается логикой клиента сети, отвечающего за маршрутизацию, шифрование и т.п.
Если поставите себе клиент сети, можете заглянуть в IRC для обсуждения и вопросов
http://[324:71e:281a:9ed3::41]/
, канал#howtoygg
.victor_1212
да понимаю, шифрование само собой, ну это уже другой уровень, сейчас пытаюсь понять, что это значит на сетевом (3) уровне
pureacetone Автор
На сетевом уровне - передача трафика на IP-адрес публичного пира, к которому вы подключились, либо на локальный адрес, если работаете в локалке (где сеть Yggdrasil организуется автоматически через мультикаст).
victor_1212
да конечно, авторы в своих сообщениях на reddit акцентируют routing, пытаюсь понять есть ли рациональное зерно, шифрование это особая статья сейчас мне не слишком интересная
ps
когда-то работал под одной крышей с radia perlman, и ее мужа майка знал, вообще сетями долго занимался, так что кое-что еще помню про routing :)
qw1
Это конечно удобно, но оставляет всего 112 бит ключа для публичного адреса. Если кто-то захочет захватить адрес, ему нужно будет совершить перебор приватного ключа в объёме 112 бит, чтобы получить нужный публичный адрес. Это конечно много, но для ресурсов, которые есть у государств, вроде и нет (даже 160-битные ключи давно призывают менять на 256 и 512-битные).
pureacetone Автор
Да, эта тема обсуждалась. Но пока что реальных прецедентов при всех попытках не было.
Рекомендую ознакомиться с термином "Выского адреса" - адреса с большим значением во втором байте: чем выше это значение - тем большее количество бит ключа используется в образовании IPv6. Старая статья про образование IPv6 в Yggdrasil, но понятие "Высокого адреса" актуально.
Майнер адресов.
qw1
Неважно. Несколько бит погоды не сделают.
Пока что сеть спасает статус «неуловимого джо», хотя если такие атаки будут практически использоваться, никто не запрещает сделать ещё один breaking change.
pureacetone Автор
Ключи ed25519 и x25519, которые используются практически повсеместно составляют всего 256 бит (32 байта). Если вдруг появится фактическая возможность их конвеерного брутфорса, то у меня плохие новости для человечества в целом)) Насколько я знаю, вероятность подбора этих ключей в разумные сроки даже на самом превосходном железе пока что не заявлена.
Наглядный бытовой пример: чтобы получить ключ с лидирующими тридцатью семью нулями (нулевыми битами) нужно на домашнем железе затратить несколько минут, а иногда и часов. Остальные 14 байт после этих нулей составляют тело IPv6-адреса Yggdrasil. Очевидно, что энтропия 14 байт из тела адреса отлично усложняется изначальным количеством лидирующих нулей. По тестам энтузиастов и своим личным могу сказать, что каждый бит делает погоду. Сложность растет по экспоненте.
Для каких-либо проектов в сети Yggdrasil я рекомендую одну ночь помайнить высокий адрес, чтобы оставшийся свой век даже не думать, что кто-то будет пытаться брутфорсить.
qw1
pureacetone Автор
Половину от такого ключа подобрать тоже сложно, но мысль в другом: чем выше ключ (чем выше второй байт IPv6-адреса) тем большее количество бит от ключа используется. Это является дополнительным фактором безопасности. В теории высокий адрес может использовать 90% (а в абсолютном вакууме и все 100%) от публичного ключа.
Обычному пользователю с головой хватит 112 бит, потому что гугл его брутфорсить не станет, а для сервисов рекомендуется майнить высокий адрес.
Если вы не совсем поняли при чем тут "высота" адреса и объем использованной информации от публичного ключа, посмотрите на иллюстрацию в этой статье, которая описывает алгоритм образования IPv6.
qw1
Прочитал внимательно, понял. Интересная идея. Атакующему нужно найти не любую коллизию на 112 битах, а редкую коллизию. Фактически, второй байт определяет, на сколько бит увеличивается перебираемый ключ. Например, второй байт = 0x20 — увеличивается на 32 бита, т.е. надо перебирать 144 бита уже, а не 112.
Причём для своего адреса намайнить коллизию намного проще, чем для атакующего. У него множитель сложности x2112, когда у абонента множитель x1
pureacetone Автор
Абсолютно верно. Стоит отметить, что адрес с высотой в 32 бита (как в вашем примере с вторым байтом == 0x20) - даже на старом домашнем ПК находится за минуты, а вот дальнейший подбор коллизии такого адреса усложняется N-кратно, где N - очень и очень много.
dsrk_dev
Я могу сейчас открыть через любой браузер сайт хостящийся через Yggdrasil