![](https://habrastorage.org/storage2/eb0/1c8/c52/eb01c8c5245f9d0fdf2ed81456c3ac5c.png)
Здравствуй, уважаемое хабрасообщество!
Не так давно пришлось столкнуться с топологией сети в виде “избыточного кольца (redundant ring)”, о принципах работы которого и хотелось бы поговорить.
Дабы избежать недоразумений, сразу скажу, что в кольцо соединены только устройства на микроконтроллерах — на линии нет никаких коммутаторов и прочего такого. Только микроконтроллеры с физическим уровнем Ethernet в виде трёхпортового аппаратного свитча Micrel (о нём — далее). Ну и поскольку использовался Ethernet, то позволю себе вольность дальше употреблять выражение “сетевое кольцо”.
Изначально пост планировался в виде перевода способов организации кольца из вот этого документа с
Кому интересно — добро пожаловать под кат.
Под катом много букв и трафика
Немного самой простой теории
Кто знаком с темой, могут смело переходить к следующему разделу (“Суть задачи”).
Начнём издалека. Если в одну сеть необходимо объединить больше двух устройств, то возникает задача о конфигурации сети. Ведь соединены между собой устройства могут быть несколькими разными способами (в разном порядке, так сказать). Схема расположения и соединения сетевых устройств называется топологией сети. Существуют три базовые топологии: Звезда, Шина и Кольцо. В этой статье мы будем рассматривать топологию в виде кольца.
Итак, представим себе
![](https://habrastorage.org/storage2/b92/879/24c/b9287924c0bb2ebbdbf1e0baa698544d.png)
Стоит упомянуть, что в кольцевой топологии избыточность может достигаться так же за счёт использования двойного кольца, в котором данные дублируются и передаются в разных направлениях, но мы далее будем говорить только о топологии с одним кольцом.
Итак, избыточность позволяет в случае одиночного обрыва обеспечить работоспособность. Это, несомненно, плюс, но с другой стороны это создаёт некоторые проблемы. Например, если одно из устройств отправляет широковещательные пакеты (бродкаст), то каждое последующее устройство в кольце будет транслировать их своему соседу и так по кругу до бесконечности, что в конечном итоге может привести к полной неработоспособности сети (так называемый бродкаст шторм). Для предотвращения этого были разработаны специальные протоколы, позволяющие избежать “зацикливаний”, а также позволяющие находить кратчайшие пути в сетевой топологии. Такими протоколами, например, являются STP, RSTP, MSTP и STB. В основном эти протоколы предназначены для сложных топологий, в которых могут одновременно использоваться конфигурации и в виде звезды, и в виде кольца, и в более сложных комбинациях трёх базовых топологий. Более подробно можно ознакомиться тут, тут, и тут.
Суть задачи
Организовать обмен между устройствами таким образом, чтобы не возникало бродкаст шторма, а в случае единичного обрыва сеть должна остаться работоспособной. Кроме того должна быть возможность точно определить место разрыва кольца.
Что имеем
- 14 устройств на микроконтроллерах (STM32F207), установленные в электропоезде
- Устройства соединены в одно кольцо
- В качестве физического уровня Ethernet используется аппаратный свитч Micrel KSZ8863/RLL
- Нагрузка на процессор от использования сети должна быть минимальной. Фактически — это одно из самых “главных-преглавных” требований, поскольку процессор в реальном времени занимается довольно таки ресурсоёмкими задачами и на счету каждая миллисекунда
- Так сложилось, что связь типа “точка-точка” оказалась не нужна. Используется только бродкаст. Но финальное решение позволяет реализовать и это (с определённым допиливанием)
Поскольку все дальнейшие алгоритмы будут непосредственно связаны с со свитчем KSZ8863/RLL, то дальше немного о нём:
Даташит тут
Вкратце:
- трёхпортовый свитч (два Ethernet порта “наружу” и один “внутрь” на процессор, далее будут называться портами №1, 2 и 3 соответственно)
- Отдельные MAC адреса для портов 1 и 2
- Интерфейсы на процессор: MII и RMII плюс интерфейсы для управления — I2C и SPI
- 8 статических маршрутов (о, как поздно я эту сноску в даташите увидел, но об этом далее)
- Фильтрация “своих” пакетов при маршрутизации (такая себе защита от зацикливания)
- Динамические маршруты (сам обучается на основании MAC адресов и номера порта, по которому пакеты приходят)
- Поддержка VLAN ID, скорость 10/100 мегабит, автосогласование, сам умеет определять “перекрещенность” кабеля, расстояние в метрах до точки обрыва кабеля и ещё несколько плюшек
Поиск решения
На начальном этапе разработки сразу же было решено отказаться от существующих протоколов управления сетью (STP, RSTP и т.п.) по следующим причинам:
- Слишком много лишнего для нашей простой топологии
- Ни один из этих протоколов аппаратно не поддерживается физическим уровнем. И, соответственно, нагрузка ложится на процессор, которому и так уже не сладко. Хотя в даташите свитча и есть строчка “IEEE 802.1d rapid spanning tree protocol support”, фактически это означает, что в даташите дан примерный алгоритм, который можно реализовать средствами процессора, что нам не совсем подходит.
А также, поскольку в нашей системе был нужен только бродкаст, то для уменьшения нагрузки было принято решение отказаться и от протоколов транспортного уровня, то есть делать всё на “голых” Ethernet пакетах.
Но сначала давайте посмотрим, как нам предлагает строить кольцо производитель свитчей.
Вариант №1 (попытка решить задачу только средствами физического уровня — unmanaged ring)
Далее идёт вольный пересказ документа по ссылке из заголовка статьи.
Фильтрация по MAC адресу устройства-отправителя пакета (MAC Source Address Filtering)
В свитче реализован аппаратный механизм “Обучения” и “Пересылки” пакетов. После получения пакета, свитч “обучается” — сохраняет в таблице динамических маршрутов MAC адрес отправителя и соответствующий номер порта, из которого был получен пакет. Решение о пересылке пакетов принимается на основании этой таблицы и MAC адреса устройства-получателя (Destination MAC). Если в таблице найдено совпадение (MAC адрес уже известен свитчу), то пакет перенаправляется в соответствующий порт. Если адрес неизвестен, то пакет бродкастится во все порты, кроме того, из которого он был получен (это поведение по умолчанию и оно настраивается).
Таким образом (при получении пакета) адрес отправителя только запоминается и не используется при принятии решения о маршрутизации. Это чревато возникновением “зацикленных” пакетов. Однако, если свитч умеет фильтровать (а KSZ8863 умеет) входящие пакеты на основании адреса отправителя, то реализация неуправляемого кольца возможна. Как только свитч получает пакет, адрес отправителя в котором совпадает с локальным MAC адресом одного из портов, то пакет отбрасывается (drop). То есть пакет удаляется из кольца после прохождения полного круга.
Схематично это можно изобразить так:
![](https://habrastorage.org/storage2/9cf/208/96b/9cf20896b16cb4d3f2f5d04b4537fc21.png)
Примерный алгоритм:
- Свитч получает пакет от процессора (из порта 3)
- Отправляет его в один из портов (например, всегда в порт №2)
- Пакет пробегает полное кольцо и дропается отправителем (на рисунке 1-й свитч отправил пакет и, после полного круга, он же его дропнул)
Плюсы:
- Всё сделано силами свитча, на процессор нагрузки нет
Минусы:
- Если разорвать одну из связей, то сеть перестанет работать
Нам не подходит. Смотрим дальше.
Вариант №2 — Избыточность на основании варианта №1
Самый простой вариант организовать избыточность с помощью фильтрации по адресу отправителя — это отправлять пакеты, полученные из процессора, сразу в два внешних порта (в обе стороны кольца). В таком случае при единичном обрыве пакет всё-равно получат все устройства. Самой главной проблемой является то, что если в кольце нет обрыва, то все устройства получат этот пакет дважды (дубликат). С другой стороны — по отсутствию дубликатов можно определить, между какими устройствами произошёл обрыв. Работает всё примерно так:
![](https://habrastorage.org/storage2/41c/f32/f4c/41cf32f4c8a0690170c013223cc30f4b.png)
Здесь как и в прошлом варианте пакет пробегает по кругу и дропается отправителем.
Как уже было указано, недостатком являются дубликаты пакетов, которые увеличивают нагрузку на процессор и снижают эффективность работы сети. Помимо этого получение дубликатов приведёт к проблеме с механизмом обучения у свитча. Во-первых, динамческая таблица постоянно будет меняться (то с одного порта придёт пакет, то с другого, а MAC адрес одинаковый) и в таблице всегда последним останется худший вариант пути (пакет, который шёл дольше всех запишет “худший” порт в таблицу). Во-вторых — это может привести к тому, что один из пакетов будет дропнут как “локальный пакет”. Локальный — это пакет, пришедший из порта, на который он должен перенаправляться при маршрутизации. Это получается примерно так:
![](https://habrastorage.org/storage2/01b/9e3/1f1/01b9e31f12d4e5837d2405359de15f70.png)
Пояснение к рисунку:
- Процессор №4 отправляет пакет устройству №1
- Свитч №4 отправляет его в оба внешних порта
- На свитч №1 первым придёт пакет в порт 1 (потому что короче расстояние) и в таблице маршрутизации для MAC 4 установится порт равным 1.
- Потом на свитч №1 придёт дубликат пакета в порт 2 (расстояние больше и надо через два устройства пройти) и в таблице маршрутизации для MAC 4 установится порт равным 2.
Потом происходит следующее:
![](https://habrastorage.org/storage2/70b/c5d/c75/70bc5dc751148b1b33cc722c98fc6f93.png)
- Процессор №2 отправляет пакет устройству №4
- Свитч №2 отправляет его в оба внешних порта
- Пакет приходит в свитч №1 на порт 2 и дропается потому что в таблице маршрутизации установлено, что для MAC 4 пакеты нужно перенаправлять на тот же порт.
- Устройство №4 в итоге получит только один пакет без дубликата.
В итоге имеем то, что мы не можем точно определить, почему не было дубликата. И что именно явилось причиной пропадания дубликата — обрыв или случайный дроп.
Для того, чтобы убедиться, что оба дубликата приходят, когда нет обрыва — нужно отключить обучение в свитче. Отключение обучения приводит к тому, что таблица маршрутизации всегда остаётся пустой и локальные пакеты никогда не дропаются. Главным недостатком такого решения является, опять же, перекладывание нагрузки по разгребанию дубликатов на процессор. В таком случае все приходящие пакеты направляются в порт 3 в независимости от того, адресованы они процессору или нет. Решить эту проблему можно с помощью запрета перенаправления пакетов с неизвестными адресами в третий порт (чтобы все пакеты, проходили “мимо” кроме тех адресов, которые есть в статических маршрутах).
Плюсы:
- При обрыве сеть продолжит работать
Минусы:
- Дополнительная нагрузка на процессор
Примечание: После всех изысков я таки пришёл к этому варианту с некоторыми изменениями и своим протоколом. Но пока мы этого не знаем, поэтому давайте продолжим пробовать варианты...
Вариант №3 — Улучшенный вариант предыдущего (теперь — управляемое кольцо)
Как уже было сказано, главным недостатком предыдущего способа являются дубликаты пакетов, увеличивающие нагрузку на процессор. В этом варианте мы это исправим, сделав вот что:
- Каждое устройство в кольце должно отключить у себя один из внешних портов на приём (внимание: все устройства должны отключить порт с одним и тем же номером!). Благодаря этому получим:
- Пакеты в кольце будут двигаться только в одну сторону
- Процессор-приёмник в итоге получит только один пакет без дубликата
- Отключенный порт будет использоваться в случае разрыва кольца
Также здесь мы будем использовать специальное прерывание свитча, которое сигнализирует о изменении состояния линии (Link Status Interrupt).
Это решение уже требует постоянного контроля и (в случае обрыва) управления от процессора. То есть фактически кольцо уже становится управляемым (managed), но взамен мы получаем быстрое обнаружение неисправности и быструю переконфигурацию кольца даже в больших сетях.
Инициализация свитча:
![](https://habrastorage.org/storage2/f4f/36f/6a8/f4f36f6a803e89da43f1baf6ae2f741c.png)
- Отключаем обучение на портах 1 и 2 в каждом устройстве.
- Настраиваем статический маршрут, по которому все пакеты с адресом получателя, равным нашему локальному, направляются в порт 3 (на процессор).
- Включаем маршрутизацию для неизвестных адресов (все пакеты с неизвестными адресами получателей будут бродкаститься на оба внешних порта).
- Отключаем приём на 2-м порту во всех устройствах.
- Включаем прерывание по изменению статуса линии.
В итоге весь трафик приходит на первый порт и уходит со второго (пакеты бегают по часовой стрелке).
Если между устройствами обрывается связь, то на эти устройства приходят прерывания (Link Status Interrupt) и мы должны перейти к переконфигурации кольца.
Переконфигурация кольца:
![](https://habrastorage.org/storage2/d8f/705/6bc/d8f7056bc68fb49919b76d93291b9de2.png)
Процедура очень простая и проходит с минимальными задержками
- После получения проверяем, по какому порту обрыв.
- Если по первому, то
- Отправляем бродкаст сообщение всем устройствам о том, что необходимо включить порт 2 на приём
- Включаем приём пакетов из 2-го порта у себя
- Если по второму порту, то
- Игнорируем бродкаст сообщение о том, что необходимо включить 2-й порт на приём
- Делаем диагностику, если нужно.
KSZ8863 имеет встроенный механизм для определения местоположения обрыва (из внутренних регистров можно прочитать примерное расстояние в метрах до точки повреждения кабеля).
Восстановление кольца:
Когда кабель будет исправлен, мы снова получим прерывание Link Status Interrupt
- Проверяем, по какому порту пришло прерывание
- Если по первому, то отправляем бродкаст сообщение всем устройствам о том, что необходимо отключить приём из порта 2
Пакеты начинают опять бегать по часовой стрелке. Профит!
Также в документе приводятся следующие цифры:
Задержка при перестройке кольца:
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 в принципе устраивал всем. Естественно, с небольшими правками, потому как в представленном сейчас виде он не совсем надёжен. Поясню на примере: если бродкаст пакет, отправленный после выявления обрыва, получат не все устройства (мало ли, как сложится, может питание пропадёт), то связь полностью развалится.
Но глубже копать это не пришлось, потому что появилась другая глобальная проблема.
Итак, у нас есть схематически изображённое кольцо:
![](https://habrastorage.org/storage2/c97/fcf/2f3/c97fcf2f3bfbb9b381c554b78985f82c.png)
К сожалению вагоны в поезде так не сцеплены.
Расцепляем наш поезд, устанавливаем устройства в вагоны так, как они должны стоять и получаем такое:
![](https://habrastorage.org/storage2/d1f/60c/4ea/d1f60c4eaeb01f43385e8573c99fec3e.png)
Сразу бросается в глаза первая оплошность — на рисунке тот кабель, который идёт “по низу” вагонов слишком длинный. Длину всего состава сейчас точно не вспомню, но это примерно метров 250. То есть для ethernet’а кабель такой длины смертелен.
На самом деле это не проблема, и главный разработчик придумал оптимальный вариант — вот так:
![](https://habrastorage.org/storage2/97b/f36/70d/97bf3670d3354eafe8ab4df47db09c49.png)
Как видим — теперь вагоны соединены через один. Здесь же проставлены номера портов на устройствах. И можно заметить, что они очень даже хорошо вписываются в предложенный ранее вариант построения кольца.
Профит? — Как бы не так.
Неожиданно
![](https://habrastorage.org/storage2/da6/cca/caa/da6ccacaaf5196e52524306baa6905b1.png)
Как видим, связь по кольцу нарушилась. Если не совсем понятно почему — поясню. На предыдущей картинке можно полностью пройти по кольцу, если предположить, например, что порт 2 работает только на передачу. То есть можно пройти по цепочке: порт 2 --> порт 1, порт 2 --> порт 1. И так далее. После разворота вагона этот принцип нарушается.
Поэтому от, казалось бы, идеального варианта пришлось отказаться и искать новый.
Ну что ж, приступим к изобретению велосипедов…
Велосипед №1 — Managed Redundant Ring Control Protocol (MRRCP) v1.0
Тут пришло понимание, что процессор грузить всё-равно придётся и одним физическим уровнем с минимальным управлением не отделаешься. Нужно уметь динамически перестраивать кольцо для любой конфигурации соединений.
Как ни крути, пришлось изобретать свой протокол для управления кольцом. Собственно, его
Ещё раз напомню — критично было как можно меньше грузить процессор работой с сетью. Поэтому с точки зрения “правильности” использования Ethernet всё, что дальше написано в этом варианте, является ужасом, летящим на крыльях ночи…
Инициализация:
- Отключаем обучение адресам в свитче
- Локальные адреса для портов 1 и 2 НЕ устанавливаем, они нам не нужны
- Запрещаем приём и передачу на всех портах (статические маршруты могут “перебивать” эти запреты — ими мы и пользуемся)
- Очищаем статические маршруты в нашем свитче.
- Пишем нулевым маршрутом вот такой:
- Всё, что идёт с адресом получателя 44:44:00:00:00:07 (это типа бродкаст адрес для MRRCP) перенаправляется только на процессор.
Смысл инициализации в том, чтобы вообще ничего без нашего разрешения на процессор не попадало и в сеть не уходило.
Непосредственно алгоритм:
Каждое устройство имеет таблицу маршрутов (фактически это массив структур). Каждая такая структура относится к определённому устройству в кольце. Структура примерно такая:
struct
{
// Маршрут к устройству номер ...
unsigned char DestinationDevice;
// В какой из портов нужно слать пакеты, чтобы достучаться до устройства
unsigned char SendToPort;
// Минимальное "расстояние" до устройства - количество промежуточных устройств + 1.
unsigned char MinDistance;
// Сколько раз этот маршрут НЕ обновлялся
unsigned char NotUpdatedTimes;
} Route;
То есть каждое устройство знает, как добраться до любого другого (в какой порт отправлять пакеты и как далеко устройство расположено). Весь алгоритм крутится вокруг этой таблицы маршрутов.
После включения питания вся таблица маршрутов забивается заведомо неверными значениями, чтобы кольцо строилось, так сказать, с ноля.
С периодичностью N миллисекунд каждое устройство отправляет свой MRRCP пакет в порт 1 и порт 2. N можно взять с потолка. Например, 200. Всё зависит от того, насколько критична скорость переконфигурации кольца после обрыва. Нам важнее была надёжность (чтобы точно перестроилось, а не перестроилось как-нибудь, зато быстро).
Пакет отправляется такой:
- Destination MAC — 44:44:00:00:00:07
- Source MAC — 22:22:00:00:00:XX, где XX — номер устройства
- EtherType — 0x7701 (выбрано так, чтобы не пересекалось с общепринятыми)
- Тело пакета:
- YY — количество устройств в цепочке
- XX — номер устройства, которое начало цепочку
- ZZ..ZZ — Цепочка устройств — номера устройств, через которые проходил этот пакет. То есть каждое устройство оставляет свою метку здесь и отправляет пакет дальше (с некоторыми оговорками — см. ниже).
- В последних четырёх байтах Ethernet пакета передаётся аппаратно подсчитанная контрольная сумма (CRC32)
Обработка MRRCP пакета:
- Разнообразные проверки на валидность данных в пакете (проверка номера устройства отправителя, контрольной суммы и пр.)
- Проходим цепочку устройств в цикле, начиная с конца:
- Самое последнее устройство в цепочке — это номер ближайшего к нам соседа, предпоследнее — номер соседа через одно устройство и т.д. Таким образом определяется “расстояние” до устройства из цепочки
- Если расстояние до устройства меньше того, которое у нас хранится в таблице маршрутов, то обновляем маршрут в памяти процессора и также обновляем статический маршрут №X в свитче (где X — это номер устройства из цепочки). Обратите внимание, что обновлять нужно именно если значение меньше. Потому что при чётном количестве устройств в кольце противоположные устройства будут иметь одинаковые расстояния как по одной стороне кольца, так и по другой. Для примера посмотрите на устройства №1 и №8 ниже на рисунке.
- Кроме того, необходимо обносить маршруты, если поле NotUpdatedTimes превышает определённый предел (это таймаут валидности маршрута). То есть если маршрут давно не обновлялся и вдруг пришёл пакет от этого устройства, то он обновится, даже если расстояние больше, чем то, что хранится в таблице маршрутов. Это необходимо для перестроения кольца при обрыве. Поле NotUpdatedTimes наращивается по таймеру и обнуляется, когда мы получаем пакет из устройства, а в таблице маршрутов для него уже записаны корректные значения.
- Если пакет отправляло не наше устройство (и если наше устройство не встречается в цепочке), то дописываем свой номер в конец цепочки и отправляем в порт, “противоположный” тому, откуда был получен пакет MRRCP (например, если из 1-го, то во 2-й и наоборот, то есть мы как бы пропускаем пакет сквозь себя, добавив отметку, что нас пакет прошёл). Проверка на наличие нашего устройства в цепочке нужна для дропа пакетов, которые уже пробежали по кругу.
Вот небольшой пример на рисунке:
![](https://habrastorage.org/storage2/0f1/7d5/bb9/0f17d5bb93ac9459c28c65a6564c7749.png)
Для устройства №8 (оно противоположно первому) маршруты построятся в зависимости от того, по какой стороне кольца быстрее «добежит» самый первый пакет.
Обратите внимание, что на рисунке указаны маршруты, которые хранятся в памяти процессора, а не статические маршруты свитча.
О записи статических маршрутов в свитч:
Маршруты нужно записывать таким образом:
Dest.MAC — 55:55:00:00:00:XX, где (XX — номер устройства из цепочки)
Forwarding Port — номер порта, обратный тому, откуда пришёл пакет + порт 3 (процессор). То есть если пакет пришёл с порта 1, то записать нужно порт 2 и наоборот. Почему именно так — см. пояснение ниже.
В общем работа алгоритма получается такой:
- Изначально кроме MRRCP пакетов никакого обмена нет
- После того, как MRRCP пакеты от каждого устройства по одному разу пробегут по всему кольцу, во всех устройствах появляется общая картина топологии сети (кому в какой порт отправлять)
- Начинается обмен данными между устройствами
Благодаря статическим маршрутам (с портами “наоборот”) мы можем вещать бродкаст, но не будет возможности связи типа “точка-точка”.
Для вещания бродкастом нужно отправлять пакет в оба порта (1 и 2) с Dest.MAC 55:55:00:00:00:XX, где XX — это номер своего (да-да, именно своего) устройства. Из-за переворота номеров портов в статических маршрутах, бродкаст пройдёт по всем устройствам по кольцу и дропнется на устройстве, “с обратной стороны" кольца от отправителя. Дубликатов не будет, потому что они будут дропаться на основании статических маршрутов
Пояснение:
![](https://habrastorage.org/storage2/cec/2c8/149/cec2c81491621d5f98c5ed54261db159.png)
Точка разрыва определяется на тех устройствах, у которых максимальное расстояние до самого отдалённого устройства равно N — 1 (где N — это количество устройств в сети). См. картинку:
![](https://habrastorage.org/storage2/e99/cf0/579/e99cf0579305ffd32e4f3271bd3ea23d.png)
Видно, что у устройств №1 и №6 будут маршруты с расстоянием (6 — 1) = 5, а значит между ними обрыв.
Для кольца с нечётным количеством устройств обрыв считается немного по-другому — там чуть проще.
Альтернативным вариантом для определения обрыва может быть использование прерывания Link Status, как описано выше.
Плюсы:
- Кольцо динамически перестраивается при обрыве и восстановлении сети
Минусы:
- Казалось, что их нет, но я ошибся
Итог:
Всё заработало на отладочном стенде из 3-х устройств, но в изобретении велосипедов меня уже было не остановить…
Велосипед №2 — финальный, MRRCP v2.0
При проверке выяснилось, что устройства с номерами выше седьмого работают в кольце неправильно. Убив день на поиски проблемы оказалось, что я (каюсь и сыплю на голову пепел) не заметил сноску в даташите свитча о том, что статических маршрутов у него всего восемь. То есть алгоритм работает только если устройств в кольце меньше 8. Это был эпик-фейл. Зато появилась веская причина переработать костыль с Dest.MAC для бродкаста из предыдущего велосипеда.
Вот так родился новый и финальный вариант. Он отличается только работой со свитчем. С MRRCP пакетами всё остаётся так же.
Инициализация:
- Устанавливаем локальные MAC адреса для портов 1 и 2 в виде 22:22:00:00:00:XX, где XX — номер нашего устройства
- Очищаем статические маршруты
- Пишем нулевым маршрутом вот такой:
- Всё, что идёт с адресом получателя 44:44:00:00:00:07 (это типа бродкаст адрес MRRCP) перенаправляется только на процессор в порт 3.
- Пишем первым маршрутом вот такой:
- Всё, что идёт с адресом получателя 55:55:00:00:00:01 (это типа бродкаст адрес для данных) перенаправляется на все порты (свитч сам пропустит порт, по которому перенаправляемый пакет было получен).
- Отключаем обучение, а также приём и передачу на всех портах (чтобы работали только статические маршруты)
- Включаем Source MAC фильтр на портах 1 и 2 (чтобы устройство, которое вещает бродкаст, дропало свои пакеты после пробегания ими полного круга)
Пакеты MRRCP отправляются так же, как и в предыдущем варианте. А в обработчике просто необходимо убрать запись статических маршрутов в свитч.
Теперь бродкаст делается так:
- Вещающее устройство отправляет пакет в оба порта с Dest.MAC 55:55:00:00:00:01 и Src.MAC 22:22:00:00:00:XX, где XX — номер своего устройства
- Пакеты проходят по кругу по кольцу и дропаются отправителем
![](https://habrastorage.org/storage2/aeb/fc7/8f1/aebfc78f10f16a682a53932f8f9d6120.png)
Определение разрыва кольца — аналогично, как и в предыдущем варианте.
Если нужна связь “точка-точка”, то её можно реализовать, добавив в инициализацию настройку статического перенаправления пакетов с Dest.MAC равным локальному MAC’у в порт 3. А также настроить перенаправление пакетов с неизвестными адресами в порты 1 и 2.
Плюсы:
- Использует всего два статических маршрута свитча и при этом количество устройств не ограничено 7-ю.
- Можно организовать связь типа “точка-точка”.
- В кольце автоматически меняются маршруты после изменения конфигурации сети.
- Нет постоянного обмена со свитчем по интерфейсу управления (I2C). Оказалось важным, поскольку на этом же интерфейсе “висит” ещё три устройства. Свитч настраивается только один раз после включения питания.
Минусы:
- Дубликаты пакетов. Для их отсеивания нам как раз и нужна таблица маршрутов, по которой мы можем отфильтровать пакеты, пришедшие “не из того порта” прямо в прерывании Ethernet не пропуская их дальше.
Время перестройки кольца точно не замерялось. С очень сильно завышенными значениями таймаута валидности маршрутов и периодом отправки MRRCP пакетов, кольцо пересобирается за 1-2 секунды. В нашем случае этого оказалось достаточно.
Итог можно подвести так — покатавшись на разных велосипедах, мы вернулись к варианту №2 (от разработчиков свитча) с доработками в виде MRRCP.
Конструктивная критика категорически приветствуется.
Надеюсь, что кому-то эта статья окажется полезной и некоторые грабли получится обойти стороной. Удачи!
UPD2: MAC адреса для устройств и для MRRCP пакетов были выбраны случайным образом. Как заметил dukelion, если всё делать по стандартам, то в MAC адресах есть специальные биты для определения групп и функциональных MAC адресов. Ссылка на туториал IEEE.
UPD: Спасибо за инвайт, перенёс статью в хаб «Программинг микроконтроллеров»
p.s. Исходники дать не могу. Я надеюсь, что Вы понимаете почему.
p.p.s Прошу прощения, если объяснения местами показались слишком затянутыми и термины используются не совсем правильно — я старался объяснить максимально доступным языком, чтобы было понятнее.
p.p.p.s. Очень интересно узнать, как бы Вы решали вот такую задачу с учётом вышеизложенного? Спасибо.