Как устроен L3-коммутатор: разбираемся с железом и настройками конфигурации на примере проблемы с котиками

от автора

L3-коммутаторы именитых брендов, как правило, хорошо отлажены, а редкие сложности с ними решаются готовой прошивкой с патчем. Но если производитель еще растет на рынке или речь о white box-коммутаторе, то сетевой инженер или разработчик фактически остается с проблемами один на один. 

Меня зовут Антон Гузарев, я тимлид команды, которая разрабатывает ПО для управления сетевыми устройствами в YADRO. Наша команда вдыхает жизнь в железо — создает софт для коммутаторов KORNFELD, которые встают в серверные стойки рядом с СХД.

Я хочу рассказать, какие типичные проблемы мы встречаем в L3-коммутаторах, с чем они связаны и как их отлаживать. Мой рассказ будет подробным, поэтому для удобства я поделю его на две части. В этом тексте мы разберем, как устроен L3-коммутатор на уровне железа, и посмотрим на уровни управления конфигурацией — в описании опираюсь на открытый проект SONiC. А в продолжении найдем и решим проблему в ПО, которая приводит к проблеме загрузки фотографий котиков на сайт. Подписывайтесь и сохраняйте текст, чтобы не пропустить вторую часть.

Небольшой дисклеймер: Обе статьи будут наиболее полезны, если вы хоть немного знакомы с сетями и коммутаторами. Например, знаете о модели OSI, встречались с аббревиатурами ARP или FIB, представляете, что такое интерфейс в сетевых устройствах. Я постараюсь объяснять термины, но все же статьи больше рассчитаны на подготовленного читателя.   

Проблема с котиками

Однажды Пётр загружал на сайт фотографии котиков, но на экране появилась ошибка: 

Пётр не знает, почему так произошло, и обращается к нам — администраторам. Мы понимаем, что браузер не может проверить HTTPS-сертификат, потому что потерян доступ к NTP-серверу. 

На одном конце нашей сети — компьютер пользователя, на другом — сервер, к которому нет доступа. Между ними — L3-коммутатор и маршрутизатор.  

Сеть, в которой нам предстоит найти проблему, включает компьютер Петра pc1, коммутатор switch1, маршрутизатор router1, подсеть 10.2.2.0/24 и сервер 10.2.2.1

Сеть, в которой нам предстоит найти проблему, включает компьютер Петра pc1, коммутатор switch1, маршрутизатор router1, подсеть 10.2.2.0/24 и сервер 10.2.2.1

Чтобы понять, почему трафик перестал ходить и фотографии котиков не грузятся, можно последовательно проверить доступность устройств. Для начала проверяем доступ с коммутатора 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-записей почему-то нет. Но это не сама проблема, а только ее следствие. Проблему еще предстоит найти. 

На текущий момент коротким движением по сети мы выяснили два факта:

  1. Конфигурация коммутатора достаточна для движения трафика.

  2. Проблема скрывается в коммутаторе. 

Значит, нужно лезть внутрь коммутатора и последовательно проходиться по его подсистемам, как по кабелю. Базово для корректной работы уровня 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-порт:

Драйверы, системные компоненты и т.д. Загружаются в память, выделенную для операционной системы (и далее исполняются в ней). Программы пользователя помещаются в другой сектор памяти и не могут получить доступ к сектору операционной системы. Удобно классифицировать код на работающий в kernal space и user space.

Драйверы, системные компоненты и т.д. Загружаются в память, выделенную для операционной системы (и далее исполняются в ней). Программы пользователя помещаются в другой сектор памяти и не могут получить доступ к сектору операционной системы. Удобно классифицировать код на работающий в kernal space и user space.

Драйвер создает интерфейсы (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). 

Для простоты восприятия все кодовые блоки я буду иллюстрировать схемами:

Графическое представление введенных SAI-команд

Графическое представление введенных SAI-команд

В коммутаторе есть блок 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. 

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

Теперь мы с вами знаем чуть больше про устройство коммутатора и распространенные проблемы. В продолжении статьи, которая выйдет через неделю, мы вернемся к фотографиям котиков нашего пользователя, которые по-прежнему не грузятся, и попробуем исправить ситуацию. Подписывайтесь или сохраняйте текст в избранном — так вы не пропустите вторую часть.


ссылка на оригинал статьи https://habr.com/ru/articles/865186/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *