УСТАРЕЛ - SSU был заменен на SSU2. Поддержка SSU была удалена из i2pd в релизе 2.44.0 (API 0.9.56) 2022-11. Поддержка SSU была удалена из Java I2P в релизе 2.4.0 (API 0.9.61) 2023-12.
SSU (также называемый “UDP” в большей части документации I2P и пользовательских интерфейсов) был одним из двух транспортов , реализованных в I2P. Другим является NTCP2 . Поддержка NTCP была удалена.
SSU был представлен в релизе I2P 0.6. В стандартной установке I2P router использует как NTCP, так и SSU для исходящих соединений. SSU-over-IPv6 поддерживается начиная с версии 0.9.8.
SSU называется “полунадёжным”, поскольку он будет многократно повторно передавать неподтверждённые сообщения, но только до максимального количества раз. После этого сообщение отбрасывается.
SSU Services
Как и транспорт NTCP, SSU обеспечивает надёжную, зашифрованную, ориентированную на соединение, двухточечную передачу данных. Уникальной особенностью SSU является то, что он также предоставляет услуги обнаружения IP и обхода NAT, включая:
- Кооперативное преодоление NAT/файрвола с использованием введения
- Обнаружение локального IP путем анализа входящих пакетов и тестирования узлов
- Передача статуса файрвола и локального IP, а также изменений в любом из них в NTCP
- Передача статуса файрвола и локального IP, а также изменений в любом из них, в router и пользовательский интерфейс
Спецификация адреса router
Следующие свойства хранятся в сетевой базе данных.
- Transport name: SSU
- caps: [B,C,4,6] См. ниже .
- host: IP (IPv4 или IPv6). Разрешается сокращенный IPv6 адрес (с “::”). Может присутствовать или отсутствовать, если используется firewall. Имена хостов ранее разрешались, но считаются устаревшими с версии 0.9.32. См. предложение 141.
- iexp[0-2]: Время истечения данного introducer. ASCII цифры, в секундах с начала эпохи. Присутствует только при использовании firewall, когда требуются introducers. Необязательно (даже если присутствуют другие свойства для данного introducer). С версии 0.9.30, предложение 133.
- ihost[0-2]: IP introducer’а (IPv4 или IPv6). Имена хостов ранее разрешались, но считаются устаревшими с версии 0.9.32. См. предложение 141. Разрешается сокращенный IPv6 адрес (с “::”). Присутствует только при использовании firewall, когда требуются introducers. См. ниже .
- ikey[0-2]: Base 64 introduction key introducer’а. См. ниже . Присутствует только при использовании firewall, когда требуются introducers. См. ниже .
- iport[0-2]: Порт introducer’а 1024 - 65535. Присутствует только при использовании firewall, когда требуются introducers. См. ниже .
- itag[0-2]: Тег introducer’а 1 - (2^32 - 1) ASCII цифры. Присутствует только при использовании firewall, когда требуются introducers. См. ниже .
- key: Base 64 introduction key. См. ниже .
- mtu: Необязательно. По умолчанию и максимум 1484. Минимум 620. Должно присутствовать для IPv6, где минимум 1280 и максимум 1488 (максимум был 1472 до версии 0.9.28). IPv6 MTU должно быть кратно 16. (IPv4 MTU + 4) должно быть кратно 16. См. ниже .
- port: 1024 - 65535 Может присутствовать или отсутствовать, если используется firewall.
Детали протокола
Управление перегрузкой
Потребность SSU только в полунадежной доставке, TCP-совместимой работе и возможности высокой пропускной способности предоставляет большую свободу в управлении перегрузками. Алгоритм управления перегрузками, описанный ниже, предназначен для эффективного использования пропускной способности и простоты реализации.
Пакеты планируются в соответствии с политикой router’а, следя за тем, чтобы не превысить исходящую пропускную способность router’а или измеренную пропускную способность удаленного узла. Измеренная пропускная способность работает по принципу медленного старта и предотвращения перегрузки TCP, с аддитивным увеличением пропускной способности отправки и мультипликативным уменьшением при возникновении перегрузки. В отличие от TCP, router’ы могут отказаться от некоторых сообщений после определенного периода времени или количества повторных передач, продолжая при этом передавать другие сообщения.
Методы обнаружения перегрузки также отличаются от TCP, поскольку каждое сообщение имеет свой уникальный и непоследовательный идентификатор, и каждое сообщение имеет ограниченный размер - максимум 32КБ. Для эффективной передачи этой обратной связи отправителю, получатель периодически включает список полностью подтвержденных идентификаторов сообщений и также может включать битовые поля для частично полученных сообщений, где каждый бит представляет получение фрагмента. Если прибывают дублированные фрагменты, сообщение должно быть подтверждено снова, или если сообщение все еще не было полностью получено, битовое поле должно быть передано повторно с любыми новыми обновлениями.
Текущая реализация не дополняет пакеты до какого-либо определённого размера, а вместо этого просто помещает один фрагмент сообщения в пакет и отправляет его (внимательно следя за тем, чтобы не превысить MTU).
MTU
Начиная с версии router 0.8.12, для IPv4 используются два значения MTU: 620 и 1484. Значение MTU корректируется на основе процента пакетов, которые передаются повторно.
Для обоих значений MTU желательно, чтобы (MTU % 16) == 12, так чтобы полезная нагрузка после 28-байтового заголовка IP/UDP была кратна 16 байтам для целей шифрования.
Для небольшого значения MTU желательно эффективно упаковать Variable Tunnel Build Message размером 2646 байт в несколько пакетов; при MTU 620 байт оно хорошо помещается в 5 пакетов.
На основе измерений, 1492 подходит для почти всех разумно небольших I2NP сообщений (более крупные I2NP сообщения могут достигать 1900-4500 байт, что в любом случае не поместится в реальный сетевой MTU).
Значения MTU составляли 608 и 1492 для релизов 0.8.9 - 0.8.11. Большое значение MTU было 1350 до релиза 0.8.9.
Максимальный размер получаемого пакета составляет 1571 байт начиная с версии 0.8.12. Для версий 0.8.9 - 0.8.11 он составлял 1535 байт. До версии 0.8.9 он составлял 2048 байт.
Начиная с версии 0.9.2, если MTU сетевого интерфейса router меньше 1484, он будет публиковать это в базе данных сети, и другие router должны учитывать это при установлении соединения.
Для IPv6 минимальный MTU составляет 1280. Заголовок IPv6 IP/UDP составляет 48 байт, поэтому мы используем MTU, где (MTU % 16 == 0), что верно для 1280. Максимальный IPv6 MTU составляет 1488. (максимум был 1472 до версии 0.9.28).
Ограничения размера сообщений
Хотя максимальный размер сообщения номинально составляет 32 КБ, практический лимит отличается. Протокол ограничивает количество фрагментов до 7 бит, или 128. Однако текущая реализация ограничивает каждое сообщение максимум 64 фрагментами, что достаточно для 64 * 534 = 33,3 КБ при использовании MTU 608. Из-за накладных расходов на объединенные leaseSet и ключи сессий практический лимит на уровне приложения примерно на 6 КБ ниже, или около 26 КБ. Необходима дальнейшая работа для повышения лимита UDP-транспорта выше 32 КБ. Для соединений, использующих больший MTU, возможны сообщения большего размера.
Тайм-аут простоя
Таймаут простоя и закрытие соединения зависят от каждой конечной точки и могут варьироваться. Текущая реализация уменьшает таймаут по мере приближения количества соединений к настроенному максимуму и увеличивает таймаут при малом количестве соединений. Рекомендуемый минимальный таймаут составляет две минуты или более, а рекомендуемый максимальный таймаут — десять минут или более.
Ключи
Вся используемая криптография — AES256/CBC с 32-байтовыми ключами и 16-байтовыми IV. Когда Алиса инициирует сессию с Бобом, MAC и сессионные ключи согласовываются как часть DH обмена, и затем используются для HMAC и шифрования соответственно. Во время DH обмена публично известный introKey Боба используется для MAC и шифрования.
Как первоначальное сообщение, так и последующий ответ используют introKey получателя (Боба) - получателю не нужно знать introKey инициатора (Алисы). DSA ключ подписи, используемый Бобом, уже должен быть известен Алисе, когда она связывается с ним, хотя DSA ключ Алисы может еще не быть известен Бобу.
При получении сообщения получатель проверяет IP-адрес и порт отправителя “from” со всеми установленными сессиями - если есть совпадения, MAC-ключи этой сессии тестируются в HMAC. Если ни один из них не прошёл проверку или если нет совпадающих IP-адресов, получатель пробует свой introKey в MAC. Если он не прошёл проверку, пакет отбрасывается. Если проверка прошла успешно, сообщение интерпретируется согласно типу сообщения, хотя если получатель перегружен, оно всё равно может быть отброшено.
Если у Алисы и Боба установлена сессия, но Алиса по какой-то причине теряет ключи и хочет связаться с Бобом, она может в любой момент просто установить новую сессию через SessionRequest и связанные сообщения. Если Боб потерял ключ, но Алиса об этом не знает, она сначала попытается подтолкнуть его к ответу, отправив DataMessage с установленным флагом wantReply, и если Боб постоянно не отвечает, она предположит, что ключ потерян, и установит новый.
Для согласования ключей DH используется группа RFC3526 2048-битная MODP (#14):
p = 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
g = 2
Это те же самые p и g, которые используются для ElGamal шифрования в I2P.
Предотвращение повторных атак
Предотвращение повторов на уровне SSU происходит путем отклонения пакетов с чрезмерно старыми временными метками или тех, которые повторно используют IV. Для обнаружения дублирующихся IV используется последовательность фильтров Блума, которые периодически “затухают”, так что обнаруживаются только недавно добавленные IV.
MessageIds, используемые в DataMessages, определяются на уровнях выше транспорта SSU и передаются прозрачно. Эти ID не имеют какого-либо определенного порядка - фактически, они, скорее всего, полностью случайны. Уровень SSU не предпринимает попыток предотвращения повторного воспроизведения messageId - это должны учитывать более высокие уровни.
Адресация
Для связи с SSU-пиром необходим один из двух наборов информации: прямой адрес, когда пир публично доступен, или косвенный адрес для использования третьей стороны для представления пира. Нет ограничений на количество адресов, которые может иметь пир.
Direct: host, port, introKey, options
Indirect: tag, relayhost, port, relayIntroKey, targetIntroKey, options
Каждый из адресов может также содержать ряд опций - специальные возможности данного конкретного узла. Список доступных возможностей см. ниже .
Адреса, параметры и возможности публикуются в сетевой базе данных .
Установление прямого соединения
Прямое установление сессии используется, когда не требуется третья сторона для преодоления NAT. Последовательность сообщений следующая:
Установление соединения (прямое)
Alice подключается напрямую к Bob. IPv6 поддерживается начиная с версии 0.9.8.
Alice Bob
SessionRequest --------------------->
<--------------------- SessionCreated
SessionConfirmed ------------------->
<--------------------- DeliveryStatusMessage
<--------------------- DatabaseStoreMessage
DatabaseStoreMessage --------------->
Data <--------------------------> Data
После получения сообщения SessionConfirmed, Bob отправляет небольшое сообщение DeliveryStatus в качестве подтверждения. В этом сообщении 4-байтовый идентификатор сообщения устанавливается в случайное число, а 8-байтовое “время прибытия” устанавливается в текущий общесетевой ID, который равен 2 (т.е. 0x0000000000000002).
После отправки сообщения о статусе узлы обычно обмениваются сообщениями DatabaseStore , содержащими их RouterInfos , однако это не обязательно.
Похоже, что тип статусного сообщения или его содержимое не имеет значения. Оно было изначально добавлено из-за того, что сообщение DatabaseStore задерживалось на несколько секунд; поскольку теперь store отправляется немедленно, возможно, статусное сообщение можно исключить.
Введение
Ключи введения доставляются через внешний канал (сетевая база данных), где они традиционно были идентичны хешу router до версии 0.9.47, но могут быть случайными начиная с версии 0.9.48. Они должны использоваться при установлении ключа сессии. Для непрямого адреса узел должен сначала связаться с relayhost и запросить у него представление узлу, известному на этом relayhost под данным тегом. Если возможно, relayhost отправляет сообщение адресуемому узлу, сообщая ему связаться с запрашивающим узлом, а также предоставляет запрашивающему узлу IP и порт, на которых расположен адресуемый узел. Кроме того, узел, устанавливающий соединение, должен уже знать открытые ключи узла, к которому он подключается (но не обязательно знать ключи любого промежуточного relay-узла).
Непрямое установление сессии посредством введения третьей стороной необходимо для эффективного прохождения NAT. Чарли, router за NAT или firewall, который не разрешает нежелательные входящие UDP-пакеты, сначала связывается с несколькими узлами, выбирая некоторых в качестве представителей. Каждый из этих узлов (Боб, Билл, Бетти и т.д.) предоставляет Чарли тег представления - случайное 4-байтное число - которое он затем делает общедоступным в качестве способа связаться с ним. Алиса, router, у которого есть опубликованные методы связи с Чарли, сначала отправляет пакет RelayRequest одному или нескольким представителям, прося каждого представить её Чарли (предлагая тег представления для идентификации Чарли). Затем Боб пересылает пакет RelayIntro Чарли, включая публичный IP и номер порта Алисы, а затем отправляет Алисе обратно пакет RelayResponse, содержащий публичный IP и номер порта Чарли. Когда Чарли получает пакет RelayIntro, он отправляет небольшой случайный пакет на IP и порт Алисы (пробивая дыру в своем NAT/firewall), и когда Алиса получает пакет RelayResponse от Боба, она начинает новое полнонаправленное установление сессии с указанным IP и портом.
Установление соединения (косвенно через introducer)
Алиса сначала подключается к introducer Бобу, который перенаправляет запрос к Чарли.
Alice Bob Charlie
RelayRequest ---------------------->
<-------------- RelayResponse RelayIntro ----------->
<-------------------------------------------- HolePunch (data ignored)
SessionRequest -------------------------------------------->
<-------------------------------------------- SessionCreated
SessionConfirmed ------------------------------------------>
<-------------------------------------------- DeliveryStatusMessage
<-------------------------------------------- DatabaseStoreMessage
DatabaseStoreMessage -------------------------------------->
Data <--------------------------------------------------> Data
После пробивания NAT сессия устанавливается между Alice и Charlie как при прямом соединении.
Заметки по IPv6
IPv6 поддерживается начиная с версии 0.9.8. Опубликованные адреса relay могут быть IPv4 или IPv6, а связь Alice-Bob может осуществляться через IPv4 или IPv6. До релиза 0.9.49 включительно связь Bob-Charlie и Alice-Charlie осуществляется только через IPv4. Релеинг для IPv6 поддерживается начиная с релиза 0.9.50. См. спецификацию для получения подробностей.
Хотя спецификация была изменена начиная с версии 0.9.8, связь Alice-Bob через IPv6 фактически не поддерживалась до версии 0.9.50. Более ранние версии Java router’ов ошибочно публиковали возможность ‘C’ для IPv6 адресов, даже несмотря на то, что они фактически не действовали как introducer через IPv6. Поэтому router’ы должны доверять возможности ‘C’ на IPv6 адресе только если версия router’а 0.9.50 или выше.
Тестирование узлов
Автоматизация совместного тестирования доступности для узлов обеспечивается последовательностью сообщений PeerTest. При правильном выполнении узел сможет определить свою собственную доступность и может соответствующим образом обновить свое поведение. Процесс тестирования довольно прост:
Alice Bob Charlie
PeerTest ------------------->
PeerTest-------------------->
<-------------------PeerTest
<-------------------PeerTest
<------------------------------------------PeerTest
PeerTest------------------------------------------>
<------------------------------------------PeerTest
Каждое из сообщений PeerTest несет nonce, идентифицирующий саму серию тестов, как инициализировано Alice. Если Alice не получает определенное сообщение, которое она ожидает, она будет повторно передавать соответственно, и на основе полученных данных или пропущенных сообщений, она узнает свою доступность. Различные конечные состояния, которые могут быть достигнуты, следующие:
Если она не получит ответ от Боба, она будет повторно передавать до определенного количества раз, но если ответ так и не придет, она поймет, что ее брандмауэр или NAT каким-то образом настроен неправильно, отклоняя все входящие UDP-пакеты даже в прямом ответе на исходящий пакет. В качестве альтернативы, Боб может быть недоступен или не может заставить Чарли ответить.
Если Alice не получает сообщение PeerTest с ожидаемым nonce от третьей стороны (Charlie), она будет повторно передавать свой первоначальный запрос к Bob определенное количество раз, даже если она уже получила ответ от Bob. Если первое сообщение от Charlie все еще не проходит, но сообщение от Bob проходит, она знает, что находится за NAT или межсетевым экраном, который отклоняет незапрошенные попытки соединения, и что переадресация портов работает неправильно (IP и порт, которые предложил Bob, должны быть переадресованы).
Если Алиса получает сообщение PeerTest от Боба и оба сообщения PeerTest от Чарли, но IP-адрес и номер порта во втором сообщении Боба и Чарли не совпадают, она понимает, что находится за симметричным NAT, который переписывает все её исходящие пакеты с разными портами отправителя для каждого контактируемого узла. Ей потребуется явно перенаправить порт и всегда держать этот порт открытым для удалённого подключения, игнорируя дальнейшее обнаружение портов.
Если Алиса получает первое сообщение Чарли, но не получает второе, она будет повторно передавать свое PeerTest сообщение Чарли до определенного количества раз, но если ответ не получен, она знает, что Чарли либо в замешательстве, либо больше не в сети.
Alice должна произвольно выбрать Bob из известных узлов, которые, по-видимому, способны участвовать в тестах узлов. Bob, в свою очередь, должен произвольно выбрать Charlie из известных ему узлов, которые, по-видимому, способны участвовать в тестах узлов и находятся на IP-адресе, отличном от IP-адресов Bob и Alice. Если возникает первое условие ошибки (Alice не получает сообщения PeerTest от Bob), Alice может решить назначить нового узла в качестве Bob и попробовать снова с другим nonce.
Ключ знакомства Алисы включается во все сообщения PeerTest, чтобы Чарли мог связаться с ней, не зная никакой дополнительной информации. Начиная с версии 0.9.15, у Алисы должна быть установленная сессия с Бобом для предотвращения атак подмены. У Алисы не должно быть установленной сессии с Чарли, чтобы тест узла был действительным. Алиса может впоследствии установить сессию с Чарли, но это не обязательно.
Заметки по IPv6
До релиза 0.9.26 поддерживалось только тестирование IPv4 адресов. Поддерживается только тестирование IPv4 адресов. Поэтому вся коммуникация Алиса-Боб и Алиса-Чарли должна происходить через IPv4. Коммуникация Боб-Чарли, однако, может происходить через IPv4 или IPv6. Адрес Алисы, когда он указывается в сообщении PeerTest, должен быть длиной 4 байта. Начиная с релиза 0.9.27, поддерживается тестирование IPv6 адресов, и коммуникация Алиса-Боб и Алиса-Чарли может происходить через IPv6, если Боб и Чарли указывают поддержку с возможностью ‘B’ в своем опубликованном IPv6 адресе. См. Предложение 126 для подробностей.
До релиза 0.9.50 Алиса отправляет запрос Бобу, используя существующую сессию через транспорт (IPv4 или IPv6), который она хочет протестировать. Когда Боб получает запрос от Алисы через IPv4, Боб должен выбрать Чарли, который рекламирует IPv4-адрес. Когда Боб получает запрос от Алисы через IPv6, Боб должен выбрать Чарли, который рекламирует IPv6-адрес. Фактическое взаимодействие Боб-Чарли может происходить через IPv4 или IPv6 (то есть независимо от типа адреса Алисы).
Начиная с релиза 0.9.50, если сообщение передается по IPv6 для тестирования IPv4 узла, или (начиная с релиза 0.9.50) по IPv4 для тестирования IPv6 узла, Алиса должна включить свой адрес и порт представления.
См. Proposal 158 для подробностей.
Окно передачи, подтверждения и повторные передачи
Сообщение DATA может содержать ACK полных сообщений и частичные ACK отдельных фрагментов сообщения. Подробности см. в разделе о сообщениях данных на странице спецификации протокола .
Детали оконного управления, подтверждений (ACK) и стратегий повторной передачи здесь не указаны. См. Java-код для текущей реализации. Во время фазы установления соединения и для тестирования узлов router’ы должны реализовывать экспоненциальный откат для повторной передачи. Для установленного соединения router’ы должны реализовывать настраиваемое окно передачи, оценку RTT и тайм-аут, аналогично TCP или streaming . См. код для начальных, минимальных и максимальных параметров.
Безопасность
UDP адреса источников могут, конечно же, быть подделанными. Кроме того, IP-адреса и порты, содержащиеся в конкретных SSU сообщениях (RelayRequest, RelayResponse, RelayIntro, PeerTest), могут быть недостоверными. Также определенные действия и ответы могут требовать ограничения частоты.
Детали валидации здесь не указаны. Разработчики должны добавить защитные механизмы там, где это необходимо.
Возможности пира
Одна или несколько возможностей могут быть опубликованы в опции “caps”. Возможности могут быть в любом порядке, но “BC46” является рекомендуемым порядком для обеспечения согласованности между реализациями.
B : Если адрес узла содержит возможность ‘B’, это означает, что они готовы и способны участвовать в тестах узлов в качестве ‘Bob’ или ‘Charlie’. До версии 0.9.26 тестирование узлов не поддерживалось для IPv6 адресов, и возможность ‘B’, если она присутствовала для IPv6 адреса, должна была игнорироваться. Начиная с версии 0.9.27, тестирование узлов поддерживается для IPv6 адресов, и наличие или отсутствие возможности ‘B’ в IPv6 адресе указывает на фактическую поддержку (или её отсутствие).
C : Если адрес узла содержит возможность ‘C’, это означает, что он готов и способен служить в качестве introducer через этот адрес - выступая в роли introducer Bob для недоступного иным способом Charlie. До выпуска 0.9.50 Java router’ы неправильно публиковали возможность ‘C’ для IPv6 адресов, несмотря на то, что IPv6 introducer’ы не были полностью реализованы. Поэтому router’ы должны предполагать, что версии до 0.9.50 не могут действовать как introducer через IPv6, даже если возможность ‘C’ объявлена.
4 : Начиная с версии 0.9.50, указывает на исходящую IPv4 возможность. Если IP опубликован в поле host, эта возможность не требуется. Если это адрес с introducers для IPv4 введений, следует включить ‘4’. Если router скрыт, ‘4’ и ‘6’ могут быть объединены в одном адресе.
6 : Начиная с версии 0.9.50, указывает на возможность исходящих IPv6 соединений. Если IP опубликован в поле host, эта возможность не обязательна. Если это адрес с introducers для IPv6 введений, должна быть включена ‘6’ (в настоящее время не поддерживается). Если router скрыт, ‘4’ и ‘6’ могут быть объединены в одном адресе.
Будущая работа
Примечание: Эти проблемы будут решены при разработке SSU2.
Анализ текущей производительности SSU, включая оценку настройки размера окна и других параметров, а также корректировка реализации протокола для улучшения производительности, является темой для будущей работы.
Текущая реализация повторно отправляет подтверждения для одних и тех же пакетов, что излишне увеличивает накладные расходы.
Значение MTU по умолчанию 620 должно быть проанализировано и, возможно, увеличено. Текущая стратегия настройки MTU должна быть оценена. Помещается ли пакет streaming lib размером 1730 байт в 3 малых SSU пакета? Вероятно, нет.
Протокол должен быть расширен для обмена MTU во время настройки.
Перегенерация ключей в настоящее время не реализована и никогда не будет.
Потенциальное использование полей ‘challenge’ в RelayIntro и RelayResponse, а также использование поля padding в SessionRequest и SessionCreated, не документировано.
Набор фиксированных размеров пакетов может быть подходящим для дальнейшего сокрытия фрагментации данных от внешних противников, но padding на уровне tunnel, garlic и end-to-end должен быть достаточным для большинства потребностей до тех пор.
Времена входа в систему в SessionCreated и SessionConfirmed, по-видимому, не используются или не проверяются.