Простейшее решение «проблемы промежуточных устройств»: организация работы SCTP поверх UDP в ядре Linux

Kate

Administrator
Команда форума
Возможность организации работы SCTP поверх UDP (известная ещё как инкапсуляция SCTP-пакетов в UDP-пакеты) определена в RFC 6951 и реализована в пространстве ядра Linux начиная с версии ядра 5.11.0. Поддержку этой возможности планируется включить в Red Hat Enterprise Linux (RHEL) 8.5.0 и 9.0.



В этом материале даётся краткий обзор организации работы SCTP поверх UDP в ядре Linux.

Зачем нужна организация работы SCTP поверх UDP?​


В вышеупомянутом RFC 6951 выделены две основные причины необходимости организации работы SCTP поверх UDP:

  • Это нужно для того чтобы позволить SCTP-трафику проходить через устаревшие NAT-системы, не имеющие встроенной поддержки SCTP, как указано в этом и этом документах.
  • Это нужно для того чтобы открыть возможность реализации SCTP на хостах, которые не предоставляют прямого доступа к уровню IP. В частности, речь идёт о том, чтобы у приложений была бы возможность использовать собственную реализацию SCTP в том случае, если операционная система не предоставляет им своей реализации SCTP.

Первая причина связана с решением «проблем промежуточных устройств». Эти проблемы доставили тем, кто пользуется SCTP, много неприятностей и встали на пути у широкого использования SCTP. В основе второй причины лежит потребность в наличии механизма, который позволяет разработчикам приложений пользовательского уровня создавать собственные реализации SCTP, основанные на UDP.

Как работает инкапсуляция SCTP-пакетов в UDP-пакеты?​


Если в нашем распоряжении имеется возможность организации работы SCTP поверх UDP, то оказывается, что SCTP-пакеты инкапсулируются в UDP-пакеты. Реализация этого механизма создана с использованием набора API для работы с UDP-туннелями. Эти API уже использовались для организации работы протоколов VXLAN, GENEVE и TIPC.

Ядро, для того чтобы иметь возможность получать инкапсулированные пакеты, прослушивает особый UDP-порт на всех локальных интерфейсах. По умолчанию используется порт 9899. Этот порт, кроме того, играет роль порта src для UDP-пакетов, отправляемых хостом. Вполне логично то, что порт dest системы, с которой ведётся обмен данными, должен быть портом, который прослушивает эта система. В качестве его номера тоже, по умолчанию, используется 9899. Адреса портов src и dest, связанные с SCTP-механизмами, используются и в IP-заголовках:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| IP(v6)-заголовок (адреса, связанные с SCTP) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| UDP-заголовок (src: 9899, dest: 9899) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Общий заголовок SCTP (SCTP-порты) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Блок SCTP #1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Блок SCTP #n |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+


Обратите внимание на то, что протокол SCTP принимает во внимание UDP-заголовок при нахождении точки фрагментации данных.

Как пользоваться реализацией SCTP поверх UDP?​


С точки зрения использования описываемого здесь механизма в программах, можно сказать, что тут нет никакой разницы с тем, что было раньше. А именно, можно применять все стандартные возможности SCTP, в новых условиях доступны и все прежние API. Старые приложения будут работать правильно без необходимости внесения в них изменений или их перекомпиляции. Единственно — нужно правильно настроить UDP-порт (локальный порт, который прослушивает система, или порт src) и точку инкапсуляции (порт, который прослушивает удалённая система, или порт dest). Для конкретного сетевого пространства имён сделать это можно с помощью утилиты sysctl:

# sysctl -w net.sctp.encap_port=9899
# sysctl -w net.sctp.udp_port=9899


Можно, в качестве альтернативы, использовать команду setsockopt и настроить порт инкапсуляции для отдельного сокета (socket), для того, что в SCTP принято называть «ассоциацией» (association), или для транспортного механизма (transport):

setsockopt(SCTP_REMOTE_UDP_ENCAPS_PORT, port);


На стороне сервера обычно нет нужды специально настраивать порт инкапсуляции. Подробнее об этом мы поговорим в следующем разделе.

Порт инкапсуляции UDP​


Концепция порта инкапсуляции UDP отличается большой гибкостью. На стороне отправителя данных глобальный порт инкапсуляции демонстрирует лишь значение, задаваемое по умолчанию:

  • Порт инкапсуляции, заданный для отдельного сокета (per-socket) может быть использован тогда, когда другой сокет на одном хосте соединён с другим хостом, на котором используется другой UDP-порт.
  • Порт инкапсуляции, заданный для отдельной ассоциации (per-association) может быть использован тогда, когда тот же самый сокет соединён с другим хостом, на котором используется другой UDP-порт.
  • Порт инкапсуляции, задаваемый для отдельного транспортного механизма (per-transport), может быть использован тогда, когда в рамках одной и той же ассоциации нужно отправлять SCTP-пакеты, инкапсулированные в UDP-пакеты, пользуясь заданным транспортом.

На стороне получателя данных порт инкапсуляции обычно задавать не нужно:

  • Порт инкапсуляции каждой ассоциации можно выяснить, проанализировав первый пакет INIT. Другие такие пакеты, в которых указан другой UDP-порт src, после этого будут отбрасываться.
  • Порт инкапсуляции для каждого транспортного механизма можно узнать из входящих пакетов соответствующего пути, номер порта может быть обновлён в любое время.
  • Обычные SCTP-пакеты могут обрабатываться даже тогда, когда заданы порты инкапсуляции ассоциации и её транспортных механизмов.

Итоги​


Если вы пользуетесь SCTP, если вам нравятся возможности этого протокола, вроде поддержки множественной адресации (Multihoming), многопоточности (Multi-streaming) и разрешения на потерю некоторых пакетов (Partial Reliability), но вы сталкиваетесь с «проблемами промежуточных устройств», теперь вы можете прибегнуть к возможностям ядра Linux, которые дают нам самый простой способ эти проблемы обойти.

 
Сверху