L3-коммутаторы именитых брендов, как правило, хорошо отлажены, а редкие сложности с ними решаются готовой прошивкой с патчем. Но если производитель еще растет на рынке или речь о white box-коммутаторе, то сетевой инженер или разработчик фактически остается с проблемами один на один.
Меня зовут Антон Гузарев, я тимлид команды, которая разрабатывает ПО для управления сетевыми устройствами в YADRO. Наша команда вдыхает жизнь в железо — создает софт для коммутаторов KORNFELD, которые встают в серверные стойки рядом с СХД.
Я хочу рассказать, какие типичные проблемы мы встречаем в L3-коммутаторах, с чем они связаны и как их отлаживать. Мой рассказ будет подробным, поэтому для удобства я поделю его на две части. В этом тексте мы разберем, как устроен L3-коммутатор на уровне железа, и посмотрим на уровни управления конфигурацией — в описании опираюсь на открытый проект SONiC. А в продолжении найдем и решим проблему в ПО, которая приводит к проблеме загрузки фотографий котиков на сайт. Подписывайтесь и сохраняйте текст, чтобы не пропустить вторую часть.
Небольшой дисклеймер: Обе статьи будут наиболее полезны, если вы хоть немного знакомы с сетями и коммутаторами. Например, знаете о модели OSI, встречались с аббревиатурами ARP или FIB, представляете, что такое интерфейс в сетевых устройствах. Я постараюсь объяснять термины, но все же статьи больше рассчитаны на подготовленного читателя.
Проблема с котиками
Однажды Пётр загружал на сайт фотографии котиков, но на экране появилась ошибка:
Пётр не знает, почему так произошло, и обращается к нам — администраторам. Мы понимаем, что браузер не может проверить HTTPS-сертификат, потому что потерян доступ к NTP-серверу.
На одном конце нашей сети — компьютер пользователя, на другом — сервер, к которому нет доступа. Между ними — L3-коммутатор и маршрутизатор.
Чтобы понять, почему трафик перестал ходить и фотографии котиков не грузятся, можно последовательно проверить доступность устройств. Для начала проверяем доступ с коммутатора switch1 до сервера в сети 10.2.2.0/24:
switch1# ping 10.2.2.1
PING 10.2.2.1 (10.2.2.1) 56(84) bytes of data.
From 10.1.1.1 icmp_seq=1 Destination Host Unreachable
From 10.1.1.1 icmp_seq=2 Destination Host Unreachable
From 10.1.1.1 icmp_seq=3 Destination Host Unreachable
Доступа нет. Попробуем теперь пройти до сервера от маршрутизатора router1:
router1# ping 10.2.2.1
PING 10.2.2.1 (10.2.2.1) 56(84) bytes of data.
64 bytes from 10.2.2.1: icmp_seq=1 ttl=102 time=2.32 ms
64 bytes from 10.2.2.1: icmp_seq=2 ttl=102 time=2.13 ms
64 bytes from 10.2.2.1: icmp_seq=3 ttl=102 time=2.39 ms
С маршрутизатора доступ есть. Это значит, что проблема находится до маршрутизатора. Проверим линк между коммутатором и маршрутизатором:
switch1# ping 10.1.1.2
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
From 10.1.1.1 icmp_seq=1 Destination Host Unreachable
From 10.1.1.1 icmp_seq=2 Destination Host Unreachable
From 10.1.1.1 icmp_seq=3 Destination Host Unreachable
Связи нет. Первое предположение — у нас проблема с самим кабелем.
switch1# show ip interfaces
Но линк в состоянии UP. Значит, проблема не с физикой:
switch1# show ip interfaces
----------------------------------------------------------------------
Interface IP address/mask VRF Admin/Oper Flags
----------------------------------------------------------------------
Ethernet1 10.1.1.1/24 up/up
Созданы ли маршруты?
switch1# show ip route
Маршрут во «вторую» подсеть есть, здесь все нормально.
switch1# show ip route
Destination Gateway Interface Dist/Metric Uptime
--------------------------------------------------------------------------------
S>* 10.2.2.0/24 via 10.1.1.2 Ethernet1 1/0 05:25:31
C>* 10.1.1.0/24 Direct Ethernet1 0/0 05:25:46
Тогда проверим ARP-записи.
switch1# show ip arp
------------------------------------------------------------------------
Address Hardware address Interface Egress Interface Type
---------- ------------------ ----------- ------------------ -------
ARP-записей почему-то нет. Но это не сама проблема, а только ее следствие. Проблему еще предстоит найти.
На текущий момент коротким движением по сети мы выяснили два факта:
Конфигурация коммутатора достаточна для движения трафика.
Проблема скрывается в коммутаторе.
Значит, нужно лезть внутрь коммутатора и последовательно проходиться по его подсистемам, как по кабелю. Базово для корректной работы уровня L3 необходимы:
интерфейс,
настроенные политики Control Plane,
доступный next-hop,
маршрут.
Этим порядком мы будем руководствоваться в поисках проблемы, которая мешает Петру загрузить фотографии котиков, — в продолжении статьи, которое выйдет через неделю. Но сначала разберемся в устройстве коммутатора и поймем, как операционная система управляет коммутатором. Ведь чем лучше специалист знает сеть и сетевые устройства, тем быстрее он сможет решить любую проблему.
К слову о специалистах. Мы ищем профессионалов в нашу команду разработки коммутатора для дата-центров KORNFELD. Возможно, вас заинтересуют эти вакансии:
→ Сетевой инженер по автоматизации (Python)
→ Ведущий инженер группы аналитики и комплексного тестирования
Как физически устроен L3-коммутатор
Внутри коммутатора два физических устройства: CPU и ASIC. CPU принято называть Control Plane, там крутится сетевая операционная система, как правило, Linux. ASIC также называют Data Plane.
CPU (Control Plane) отвечает за:
сложную логику,
обработку служебного трафика,
управление ASIC.
ASIC (Data Plane) ответственен за:
простую логику на основе таблиц,
быструю обработку большого числа пакетов,
разбор заголовков, выбор выходного порта и выходного набора заголовков,
буферизацию, очереди, политики.
Администратор, который сидит в CLI, находится на CPU, а интерфейс, который он конфигурирует, — на соседнем устройстве ASIC.
Для простоты понимания можно думать о физическом устройстве коммутатора как о двух соседних городах. Первый город CPU — административный центр, он управляет своим соседом. Второй город ASIC — промышленный центр с крупными транспортными развязками и светофорами.
Физические порты находятся в ASIC, но управлять ими надо на CPU. На CPU крутятся сетевые демоны и разные алгоритмы, которые обрабатывают сетевой трафик. Его можно разделить на два вида:
Пользовательский. Например, фотографии котиков, которые при наличии всех маршрутов проходят через ASIC транзитом.
Служебный. ARP или протоколы маршрутизации, которые как раз должны обрабатываться демонами или алгоритмами на CPU.
Но откуда порты берутся в CPU? Наши города-устройства соединяет магистраль — скоростная шина. На CPU есть драйвер, который слушает шину и разбирает, какие пакеты и на какой интерфейс ему пришли. Этот же драйвер отвечает за формирование интерфейсов на стороне CPU. Со стороны ASIC задача сводится к направлению всех пакетов, предназначенных Control Plane, на CPU-порт. При этом в передаваемые пакеты добавляется мета-заголовок с номером порта, на котором был принят данный пакет, и другой информацией.
Давайте посмотрим на схеме, как это выглядит. У ASIC есть несколько «входящих» портов, на которые приходят пакеты и один CPU-порт:
Драйвер создает интерфейсы (Netlink-интерфейсы) в системе и направляет на них пакеты в зависимости от мета-информации. Сетевые демоны (процессы) работают с этими интерфейсами стандартным образом, как если бы это были обычные интерфейсы сетевых адаптеров.
В общем случае администратор, вводя команды в CLI, управляет: ASIC, сетевой подсистемой Linux, сетевым демонами.
Какие библиотеки управляют железом
Для конфигурации Linux и управления сетевой подсистемой ядра используется набор библиотек libnl. Для управления ASIC используются две библиотеки — SDK и SAI. У каждого производителя чипов свой SDK, и они сильно отличаются между собой. Чтобы разработчикам сетевых устройств не приходилось менять всю систему в зависимости от ASIC, был придуман интерфейс SAI. SAI иногда называют API для SDK — он обеспечивает совместимость с разными ASIC. Когда мы вводим команду в CLI, то применяем ее к двум уровням: сначала к Linux, потом к ASIC через интерфейс SAI.
В качестве аналогии приведу замену колес на автомобиле. Можно поставить колеса другого профиля и диаметра — получить другие характеристики комфорта и сцепления с дорогой. При этом другие изменения в автомобиле выполнять не придется.
В итоге у нас есть два физических устройства, но три уровня управления конфигурацией:
уровень Linux, который управляется libnl,
SAI,
SDK.
Вокруг них строится операционная система. Вот как они выглядят схематически:
Постарайтесь запомнить эту картинку. Во второй части статьи я буду приводить примеры конфигурации или запросов и кратко отмечать, на каком уровне мы находимся: CLI, Linux, SAI или SDK.
Как выглядит SAI
SAI — это абстрактный язык описания команд, которые мы отправляем в ASIC. Абстрактный язык подразумевает, что мы говорим о функционале как о иерархии объектов или как о квадратах и связях между ними. У каждого квадрата есть цифровой идентификатор — Object ID, или сокращенно oid.
Для примера создадим Virtual router, L3-интерфейс внутри него и маршрут. Дальше я объясню, что здесь происходит.
Создаем L3-интерфейс внутри Virtual router:
create : SAI_OBJECT_TYPE_ROUTER_INTERFACE:oid:0x60000000009da
SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID=oid:0x300000000003a
SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS=98:19:2C:9B:58:49
SAI_ROUTER_INTERFACE_ATTR_TYPE=SAI_ROUTER_INTERFACE_TYPE_PORT
SAI_ROUTER_INTERFACE_ATTR_PORT_ID=oid:0x1000000000004
...
Создаем маршрут:
create : SAI_OBJECT_TYPE_ROUTE_ENTRY: {"dest":"10.1.1.0/24","vr":"oid:0x300000000003a"}
SAI_ROUTE_ENTRY_ATTR_PACKET_ACTION=SAI_PACKET_ACTION_FORWARD
SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID=oid:0x60000000009da
Первой командой мы создали SAI-объект ROUTER_INTERFACE с атрибутами:
принадлежность к инстансу маршрутизатора (VIRTUAL_ROUTER_ID),
MAC-адрес интерфейса,
базовый тип интерфейса (может строиться на основе порта, VLAN, транковой группы и некоторых других объектов),
номер порта.
Второй командой создали SAI-объект записи в таблице маршрутизации. Ключевая информация записи — префикс сети и инстанс маршрутизатора. Атрибуты — идентификатор next-hop и действие. В общем случае действие «forward» — это передача пакета, но может быть и отброс пакета (тогда получится запись типа blackhole).
Для простоты восприятия все кодовые блоки я буду иллюстрировать схемами:
В коммутаторе есть блок Virtual router. Внутри него — абстрактный интерфейс (0x...03a на схеме), который обеспечивает взаимодействие по уровню L3. Также в коммутаторе есть физические интерфейсы (Port 1). Virtual router и физический интерфейс связывает маршрут (стрелка): он говорит, что для достижения конкретной сети нужно передать пакет через RIF (ROUTER INTERFACE), который связан с физическим портом.
Как устроено управление операционной системой
Управление операционной системой можно представить так:
В центре — база данных. Она хранит:
Конфигурацию (configuration), настроенную пользователем. Именно ее нужно поменять, чтобы внести любые изменения.
Состояние (state). Это вся актуальная информация по текущему состоянию коммутатора. Состояние включает в себя как успешно примененную конфигурацию, так и оперативное состояние (например, состояние портов, которое меняется при подключении-отключении кабеля)
Давайте посмотрим, как реализуются в операционной системе коммутатора разные действия администратора:
чтение конфигурации,
сетевое событие,
настройка конфигурации.
Чтение конфигурации. Когда мы выполняем show-команду, то ходим в state в базе данных и получаем ответ. Ни Linux, ни ASIC таким запросом мы не затрагиваем.
Сетевое событие. Нам может прийти сетевой пакет или упасть линк. Эти события «поднимаются» с уровня ASIC в Linux. А интерфейсы, с которыми мы работаем, могут опуститься в DOWN. Такие изменения тоже отслеживает операционная система и применяет управляющее воздействие. При переходе порта из состояния UP в состояние DOWN прежде всего выполняется синхронизация (интерфейсы ASIC и интерфейсы Linux должны быть в одинаковом состоянии), далее маршруты могут становиться неактивными, отправляться пакеты о недоступности подсетей и т.д.
Настройка конфигурации. Когда нужно настроить функциональность, мы вводим команды в CLI, и изменения конфигурации записываются в базу данных. Дальше сетевые подсистемы и их демоны получают информацию из базы данных и разбивают ее на две части.
Сначала конфигурация применяется к Linux. Если все хорошо, она применяется к ASIC. Если и здесь все в порядке, то изменения записываются в state базы данных и их можно увидеть в show-команде.
Хочу предупредить, что производители оборудования отображают успешность применения новой конфигурации по-разному. Одни записывают изменение конфигурации в базу данных и сразу выдают запрос на следующую команду, возвращая shell пользователю. Другие вендоры отдают shell только после завершения команды, а у третьих реализованы транзакции.
Даже если команда принята, нет сообщения об ошибке и пользователю возвращается shell, это еще не гарантирует, что конфигурация уже применена. Команда может применяться несколько секунд. Если моментально ввести show-команду, то можно не увидеть изменений — это нормально.
Ошибки при настройке конфигурации
В нашей команде мы выделяем три основных «категории» ошибок, которые возникают при настройке конфигурации. Конечно, возможные проблемы ими не ограничиваются, но они наиболее распространены.
Первый класс проблем. Возможна ситуация, когда команда применилась в Linux, а в ASIC — нет. Искать подобные ошибки нужно в логах: смотреть на слова “error” или “fail”.
Чтобы таких проблем не возникало, мы как разработчики софта для конкретного оборудования читаем конфигурацию перед тем, как вводить команду, и:
проводим валидацию команды,
исключаем варианты конфигурации, когда команда заведомо не выполнится.
У этих действий есть и обратный эффект: мы увеличиваем время применения команды и вызываем описанную выше в статье ситуацию, когда моментально выполненная show-команда может не показать результатов.
Второй класс проблем — разбивка на команды или частичное применение. Например, администратор вводит одну команду, а она разбивается на две. Или администратор вводит две команды, а применяется только одна. В результате все работает не так, как ожидалось.
Третья типичная категория проблем — многоэтажная конфигурация. Например, мы создаем интерфейс уровня L3, на него вешаем IP-адрес, потом вешаем еще один адрес, следом на этом интерфейсе поднимаем VRRP, и напоследок нам хочется переместить этот интерфейс в другую VRF-систему:
switch1# configure terminal
# Создаем L3-интерфейс
switch1(config)# interface Ethernet 1
switch1(conf-if-Ethernet1)# ip address 10.1.1.1/24
# Добавляем дополнительный IP-адрес
switch1(conf-if-Ethernet1)# ip address 10.10.10.1/24 secondary
# Настраиваем VRRP на интерфейсе
switch1(conf-if-Ethernet1)# vrrp 4 address-family ipv4
switch1(conf-if-Ethernet1-vrrp-ipv4-4)# vip 10.1.1.100
switch1(conf-if-Ethernet1-vrrp-ipv4-4)# exit
switch1(conf-if-Ethernet1)# exit
# Создаем отдельную VRF
switch1(config)# ip vrf Vrf1
# Перемещаем L3-интерфейса в другую VRF
switch1(config)# interface Ethernet 1
switch1(conf-if-Ethernet1)# ip vrf forwarding Vrf1
%Error: IP address configuration exists for Ethernet1
По-хорошему, интерфейс командной строки должен запретить такие действия и выдать ошибку. Такая многоэтажная конфигурация чревата проблемами, например:
SAI или SDK может требовать удаления и пересоздания интерфейса в другом VRF, тогда интерфейс может остаться в старом VRF.
Могут быть пересечения подсетей в VRF, требуется каскадная проверка конфигураций, и часто комбинаций много.
Могут быть ограничения на количество интерфейсов в VRF.
Если такие конфигурации должным образом не валидируются, они могут привести к потере трафика. Но это только иллюстрация возможным проблем.
Теперь мы с вами знаем чуть больше про устройство коммутатора и распространенные проблемы. В продолжении статьи, которая выйдет через неделю, мы вернемся к фотографиям котиков нашего пользователя, которые по-прежнему не грузятся, и попробуем исправить ситуацию. Подписывайтесь или сохраняйте текст в избранном — так вы не пропустите вторую часть.
Комментарии (9)
Akina
12.12.2024 12:15На одном конце нашей сети — компьютер пользователя, на другом — сервер, к которому нет доступа. Между ними — L3-коммутатор и маршрутизатор.
Вот не понял я этой схемы. Клиентский компьютер, как и switch1, имеет адрес из подсети 10.1.1.0/24 или нет? switch1 в общении клиентского компьютера с сервером времени 10.2.2.1 работает как коммутатор (только L2) или как маршрутизатор (ещё и L3)? Судя по дальнейшим объяснениям - первое, но тогда неясно, почему проверки не начались с самогО клиентского компьютера. Сразу бы обнаружилось, что связи со switch1 нет.
С маршрутизатора доступ есть. Это значит, что проблема находится до маршрутизатора.
Да с чего бы? Зафильтруйте на роутере на выходе трафик на 10.1.1.1 - получите ровно эту картину.
yadro_team
12.12.2024 12:15Вот не понял я этой схемы.
Из схемы убраны некоторые детали, так как автор не хотел ее перегружать. В листингах аналогично убраны детали, не относящиеся к теме.
Схема IP-адресов может быть такой:
PC1:
IP: 192.168.1.100/24
GW: 192.168.1.1
Switch:
Ethernet1 : 10.1.1.1/24
Ethernet2 : 192.168.1.1/24
Router:
Порт1 : 10.1.1.2/24
Порт2 : 10.2.2.2/24
Сервер:
IP: 10.2.2.1/24
GW: 10.2.2.2
Да с чего бы? Зафильтруйте на роутере на выходе трафик на 10.1.1.1 - получите ровно эту картину.
Хотя статья про коммутатор, замечание про фильтрацию на роутере верное. Действительно, проблема может быть по оба конца кабеля. А кроме фильтрации 10.1.1.0/24, можно накинуть и других вариантов — например, заблекхолить соседа 10.1.1.1 или прописать 10.1.1.1/32 доступным через совершенно другой хост.
Akina
12.12.2024 12:15Switch:
Ethernet1 : 10.1.1.1/24
Ethernet2 : 192.168.1.1/24
Тогда это не свитч/коммутатор, а маршрутизатор. И неважно, что маршрутизатор является составной частью L3-коммутатора. При поиске проблемы надо работать с функциональной схемой, а не тупо считать корпуса. Только запутываете... Если читатель в этой области новик, то в лучшем случае он ничего не поймёт. А в худшем - поймёт неправильно.
Это сродни откуда-то появившемуся и невероятно популярному заблуждению, что в каждом вилане должна ходить только одна подсеть, причём в каждом своя. Уже и не скажешь, откуда это вылезло - но адепты аж в драку кидаются, словно фанатики религиозные, и ничем не свернуть.
DamDamich
12.12.2024 12:15Третья типичная категория проблем — многоэтажная конфигурация. Например, мы создаем интерфейс уровня L3, на него вешаем IP-адрес, потом вешаем еще один адрес, следом на этом интерфейсе поднимаем VRRP, и напоследок нам хочется переместить этот интерфейс в другую VRF-систему:
Спасибо за статью! Вопрос: почему изначально не рассматривался YAML-формат для конфигурации роутера? Это ведь актуальный (и, на самом деле, весьма востребованный) тренд среди большинства вендоров на рынке. Помимо прочих, очевидных преимуществ YAML (структурированность в формате ключ-значение), он решает описанные в статье сложности с выстраиванием иерархии конфигурации, позволяя задать все параметры в более логичной форме.
yadro_team
12.12.2024 12:15При управлении через YAML часто получаются длинные строки, например в openconfig, поэтому посчитали этот формат менее подходящим для статьи. YAML очень хорошо подходит для систем автоматизации и действительно распространен.
Описанная проблема с VRRP может возникнуть при любом способе задания конфигурации. Даже после "commit check" можно получить проблему, если в системе валидации присутствует ошибка для какой-то комбинации настроек.
Neruba
12.12.2024 12:15И я так понял, для тех кому лень читать этот же автор рассказывает все тут https://youtu.be/mXpKVs2hrUk?si=i120CV_fpKjBuarr
Samurai26
Статья отличная, но я не до конца понял на платформе какого роутера всё происходит? вашей линейки, если да, то понимаю, что у него нет веб интерфейса?
Ни разу не сталкивался, чтобы на Cisco или Mikrotik слетал NTP сервер неожиданно и при этом не сгорал роутер. Почему на вашем роутере сразу слетело всё?
Ещё такой вопрос возможно не до конца понял, по схеме, зачем в этой связки и коммутатор и маршрутизатор? Если данный маршрутизатор умеет L3, то он умеет общаться IP пакетами без посредников вроде.
yadro_team
В статье описаны принципы, которые можно встретить в white box-решениях, в частности SONiC. Антон поделился своим опытом в надежде, что он будет кому-то полезен. Коммутатор KORNFELD в этой статье не рассматривается, но здорово, если вам интересно узнать о нем больше. Можем подумать об отдельной статье.
Кейс с клиентом, который не может достучаться до сервера, выбран как вариант, позволяющий погрузить читателя в более-менее конкретную проблему, но оставить внешние детали и погрузиться в мир внутреннего устройства коммутатора. История со сгоревшим коммутатором не представляет такого практического интереса, как ситуация, когда коммутатор работает, но немного не так.