В этой статье я расскажу о том, что такое рекурсивные сети передачи данных, и попытаюсь раскрыть смысл этой концепции. Во введении представлена историческая справка, её можно пропустить и перейти сразу к основаниям - они формулируются буквально в трех предложениях, затем обсуждаются немного подробнее. В заключение приводятся иллюстрации на простых примерах.
Содержание
Введение
Уже несколько лет я занимаюсь проектированием и практической реализацией высокоскоростной автономной беспроводной компьютерной сети для транспортных средств, в первую очередь для летательных аппаратов, авиации. Прежде чем перейти к созданию прототипа сети, необходимо было определиться с концепцией, которую можно взять за основу. Для этого я изучил существующие концепции сетей и выяснил, что развитие в этой сфере происходит по следующим направлениям:
эволюция IP-сетей: концепция Интернета, в которую добавляются какие-то новые сущности (в качестве примера можно привести протокол Mobile IP и его расширения);
оверлейные сети: строятся поверх Интернета, но сами уже не являются IP-сетями (в качестве примеров можно назвать Tor, I2P);
пост-IP-сети: строятся на каких-то других принципах, пытаются решить проблемы IP системно.
Основания Интернета закладывались в 60-80-е годы прошлого века, в эпоху проводных сетей, поэтому вопросам мобильности не придавалось большого значения. Как следствие, IP в мобильных сетях имеет определенные недостатки[1], более подробно о них я расскажу в другой статье. Сейчас же достаточно сказать, что эти недостатки показались мне слишком существенными, чтобы использовать его для создания новой, высокомобильной компьютерной сети. Оверлейные сети также основываются на Интернете, работают поверх него, так что напрямую не подходят для наших целей.
В итоге я обратился к концепциям пост-IP-сетей. В 2000-е и 2010-е годы тематика пост-IP-сетей была достаточно популярной среди исследователей, для обозначения всей этой активности используется тег "Future Internet"[2]. На исследования в этой области выделялись гранты: в США это проект GENI, в Японии - проект AKARI, в ЕС - проект FIRE, финансирование в рамках программ Еврокомиссии (FP7, Horizon 2020 и другие).
Но такой чисто научный, исследовательский подход, оказался совершенно неплодотворным. Умнее всех оказались американцы, они без лишнего шума свернули свою лавочку ещё в 2000-е. Лучше всех были японцы - в начале 2010-х проект AKARI был закрыт, но перед этим выпустил книгу, где они описали базовые принципы, на которых должен строиться новый интернет. К сожалению, никакой концепции сети там не представлено. Хуже всего дела с "Интернетом Будущего" обстоят в Европе - программы продолжаются до сих пор, проводятся исследования, выпускаются статьи - но при всем желании я не смог найти ни одной, имеющей хоть какое-то отношение к созданию концепции новой сети.
Одна концепция пост-IP-сети всё-таки существует - это RINA, Recursive InterNetwork Architecture. Впервые она была описана в книге Джона Дэя в 2007 году[3]. Концепция создана архитектором-одиночкой, работавшим над ней в течение нескольких десятилетий. В юности Джон Дэй лично наблюдал за созданием ARPANet'а, а затем и Интернета (в тот момент он был ещё студентом, так что не имел возможности внести какой-либо вклад в это). Потом он участвовал в комитетах по стандартизации OSI, где по-видимому получил сильнейшую психологическую травму от происходивших "протокольных войн"[4]. Сам Дэй, насколько я понимаю, оказался в лагере OSI, но при этом был сторонником компьютерных сетей, развиваемых IETF. Возможно это и сыграло решающую роль - его работы в рамках OSI умерли вместе с этой организацией, и при этом он не смог реализоваться в компьютерных сетях. С тех пор он постоянно пытался разобраться, как же должны быть устроены компьютерные сети на самом деле[5].
Как бы ни было удивительно, но RINA - это фактически единственная целостная, более-менее четко сформулированная концепция пост-IP-сети на сегодняшний день. Родившаяся под пером гения-теоретика, к сожалению она до сих пор остается концептом, применяющимся лишь в исследовательских проектах. Группа Дэя в Бостонском университете продолжает исследования в этой сфере[6], также есть исследователи в Европе[7].
Авторы RINA заявляют о том, что их подход решает большинство проблем, присущих IP-сетям, в частности интересующие нас проблемы мобильности. Подробнее об этом я также расскажу в следующих статьях, сейчас же мы сосредоточимся на ключевых понятиях рекурсивных сетей.
Я познакомился с этой концепцией почти 4 года назад, и с тех пор пытался как-то осмыслить её, освоить и применить в своем собственном дизайне. Но что-то постоянно не сходилось, меня не покидало ощущение того, что я ошибаюсь в своем понимании этой концепции, и потому на самом деле строю свою сеть - она называется Флорете - на размытых основаниях. Сколько я ни перечитывал книгу и статьи о RINA[8], ясности не прибавлялось.
Полгода назад я начал очередной пересмотр своих построений во Флорете, но в этот раз постепенно спустился до самых базовых определений, записанных мной буквально на первой странице - что есть узел в сети и сетевой слой. Оказалось, что именно здесь кроется краеугольный камень концепции, и весь мой предыдущий опыт с IP и оверлейными сетями не позволял мне сделать переход к рекурсивному взгляду. По сути, для осознания мне пришлось сделать то же открытие, что сделал и Джон Дэй - конечно, с его указанием, где искать. Отчасти это связано с не очень удачным изложением - у него в рассмотрение вводится сразу много второстепенных вещей, за которыми как "за деревьями" не видно "леса". Я постараюсь исправить этот недочет в изложении ниже. Также я немного доработал концепцию - у Дэя неявно подразумевается стековый взгляд на сетевые слои, но это ограничение является излишним сужением общности, и, как будет показано, нестековый взгляд полезен на практике[9].
В отличие от авторов RINA, я не считаю её общей теорией сетей, а лишь одним из возможных представлений. Но это действительно "естественное" представление, структуры которого не произвольны, а отражают особенности задачи сетевого взаимодействия компьютерных программ в общем виде. Допускаю, что существуют другие представления, но на данный момент это единственный известный взгляд на сети, принципиально отличающийся от концепции Интернета (IP-сетей). Думаю, это рекурсивное представление должно преподаваться в университетах как альтернатива модели Интернета (и точно оттуда должна быть исключена сетевая модель OSI, не имеющая связи с реальностью).
Основания рекурсивной сети
Принципиальное устройство рекурсивной сети лучше всего излагать на абстрактной модели, которая, однако, достаточно хорошо соотносится с компьютерными реалиями. Для этого введем несколько базовых определений.
Взаимодействующие сущности
Среда, обладающая возможностью исполнять программы, называется хостом (обычно это компьютер или виртуальная машина под управлением какой-либо операционной системы).
Программа, запущенная на каком-либо хосте, называется процессом.
Процесс, предоставляющий услуги другим процессам, называется сервисом.
Процесс, пользующийся услугами какого-либо сервиса, называется клиентом. Услуги предоставляются посредством обмена сообщениями: клиент инициирует взаимодействие, отправляя сообщение к сервису, и затем они взаимодействуют, передавая сообщения друг другу.
Теперь переходим к двум ключевым понятиям.
Сетевой узел и слой узлов
Сетевой узел - это сервис, предоставляющий клиентам услуги по обмену сообщениями. Для этого он присоединяется к сетевому слою.
Сетевой слой - это группа узлов, соединенных между собой и предоставляющих клиентам услуги по обмену сообщениями. Клиентами могут быть другие сервисы, в том числе и узлы других слоев.
На этом вся конструкция закончена! Сеть в данной концепции - это межпроцессное взаимодействие, организованное с использованием одного или нескольких сетевых слоёв. Рекурсивность заключается в том, что сетевые слои устроены одинаково (реализованы с использованием одних и тех же механизмов), отличаются только конфигурацией (политикой использования механизмов).
Очень важно осознать, что узел здесь - это специальный сервис (процесс), предоставляющий услуги межпроцессного взаимодействия другим процессам. Не более и не менее. Для сравнения, в IP-сети вообще нет узлов, есть только хосты, имеющие сетевые интерфейсы, которым назначаются IP-адреса. И вся IP-сеть представляется в виде хостов с интерфейсами, соединенными физическими линиями различной природы (например, Ethernet или Wifi), по которым передаются IP-датаграммы, инкапсулированые в кадры этих каналов. Для понимания рекурсивной сети необходимо выбросить из головы это представление - за текучую реку, за высокие горы! Далее мы попытаемся построить аналогичную иллюстрацию рекурсивного подхода, но сначала разберем устройство сетевого узла.
Интерфейс сетевого узла
Интерфейс узла, принадлежащего какому-либо сетевому слою, состоит из трех частей:
пользовательский интерфейс, с помощью которого клиенты могут передавать сообщения другим клиентам;
системный интерфейс, с помощью которого узлы в слое взаимодействуют между собой;
конфигурационный интерфейс, с помощью которого привелигированные сущности могут настраивать работу слоя.
Чтобы добавить немного конкретики, рассмотрим эти интерфейсы чуть подробнее. Пользовательский интерфейс включает следующие методы:
user_api {
register(service_name), // зарегистрировать сервис в сетевом слое
connect(service_name), // подключиться к сервису, зарегистрированному в слое
recv() -> (from, msg), // получить сообщение
send(to, msg) // послать сообщение
}
Cистемный интерфейс включает следующие группы методов:
system_api {
join, // методы для присоединения узла к слою, и для поддержания этой связи
transmit // методы для передачи пользовательских данных между узлами
}
Здесь мы не детализируем устройство system_api
- по сути, это детали реализации слоя.
Конфигурационный интерфейс включает следующие группы методов:
conf_api {
get, // методы для получения текущих значений конфигурации
set // методы для задания значений
}
Для реализации своего системного интерфейса узел может опираться на пользовательские интерфейсы узлов из других слоёв.
Определение сетевого слоя взято из концепции RINA, но расширено: слои не образуют стека и могут связываться между собой произвольным образом. Например, вполне нормальной и практичной является ситуация, когда слой A использует пользовательский интерфейс слоя B, а слой B - пользовательский интерфейс слоя A. В конце мы разберем такой случай на конкретном примере.
Клиенты сетевого слоя могут устанавливать соединения и обмениваться сообщениями с сетевыми сервисами, зарегистрированными в этом слое. Все сетевые сервисы, зарегистрированные в слое, образуют область видимости слоя. Обратиться к этим сетевым сервисам возможно только через узлы слоя, но не через узлы, использующие данный слой, или узлы, используемые данным слоем для своей работы.
Примеры
Wifi
Хост и роутер
Рассмотрим классическую Wifi-сеть в рекурсивном представлении. Допустим, что у нас имеется роутер R
и хост A
. Запустим на каждом из устройств сервис wifi
, управляющий приёмопередатчиком и способный отправлять и передавать данные с его помощью. На роутере запустим этот сервис в режиме "мастер", а на хосте - в режиме "слейв". Мастер инициирует создание сети, используя подсистему wifi.system_api.join
, и ожидает слейва. Слейв также использует wifi.system_api.join
и устанавливает соединение с мастером (ровно так сейчас и работает инфраструктурный Wifi). Таким образом создается сетевой слой wifi.ra
с двумя узлами и :
Для иллюстрации работы этого слоя представим, что на роутере запущен HTTP-сервис для его администрирования, admind
. Этот сервис локально подключается к сервису wifi
() и вызывает метод wifi.user_api.register("router-adm")
, регистрируясь под каким-то именем, в данном случае "router-adm".
Пользователь вводит в веб-браузере на хосте адрес "http://router-adm/", браузер как клиент подключается к сервису wifi
и выполняет вызов wifi.user_api.connect("router-adm")
. Сетевой слой wifi.ra
обеспечивает это соединение[10], и браузер взаимодействует с веб-сервисом по HTTP.
Два хоста и роутер
Мы рассмотрели работу сетевого слоя, соединяющего клиенты и сервисы на паре хостов. Теперь добавим в сеть ещё один хост, B
. Для этого запустим на нем сервис wifi
() в режиме слейва и используем wifi.system_api.join
для подключения к мастеру на роутере. Таким образом создается ещё один сетевой слой с двумя узлами и . Далее возможны два варианта:
мастер поддерживает два независимых слоя с парами узлов
wifi.ra
: (, ) иwifi.rb
: (, );мастер объединяет оба слоя в один с тремя узлами
wifi_layer
: (, , ).
В первом случае сервис wifi
может быть максимально простым, умеющим организовывать взаимодействие только между двумя узлами (точка-точка, p2p).
Во втором случае в слое уже потребуется механизм маршрутизации между несколькими узлами, слой становится шиной (bus).
Можно провести такую аналогию: второй вариант соответствует использованию маршрутизации, встроенной в канальный уровень Wifi (в существующем стандарте поддерживаются довольно сложные топологии сети и до 6 MAC-адресов в одном сообщении; однако, это не общая реализация). Первый вариант соответствует использованию IP для маршрутизации поверх канального уровня.
Частное решение для маршрутизации, использующееся сейчас в Wifi, выглядит плохим. Но если учесть рекурсивную природу слоя, то становится понятно, что механизм маршрутизации уже реализован в общем виде и может быть сконфигурирован под конкретные условия. И это будет скорее похоже на использование IP вместо MAC-адресов, чем использование MAC-адресов для маршрутизации, как это сделано сейчас. Поэтому второй вариант тоже имеет право на жизнь, он может быть даже предпочтительным в данном простом случае[11]. Решение о том, как распорядиться слоями, принимает архитектор конкретной сети. Кроме того, добавление слоя в этой сетевой архитектуре - всего лишь вопрос конфигурации. И далее мы увидим, когда второй вариант, т.е. добавление слоя, становится необходимым.
Рассмотрим второй вариант подробнее. В этом случае, для того, чтобы процессы на хостах A
и B
могли взаимодействовать, нам понадобится ещё один слой (аналог IP в традиционной модели). Для этого запустим на каждом устройстве сервис lan
. Аналогичным образом для простоты посчитаем, что этот сервис работает мастером на роутере. В таком случае lan
на роутере вызывает wifi.user_api.register("lan.r")
у обоих wifi-узлов и , регистрируясь как сервис в этих слоях и получая некое имя, допустим, "lan.r".
Сервисы lan
на хостах с помощью какого-то механизма обнаружения сервисов в слоях Wifi узнают имя сервиса lan
на роутере[12]. Тогда они выполняют вызов wifi.user_api.connect("lan.r")
, каждый у своего узла wifi
, а после установления соединения используют lan.system_api.join
. Таким образом, все сервисы lan
(, и ) оказываются соединенными между собой в сетевой слой lan_layer
.
Теперь запустим на хосте B
сервис Боба, например HTTP-сервер. Этот сервис обращается к своему сервису lan
, вызывая lan.user_api.register("bobby")
, и получает имя, допустим "bobby".
На хосте A
запустим Алису, веб-браузер. Пользователь вводит в браузере адрес "http://bobby/", браузер как клиент подключается к своему сервису lan
() и выполняет вызов lan.user_api.connect("bobby")
. Узлы lan_layer
'а взаимодействуют между собой, находят, что "bobby" зарегистрирован на , и обеспечивают соединение Алисы и Боба, ретранслируя сообщения через . Теперь клиент и сервер могут обмениваться HTTP-сообщениями. То, каким образом происходит установка соединений и передача данных - это детали реализации слоя, здесь мы их не обсуждаем. И вопрос адресации - тоже деталь реализации, они недоступны клиентам слоя! Адреса узлов задаются, когда они выполняют операцию присоединения к слою system_api.join
.
Заметим, что Алиса не может обратиться к сервису "lan.r" через процесс lan
- этот сервис находится в соответствующих слоях wifi
, а не lan
, и потому просто невидим для неё.
Направленный канал связи
В этом примере мы разберем возможность реализовать более сложную сеть, пользуясь нестековостью рекурсивного подхода. Представим, что на хостах A
и B
в Wifi-сети из предыдущего примера добавлены два специальных радио-устройства, оборудованных фазированными антенными решетками, способными формировать узкий луч и обеспечивать высокую скорость передачи данных. Пусть на хосте A
установлен передатчик, а на хосте B
- приёмник (даже если на обоих стоят приёмопередатчики, то в реальности может быть ситуация, когда на хосте A
со стороны B
наблюдается сильная помеха, из-за чего приём в этом направлении невозможен).
Нашей целью является добавить новый канал между A
и B
в сеть. Для начала на хосте A
запустим процесс fast_radio
, который будет отвечать за передачу (tx
), а на хосте B
- fast_radio
, отвечающий за приём (rx
). Они будут ответственны за новый сетевой слой fr_layer
, т.е. будут его узлами и . Но т.к. физический канал - односторонний, то сами они установить и поддерживать соединение не могут. Для работы этого радиоканала приёмник и передатчик должны иметь возможность обмениваться информацией, чтобы направить свои антенны друг на друга и поддерживать это состояние во время движения хостов.
Здесь мы видим тот случай, когда нам понадобится сетевой слой lan_layer
. Если бы у нас был только один сетевой слой wifi_layer
в режиме шины, то связать его с fr_layer
в единую сеть было бы неправильным с архитектурной точки зрения - wifi_layer
должен заниматься только вопросами Wifi, а никак не другой технологией радиодоступа. А вот lan_layer
, не привязанный к конкретным каналам передачи данных, идеально подходит на такую роль.
Сетевой слой lan_layer
может предоставить услугу обмена сообщениями нашим процессам и . Процесс регистрируется как сервис на узле , вызвав lan.user_api.register("rx")
, и получает имя, допустим rx
. Процесс обращается к узлу , вызвав lan.user_api.connect("rx")
. Слой lan_layer
обеспечивает соединение процессов, опираясь на слои wifi.ra
и wifi.rb
, так что и обмениваются сообщениями, чтобы настроить свои антенны друг на друга и сформировать направленный канал.
Теперь сетевой слой fr_layer
готов к работе, т.е. к передаче любых данных от процесса tx
к rx
(узла к ). Интегрируем этот сетевой слой в слой lan_layer
в качестве провайдера услуг. Для этого узел обращается к узлу , вызывая метод fr_layer.user_api.register("lan.b")
, и получает имя "lan.b". Узел обращается к узлу , вызывая метод fast_radio.user_api.connect("lan.b")
, таким образом устанавливая соединение с использованием слоя fr_layer
.
При реализации такой зависимости возможна оптимизация, т.к. узлы и уже соединены между собой в рамках слоя lan_layer
- так что им нужно лишь зарегистрировать соединение в fr_layer
и получить возможность прокачивать данные через него (в одну сторону). После этого узел во время передачи данных через lan.system_api.transmit
может использовать метод fast_radio.user_api.send("lan.b")
наравне с wifi.user_api.send("lan.r")
- т.е. слой lan_layer
начинает опираться на слой fr_layer
для доставки сообщений!
Эту взаимную зависимость можно изобразить в виде следующей диаграммы:
Заключение
В статье представлены основания рекурсивной компьютерной сети передачи данных RINA. Такая сеть представляет собой набор сетевых слоев. Каждый слой состоит из процессов-узлов, соединенных между собой и предоставляющих процессам-клиентам услуги передачи сообщений (межпроцессное взаимодействие) по унифицированному пользовательскому интерфейсу. С помощью этого же интерфейса слои взаимодействуют между собой, поскольку один слой может быть клиентом другого слоя.
Новым результатом является утверждение о том, что строгой стековой (или древовидной) структуры слоев не требуется: слои могут иметь произвольные, в том числе циклические зависимости.
Вопросы адресации узлов в такой архитектуре оказываются внутренним делом конкретного слоя, деталью реализации: клиенты слоя оперируют только именами сервисов, с которым они желают взаимодействовать.
Вопросы именования, идентификации и аутентификации сервисов и узлов, а также обнаружения сервисов не затрагивались в статье - это самостоятельные темы, требующие отдельного изучения в контексте рекурсивных сетей.
Важной темой для последующей работы является сравнение сетей, построенных на основе рекурсивной архитектуры, с традиционными сетями модели Интернета - выявление преимуществ и недостатков этого нового подхода к построению компьютерных сетей передачи данных.
Примечания, ссылки
[1]: Одной из проблем является необходимость сохранять IP-адрес при перемещении абонента между сетями - если этого не делать, то разрываются соединения TCP/IP. Для решения проблемы был создан протокол Mobile IP, но он не является оптимальным - требует туннелирования трафика в домашнюю сеть абонента.
[2]: "Интернет будущего" https://en.wikipedia.org/wiki/Future_Internet
[3]: John Day, Patterns in Network Architecture: A Return to Fundamentals, 2007.
[4]: Дебаты о правильном устройстве Интернета, в первую очередь между представителями "компьютерных сетей" IETF и "телекоммуникаций" OSI https://en.wikipedia.org/wiki/Protocol_Wars. Победили первые.
[5]: И продолжает воевать эту протокольную войну, к сожалению - его книга пестрит ремарками об этом, хотя всё это давно утратило актуальность.
[6]: Сайт группы исследователей RINA из Бостонского Университета: https://csr.bu.edu/rina/.
[7]: Стоит отметить профессора Eduard Grasa из CERCA-центра Fundacio i2CAT (Internet and Digital Innovation in Catalonia), Испания. См. https://i2cat.net/, https://en.wikipedia.org/wiki/CERCA_Institute.
[8]: Краткое изложение концепции RINA можно прочитать, например, в их первой статье John Day, Ibrahim Matta, and Karim Mattar. "Networking is IPC: A Guiding Principle to a Better Internet". In Proceedings of ReArch'08 - Re-Architecting the Internet, Madrid, SPAIN, December 2008. Co-located with ACM CoNEXT 2008. PDF. Перечитывая её сейчас, уже обладая пониманием основной идеи, меня удивляет, как же я не понимал её тогда, ведь там всё написано прямо в первом параграфе. Но, получается, можно читать и не понимать, автоматически адаптируя новое знание в старые представления.
[9]: Необходимость в нестековости сети я обнаружил очень давно, в самом начале работы над проектом. Тогда эта неспособность RINA решить мою модельную задачу сильно "резала глаза", так что я даже связался с одним из активных авторов, как раз с Эдуардом Граса, и задал ему этот вопрос напрямую. К сожалению, он не смог ответить на него тогда, и даже прекратил общение. Вообще, у меня создалось впечатление, что сообщество вокруг RINA построено по принципу некоего культа - утверджается, что RINA это не концепция, а единственно верная общая теория сетей. И если какая-то модельная задача не вписывается в неё, то это может вызвать ступор у фанатичных последователей. Кстати, подобное положение дел наблюдается в сообществе фанатов IPv6. По счастью, теперь я понимаю, что стековость в оригинальной RINA не является необходимой, и эта модельная задача отлично решается в моей расширеной версии.
[10]: Детали реализации нас сейчас не очень интересуют, но можно представить, что узел обращается к некой распределенной базе данных слоя, в которой указано, какие сервисы зарегистрированы в нем и через какой узел к ним можно обратиться. Таким образом узнаёт, что сервис "router-adm" зарегистрирован на узле .
[11]: Проблемы, возникшие в архитектуре Интернета из-за сетей типа шина (bus), разбираются в отличной статье Avery Pennarun'а, The world in which IPv6 was a good design, 2017. Там же показано, что IPv6 мог бы заменить MAC-адреса, шины и сетевые мосты в Интернете, однако это не было реализовано.
[12]: Механизмы обнаружения сервисов и работы с именами мы здесь не рассматриваем - это отдельная и очень непростая тема, достойная своей статьи, но в данной архитектуре это уже деталь реализации.