- PVSM.RU - https://www.pvsm.ru -
Здравствуй, уважаемое читатели!
Не так давно пришлось столкнуться с топологией сети в виде “избыточного кольца (redundant ring)”, о принципах работы которого и хотелось бы поговорить.
Дабы избежать недоразумений, сразу скажу, что в кольцо соединены только устройства на микроконтроллерах — на линии нет никаких коммутаторов и прочего такого. Только микроконтроллеры с физическим уровнем Ethernet в виде трёхпортового аппаратного свитча Micrel (о нём — далее). Ну и поскольку использовался Ethernet, то позволю себе вольность дальше употреблять выражение “сетевое кольцо”.
Изначально пост планировался в виде перевода способов организации кольца из вот этого документа [1] с шутками и прибаутками комментариями и дополнениями. В процессе разработки предложенные варианты были опробованы, но они не устроили по некоторым причинам. Вследствие чего родилась эта статья. Я надеюсь, что описанные здесь способы организации работы сетевого кольца могут пригодиться не только мне.
Кому интересно — добро пожаловать под кат.
Под катом много букв и трафика
Кто знаком с темой, могут смело переходить к следующему разделу (“Суть задачи”).
Начнём издалека. Если в одну сеть необходимо объединить больше двух устройств, то возникает задача о конфигурации сети. Ведь соединены между собой устройства могут быть несколькими разными способами (в разном порядке, так сказать). Схема расположения и соединения сетевых устройств называется топологией сети [2]. Существуют три базовые топологии: Звезда, Шина и Кольцо. В этой статье мы будем рассматривать топологию в виде кольца.
Итак, представим себе сферические устройства в вакууме несколько устройств, объединённых в сеть таким образом:
Это типичное сетевое кольцо. Обычно в таком кольце данные передаются строго в одном направлении (например, по часовой стрелке). В таком случае обрыв хотя бы одной линии связи может привести к неработоспособности всей системы. Но в зависимости от способа организации связи между устройствами кольцо может стать “избыточным”. Это значит, что даже если мы умышленно (или не умышленно) разорвём одну из связей, то передача данных между устройствами всё ещё будет возможна в полном объёме. Но для этого необходима правильная организация обмена в сетевом кольце. Чем мы и займёмся далее.
Стоит упомянуть, что в кольцевой топологии избыточность может достигаться так же за счёт использования двойного кольца, в котором данные дублируются и передаются в разных направлениях, но мы далее будем говорить только о топологии с одним кольцом.
Итак, избыточность позволяет в случае одиночного обрыва обеспечить работоспособность. Это, несомненно, плюс, но с другой стороны это создаёт некоторые проблемы. Например, если одно из устройств отправляет широковещательные пакеты (бродкаст), то каждое последующее устройство в кольце будет транслировать их своему соседу и так по кругу до бесконечности, что в конечном итоге может привести к полной неработоспособности сети (так называемый бродкаст шторм [3]). Для предотвращения этого были разработаны специальные протоколы, позволяющие избежать “зацикливаний”, а также позволяющие находить кратчайшие пути в сетевой топологии. Такими протоколами, например, являются STP, RSTP, MSTP и STB. В основном эти протоколы предназначены для сложных топологий, в которых могут одновременно использоваться конфигурации и в виде звезды, и в виде кольца, и в более сложных комбинациях трёх базовых топологий. Более подробно можно ознакомиться тут [4], тут [5], и тут [6].
Организовать обмен между устройствами таким образом, чтобы не возникало бродкаст шторма, а в случае единичного обрыва сеть должна остаться работоспособной. Кроме того должна быть возможность точно определить место разрыва кольца.
Поскольку все дальнейшие алгоритмы будут непосредственно связаны с со свитчем KSZ8863/RLL, то дальше немного о нём:
Даташит тут [7]
Вкратце:
На начальном этапе разработки сразу же было решено отказаться от существующих протоколов управления сетью (STP, RSTP и т.п.) по следующим причинам:
А также, поскольку в нашей системе был нужен только бродкаст, то для уменьшения нагрузки было принято решение отказаться и от протоколов транспортного уровня, то есть делать всё на “голых” Ethernet пакетах.
Но сначала давайте посмотрим, как нам предлагает строить кольцо производитель свитчей.
Далее идёт вольный пересказ документа по ссылке [1] из заголовка статьи.
В свитче реализован аппаратный механизм “Обучения” и “Пересылки” пакетов. После получения пакета, свитч “обучается” — сохраняет в таблице динамических маршрутов MAC адрес отправителя и соответствующий номер порта, из которого был получен пакет. Решение о пересылке пакетов принимается на основании этой таблицы и MAC адреса устройства-получателя (Destination MAC). Если в таблице найдено совпадение (MAC адрес уже известен свитчу), то пакет перенаправляется в соответствующий порт. Если адрес неизвестен, то пакет бродкастится во все порты, кроме того, из которого он был получен (это поведение по умолчанию и оно настраивается).
Таким образом (при получении пакета) адрес отправителя только запоминается и не используется при принятии решения о маршрутизации. Это чревато возникновением “зацикленных” пакетов. Однако, если свитч умеет фильтровать (а KSZ8863 умеет) входящие пакеты на основании адреса отправителя, то реализация неуправляемого кольца возможна. Как только свитч получает пакет, адрес отправителя в котором совпадает с локальным MAC адресом одного из портов, то пакет отбрасывается (drop). То есть пакет удаляется из кольца после прохождения полного круга.
Схематично это можно изобразить так:
Плюсы:
Минусы:
Нам не подходит. Смотрим дальше.
Самый простой вариант организовать избыточность с помощью фильтрации по адресу отправителя — это отправлять пакеты, полученные из процессора, сразу в два внешних порта (в обе стороны кольца). В таком случае при единичном обрыве пакет всё-равно получат все устройства. Самой главной проблемой является то, что если в кольце нет обрыва, то все устройства получат этот пакет дважды (дубликат). С другой стороны — по отсутствию дубликатов можно определить, между какими устройствами произошёл обрыв. Работает всё примерно так:
Здесь как и в прошлом варианте пакет пробегает по кругу и дропается отправителем.
Как уже было указано, недостатком являются дубликаты пакетов, которые увеличивают нагрузку на процессор и снижают эффективность работы сети. Помимо этого получение дубликатов приведёт к проблеме с механизмом обучения у свитча. Во-первых, динамческая таблица постоянно будет меняться (то с одного порта придёт пакет, то с другого, а MAC адрес одинаковый) и в таблице всегда последним останется худший вариант пути (пакет, который шёл дольше всех запишет “худший” порт в таблицу). Во-вторых — это может привести к тому, что один из пакетов будет дропнут как “локальный пакет”. Локальный — это пакет, пришедший из порта, на который он должен перенаправляться при маршрутизации. Это получается примерно так:
Пояснение к рисунку:
Потом происходит следующее:
В итоге имеем то, что мы не можем точно определить, почему не было дубликата. И что именно явилось причиной пропадания дубликата — обрыв или случайный дроп.
Для того, чтобы убедиться, что оба дубликата приходят, когда нет обрыва — нужно отключить обучение в свитче. Отключение обучения приводит к тому, что таблица маршрутизации всегда остаётся пустой и локальные пакеты никогда не дропаются. Главным недостатком такого решения является, опять же, перекладывание нагрузки по разгребанию дубликатов на процессор. В таком случае все приходящие пакеты направляются в порт 3 в независимости от того, адресованы они процессору или нет. Решить эту проблему можно с помощью запрета перенаправления пакетов с неизвестными адресами в третий порт (чтобы все пакеты, проходили “мимо” кроме тех адресов, которые есть в статических маршрутах).
Плюсы:
Минусы:
Примечание: После всех изысков я таки пришёл к этому варианту с некоторыми изменениями и своим протоколом. Но пока мы этого не знаем, поэтому давайте продолжим пробовать варианты...
Как уже было сказано, главным недостатком предыдущего способа являются дубликаты пакетов, увеличивающие нагрузку на процессор. В этом варианте мы это исправим, сделав вот что:
Также здесь мы будем использовать специальное прерывание свитча, которое сигнализирует о изменении состояния линии (Link Status Interrupt).
Это решение уже требует постоянного контроля и (в случае обрыва) управления от процессора. То есть фактически кольцо уже становится управляемым (managed), но взамен мы получаем быстрое обнаружение неисправности и быструю переконфигурацию кольца даже в больших сетях.
В итоге весь трафик приходит на первый порт и уходит со второго (пакеты бегают по часовой стрелке).
Если между устройствами обрывается связь, то на эти устройства приходят прерывания (Link Status Interrupt) и мы должны перейти к переконфигурации кольца.
Процедура очень простая и проходит с минимальными задержками
KSZ8863 имеет встроенный механизм для определения местоположения обрыва (из внутренних регистров можно прочитать примерное расстояние в метрах до точки повреждения кабеля).
Когда кабель будет исправлен, мы снова получим прерывание Link Status Interrupt
Пакеты начинают опять бегать по часовой стрелке. Профит!
Также в документе приводятся следующие цифры:
Задержка при перестройке кольца:
Latency = Tinterrupt + Tread interrupt + Tbroadcast message + Tenable P2 Receive
Tinterrupt = приблизительно 100us
Tread / write = 4.8us для интерфейса SPI с частотой 5Mhz (чтобы записать один регистр нужно предать три байта)
Tmessage = (n — 1) x 7.7us (где n — это количество устройств в кольце) — время, за которое до последнего устройства дойдёт бродкаст. Подразумевается пакет 64 байта длиной и задержка 7.7us на одно устройство
Получаем:
Latency = 100us + 4.8us + (n — 1) x 7.7us + 4.8us
Например, для кольца из 16 устройств получим:
Latency = 225us (примерно)
Исходя из вышеприведённой формулы можно рассчитать максимальное количество устройств в сети при максимально допустимой задержке:
Например, если максимально допустимая задержка равна 1мс, то получим:
1ms = 100us + 4.8us + (n — 1) x 7.7us + 4.8us
n < (890.4 / 7.7) + 1
Максимальное количество устройств в кольце: n = 116
На этом официальный документ от разработчиков KSZ8863 заканчивается.
Все счастливы, всем спасибо, занавес…
А после занавеса, как обычно, начинается самое интересное:
Вариант №3 в принципе устраивал всем. Естественно, с небольшими правками, потому как в представленном сейчас виде он не совсем надёжен. Поясню на примере: если бродкаст пакет, отправленный после выявления обрыва, получат не все устройства (мало ли, как сложится, может питание пропадёт), то связь полностью развалится.
Но глубже копать это не пришлось, потому что появилась другая глобальная проблема.
Итак, у нас есть схематически изображённое кольцо:
К сожалению вагоны в поезде так не сцеплены.
Расцепляем наш поезд, устанавливаем устройства в вагоны так, как они должны стоять и получаем такое:
Сразу бросается в глаза первая оплошность — на рисунке тот кабель, который идёт “по низу” вагонов слишком длинный. Длину всего состава сейчас точно не вспомню, но это примерно метров 250. То есть для ethernet’а кабель такой длины смертелен.
На самом деле это не проблема, и главный разработчик придумал оптимальный вариант — вот так:
Как видим — теперь вагоны соединены через один. Здесь же проставлены номера портов на устройствах. И можно заметить, что они очень даже хорошо вписываются в предложенный ранее вариант построения кольца.
Профит? — Как бы не так.
Неожиданно (ага, как всегда) появилось дополнительное условие, что в процессе эксплуатации любой вагон (кроме головных) могут развернуть. А система, естественно, должна работать. И получается вот такая беда:
Как видим, связь по кольцу нарушилась. Если не совсем понятно почему — поясню. На предыдущей картинке можно полностью пройти по кольцу, если предположить, например, что порт 2 работает только на передачу. То есть можно пройти по цепочке: порт 2 --> порт 1, порт 2 --> порт 1. И так далее. После разворота вагона этот принцип нарушается.
Поэтому от, казалось бы, идеального варианта пришлось отказаться и искать новый.
Ну что ж, приступим к изобретению велосипедов…
Тут пришло понимание, что процессор грузить всё-равно придётся и одним физическим уровнем с минимальным управлением не отделаешься. Нужно уметь динамически перестраивать кольцо для любой конфигурации соединений.
Как ни крути, пришлось изобретать свой протокол для управления кольцом. Собственно, его эпичное название приведено выше.
Ещё раз напомню — критично было как можно меньше грузить процессор работой с сетью. Поэтому с точки зрения “правильности” использования Ethernet всё, что дальше написано в этом варианте, является ужасом, летящим на крыльях ночи…
Смысл инициализации в том, чтобы вообще ничего без нашего разрешения на процессор не попадало и в сеть не уходило.
Каждое устройство имеет таблицу маршрутов (фактически это массив структур). Каждая такая структура относится к определённому устройству в кольце. Структура примерно такая:
struct
{
// Маршрут к устройству номер ...
unsigned char DestinationDevice;
// В какой из портов нужно слать пакеты, чтобы достучаться до устройства
unsigned char SendToPort;
// Минимальное "расстояние" до устройства - количество промежуточных устройств + 1.
unsigned char MinDistance;
// Сколько раз этот маршрут НЕ обновлялся
unsigned char NotUpdatedTimes;
} Route;
То есть каждое устройство знает, как добраться до любого другого (в какой порт отправлять пакеты и как далеко устройство расположено). Весь алгоритм крутится вокруг этой таблицы маршрутов.
После включения питания вся таблица маршрутов забивается заведомо неверными значениями, чтобы кольцо строилось, так сказать, с ноля.
С периодичностью N миллисекунд каждое устройство отправляет свой MRRCP пакет в порт 1 и порт 2. N можно взять с потолка. Например, 200. Всё зависит от того, насколько критична скорость переконфигурации кольца после обрыва. Нам важнее была надёжность (чтобы точно перестроилось, а не перестроилось как-нибудь, зато быстро).
Пакет отправляется такой:
Вот небольшой пример на рисунке:
Для устройства №8 (оно противоположно первому) маршруты построятся в зависимости от того, по какой стороне кольца быстрее «добежит» самый первый пакет.
Обратите внимание, что на рисунке указаны маршруты, которые хранятся в памяти процессора, а не статические маршруты свитча.
О записи статических маршрутов в свитч:
Маршруты нужно записывать таким образом:
Dest.MAC — 55:55:00:00:00:XX, где (XX — номер устройства из цепочки)
Forwarding Port — номер порта, обратный тому, откуда пришёл пакет + порт 3 (процессор). То есть если пакет пришёл с порта 1, то записать нужно порт 2 и наоборот. Почему именно так — см. пояснение ниже.
В общем работа алгоритма получается такой:
Благодаря статическим маршрутам (с портами “наоборот”) мы можем вещать бродкаст, но не будет возможности связи типа “точка-точка”.
Для вещания бродкастом нужно отправлять пакет в оба порта (1 и 2) с Dest.MAC 55:55:00:00:00:XX, где XX — это номер своего (да-да, именно своего) устройства. Из-за переворота номеров портов в статических маршрутах, бродкаст пройдёт по всем устройствам по кольцу и дропнется на устройстве, “с обратной стороны" кольца от отправителя. Дубликатов не будет, потому что они будут дропаться на основании статических маршрутов
Пояснение:
Точка разрыва определяется на тех устройствах, у которых максимальное расстояние до самого отдалённого устройства равно N — 1 (где N — это количество устройств в сети). См. картинку:
Видно, что у устройств №1 и №6 будут маршруты с расстоянием (6 — 1) = 5, а значит между ними обрыв.
Для кольца с нечётным количеством устройств обрыв считается немного по-другому — там чуть проще.
Альтернативным вариантом для определения обрыва может быть использование прерывания Link Status, как описано выше.
Плюсы:
Минусы:
Всё заработало на отладочном стенде из 3-х устройств, но в изобретении велосипедов меня уже было не остановить…
При проверке выяснилось, что устройства с номерами выше седьмого работают в кольце неправильно. Убив день на поиски проблемы оказалось, что я (каюсь и сыплю на голову пепел) не заметил сноску в даташите свитча о том, что статических маршрутов у него всего восемь. То есть алгоритм работает только если устройств в кольце меньше 8. Это был эпик-фейл. Зато появилась веская причина переработать костыль с Dest.MAC для бродкаста из предыдущего велосипеда.
Вот так родился новый и финальный вариант. Он отличается только работой со свитчем. С MRRCP пакетами всё остаётся так же.
Пакеты MRRCP отправляются так же, как и в предыдущем варианте. А в обработчике просто необходимо убрать запись статических маршрутов в свитч.
Теперь бродкаст делается так:
Определение разрыва кольца — аналогично, как и в предыдущем варианте.
Если нужна связь “точка-точка”, то её можно реализовать, добавив в инициализацию настройку статического перенаправления пакетов с Dest.MAC равным локальному MAC’у в порт 3. А также настроить перенаправление пакетов с неизвестными адресами в порты 1 и 2.
Плюсы:
Минусы:
Время перестройки кольца точно не замерялось. С очень сильно завышенными значениями таймаута валидности маршрутов и периодом отправки MRRCP пакетов, кольцо пересобирается за 1-2 секунды. В нашем случае этого оказалось достаточно.
Итог можно подвести так — покатавшись на разных велосипедах, мы вернулись к варианту №2 (от разработчиков свитча) с доработками в виде MRRCP.
Конструктивная критика категорически приветствуется.
Надеюсь, что кому-то эта статья окажется полезной и некоторые грабли получится обойти стороной. Удачи!
UPD: Спасибо за инвайт, перенёс статью в хаб «Программинг микроконтроллеров»
p.s. Исходники дать не могу. Я надеюсь, что Вы понимаете почему.
p.p.s Прошу прощения, если объяснения местами показались слишком затянутыми и термины используются не совсем правильно — я старался объяснить максимально доступным языком, чтобы было понятнее.
p.p.p.s. Очень интересно узнать, как бы Вы решали вот такую задачу с учётом вышеизложенного? Спасибо.
Автор: ak84
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/mikrokontrollery/26261
Ссылки в тексте:
[1] вот этого документа: http://www.micrel.com/_Products/LanSolutions/UnmanagedRedundantRingWP.pdf
[2] топологией сети: http://ru.wikipedia.org/wiki/%D0%A1%D0%B5%D1%82%D0%B5%D0%B2%D0%B0%D1%8F_%D1%82%D0%BE%D0%BF%D0%BE%D0%BB%D0%BE%D0%B3%D0%B8%D1%8F
[3] бродкаст шторм: http://ru.wikipedia.org/wiki/%D0%A8%D0%B8%D1%80%D0%BE%D0%BA%D0%BE%D0%B2%D0%B5%D1%89%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D1%88%D1%82%D0%BE%D1%80%D0%BC
[4] тут: http://habrahabr.ru/post/156695/
[5] тут: http://habrahabr.ru/post/143768/
[6] тут: http://habrahabr.ru/post/129559/
[7] Даташит тут: http://www.micrel.com/_PDF/Ethernet/datasheets/KSZ8863MLL_FLL_RLL_DS.pdf
[8] Источник: http://habrahabr.ru/post/168119/
Нажмите здесь для печати.