Язык сетевого программирования P4. Часть 1: обзор возможностей и настройка SONiC-P4

Язык сетевого программирования P4. Часть 1: обзор возможностей и настройка SONiC-P4

 

Эта первая часть обзорной статьи, в которой мы разбираемся с молодым языком программирования P4: что это такое, для чего он нужен и чем лучше прочих систем обработки пакетов. Конечно, будет и практика: примеры программирования и обзор железа с поддержкой P4. А на десерт — пошаговая настройка виртуального коммутатора Sonic-P4. Поехали!

 

Начнем с теории. Язык P4 или Programming Protocol-Independent Packet Processors — общедоступный сетевой предметно-ориентированный язык для описания плоскости данных в сети.

Изначально P4 разрабатывали для программирования плоскости пересылки сетевых коммутаторов, но по мере развития этого языка охватили и другие сетевые элементы: роутеры, программные и аппаратные коммутаторы, сетевые интерфейсные карты и т.п. 

Язык P4 — протокольно-независимый: программист сам описывает заголовки с именами полей и выбирает любой нужный протокол. 

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

Программы P4 могут также частично определять интерфейс связи между плоскостью управления и плоскостью данных, но они не могут описать функции уровня управления устройства. Покажем это на простом примере:

стандартный и программируемый на p4 коммутатор

Коммутаторы: стандартный и программируемый на P4

 

На схеме показана разница между стандартным и программируемым коммутатором на P4. В стандартном устройстве производитель определяет функциональность плоскости данных. Плоскость управления контролирует плоскость данных, а именно:

  • управляет записями в таблицах;
  • настраивает специализированные объекты, например, регистры;
  • обрабатывает управляющие пакеты.

Теперь взглянем на два основных отличия коммутатора P4:

  1. Функциональные возможности плоскости данных не задаются первоначально, т.е. программист может их описать. Настройка плоскости данных в соответствии с кодом происходит при инициализации устройства. Отметили это красной стрелкой на схеме.
  2. Взаимодействие плоскости управления и плоскости данных происходит по тем же каналам, что и в стандартном коммутаторе, но при этом набор таблиц и других объектов в плоскости данных уже не является фиксированным и определяется программой P4. Компилятор P4 генерирует API, который использует плоскость управления для связи с плоскостью данных.

 

 

Абстракции языка P4

Плавно переходим к абстракциям языка P4. Кратко опишем сущность каждой:

  • Заголовки используют для описания формата или набора полей и их размеров — для каждого заголовка в пакете.
  • Парсеры или синтаксические анализаторы описывают разрешенные последовательности заголовков в получаемых пакетах, правила идентификации заголовков и поля для извлечения из пакетов. По сути, основная задача синтаксических анализаторов — правильно идентифицировать и разобрать заголовок. 
  • Таблицы P4 объединяют ключи, действия и связи между ними. Они обобщают традиционные таблицы коммутации и помогают реализовать таблицы маршрутизации, списки управления доступом и создание сложных пользовательских таблиц. Единственное, что ограничивает количество таблиц — потребности программиста. Вхождения в таблицы происходят последовательно, если не создана абстрактная таблица, которая состоит из нескольких пользовательских.
  • Действия — фрагменты кода, описывающие правила обработки полей заголовка для пакета и метаданных.
  • Модули Match-Action — составная часть таблиц для выполнения следующей последовательности:
  1. Создание ключа поиска из полей пакета или вычисленных метаданных.
  2. Выполнение поиска в таблице для сопоставления созданного ключа и нужного действия.
  3. Выполнение найденного действия.
  • Поток управления — императивная программа, которая описывает процесс обработки пакетов на устройстве, включая последовательность вызовов модулей Match-Action.
  • Внешние объекты — архитектурно-зависимые конструкции, которыми управляют программы P4 при помощи четко определенных API. Важно: внутреннее поведение внешних объектов скорректировать невозможно. Примеры таких объектов: контрольная сумма, счетчики, регистры и т.д.
  • Пользовательские метаданные — структуры данных, которые связаны с каждым пакетом. Их определяет пользователь.
  • Внутренние метаданные — структуры данных, которые связаны с каждым пакетом. Их определяет архитектура.

 

 

Пример программирования устройства на P4

Производители устройств предоставляют аппаратную или программную среду реализации, архитектуру и компилятор P4. 

Пользователь определяет программу на P4 для конкретной архитектуры. Компилятор генерирует конфигурацию плоскости данных, которая реализует логику пересылки и API для управления состоянием объектов плоскости данных из плоскости управления.

пример программирования p4

На схеме — пример программирования устройства на P4

 

Предметно-ориентированный язык P4 реализуется на разных устройствах, включая программируемые сетевые интерфейсные карты, FPGA, программные коммутаторы и аппаратные ASIC. И разработчики программ на P4 ограничены конструкциями, которые можно эффективно реализовать на всех этих платформах. 

Учитывая фиксированную стоимость операций поиска в таблице и взаимодействий с внешними объектами, все программы P4, т.е. и парсеры, и элементы управления, выполняют константное количество операций для каждого байта входящего пакета. Парсеры могут содержать циклы, но сам пакет обеспечивает ограничение на общее выполнение парсера. 

Таким образом, вычислительная сложность программы P4 линейна по общему размеру всех заголовков и никогда не зависит от размера состояния, которое накопилось при обработке данных, например, количества потоков или общего количества обработанных пакетов. Казалось бы, быстрая обработка пакетов при этом гарантирована, но этого недостаточно, есть нюансы.

Давайте посмотрим на P4 на живых примерах. Ниже – заголовок, парсер и таблица для модели Very Simple Switch.

#include <core.p4>

#include <v1model.p4>

 

const bit<16> TYPE_IPV4 = 0x800;

 

/*******************************************

********** H E A D E R S  ******************

*******************************************/

typedef bit<9egressSpec_t;

typedef bit<48> macAddr_t;

typedef bit<32> ip4Addr_t;

 

header ethernet_t {

   macAddr_t dstAddr;

   macAddr_t srcAddr;

   bit<16>   etherType;

}

header ipv4_t {

   bit<4>    version;

   bit<4>    ihl;

   bit<8>    diffserv;

   bit<16>   totalLen;

   bit<16>   identification;

   bit<3>    flags;

   bit<13>   fragOffset;

   bit<8>    ttl;

   bit<8>    protocol;

   bit<16>   hdrChecksum;

   ip4Addr_t srcAddr;

   ip4Addr_t dstAddr;

}


 

/****************************************

********** P A R S E R ******************

*****************************************/

 

parser MyParser(packet_in packet,

               out headers hdr,

               inout metadata meta,

               inout standard_metadata_t standard_metadata) {

   state start {

       transition parse_ethernet;

   }

   state parse_ethernet {

       packet.extract(hdr.ethernet);

       transition select(hdr.ethernet.etherType) {

           TYPE_IPV4: parse_ipv4;

           default: accept;

       }

   }

   state parse_ipv4 {

       packet.extract(hdr.ipv4);

       transition accept;

   }

}

 

   action drop() {

       mark_to_drop(standard_metadata);

   }

  

   action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {

       standard_metadata.egress_spec = port;

       hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;

       hdr.ethernet.dstAddr = dstAddr;

       hdr.ipv4.ttl = hdr.ipv4.ttl - 1;

   }

  

   table ipv4_lpm {

       key = {

           hdr.ipv4.dstAddr: lpm;

       }

       actions = {

           ipv4_forward;

           drop;

           NoAction;

       }

       size = 1024;

       default_action = drop();

   }

  

   apply {

 

       if (hdr.ipv4.isValid()) {

           ipv4_lpm.apply();

       }

   }

 

Полюбовавшись на заголовки и парсеры, попробуем понять, а чем же P4 так хорош и хорош ли?

 

 

Преимущества языка P4

P4 превосходит современные системы обработки пакетов в следующих моментах:

  1. Гибкость: P4 помогает представить политики пересылки пакетов в виде программ, в отличие от традиционных коммутаторов, которые дают пользователям механизмы пересылки с фиксированными функциями. 
  2. Выразительность: с P4 легко представлять сложные аппаратно независимые алгоритмы обработки пакетов. При этом использовать нужно исключительно операции общего назначения и поиск по таблицам. Такие программы переносимы между целевыми аппаратными средствами, которые реализуют одни и те же архитектуры, если нам хватает ресурсов. 
  3. Управление ресурсами: программы P4 абстрактно описывают ресурсы хранения, например, адрес источника IPv4; компиляторы сопоставляют поля с доступными аппаратными ресурсами, которые определяет пользователь, и управляют низкоуровневыми элементами.
  4. Преимущества при разработке ПО: проверка типов, скрытие информации и повторное использование кода (software reuse).
  5. Библиотеки: библиотеки компонентов производителей могут использоваться для обертывания аппаратно-зависимых функций в переносимые высокоуровневые конструкции P4. 
  6. Разделение аппаратного и программного обеспечения: производители устройств могут использовать абстрактные архитектуры, чтобы четко отделить низкоуровневые архитектурные элементы от высокоуровневой обработки. 
  7. Отладка: производители предоставляют программные модели архитектуры для простоты разработки и отладки программ P4. 

Гибкость, простота, эффективность – вроде здорово, но много ли у нас железа с поддержкой P4? Разбираемся дальше.

 

 

Устройства с поддержкой P4

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

 

Чипы Barefoot Tofino 2

Инженеры компании Barefoot Networks создали язык P4 и чипсет типа ASIC, который не использует проприетарный SDK, такой как Broadcom или Cavium. 

Скоростные каналы SerDes Barefoot Tofino 2 Chip и более высокий предел пропускной способности, по сравнению с ASIC предыдущего поколения, в разы масштабируют производительность. Архитектура Intel Tofino 2 также дает больше ресурсов для обработки жестких рабочих нагрузок в распределенных приложениях, масштабировании виртуальных машин, искусственном интеллекте и бессерверных развертываниях.

barefoot networks


Платформа Barefoot Tofino 2 и сравнение чипов в линейке Tofino

 

Netronome Agilio SmartNIC

Продукты SmartNIC — это стандартные сетевые адаптеры PCIe с соединениями, которые разгружают функции плоскости данных на сетевую карту вместо того, чтобы помочь приложениям или ядру тратить на это много ресурсов ЦП. Эти продукты — не просто коммутаторы или сетевые карты, у них нет доступного по умолчанию набора функций. 

Netronome Agilio CX

Netronome Agilio CX

 

Xilinx Alveo

Устройства Xilinx с поддержкой P4 представляют собой смарт-карты на базе FPGA из линейки продуктов Alveo. Более старые версии карт Alveo оснащены схемами Xilinx Zynq UltraScale + FPGA, а более новые версии включают специализированную FPGA Xilinx UltraScale+, которая установлена только в линейке Alveo. 

Карта Alveo U25 доступна с двумя сетевыми интерфейсами 10/25 GbE, а все более новые версии имеют один или два интерфейса 100 GbE.

У Xilinx есть своя целевая платформа FPGA для определения обработки пакетов в плоскости данных — SDNet. С точки зрения P4, среда SDNet предлагает несколько базовых архитектурных моделей, которые могут использовать программисты P4. Эти архитектуры включают XilinxSwitch, XilinxStreamSwitch и XilinxEngineOnly.

Xilinx Alveo U25

Xilinx Alveo U25

 

 

Программный коммутатор SONiC-P4

Что такое SONiC-P4?

SONiC-P4 — программный коммутатор на базе ASIC, который эмулируется P4 и использует sai_bm.p4 для программирования ASIC-коммутатора. Также он запускает сетевой стек SONiC. Текущую версию SONiC-P4 выпустили в виде образа докера. SONiC-P4 запускается везде, где работает докер — на «голом железе» Linux / Windows-машины, внутри виртуальной машины или в облачной среде.

 

Как использовать SONiC-P4?

Продемонстрируем использование программного коммутатора SONiC-P4 на простом стенде:

Топология тестового стенда для коммутатора SONiC-P4

Switch1 и switch2 — два коммутатора SONiC-P4 в двух разных BGP AS, которые взаимодействуют друг с другом. Switch1 объявляет 192.168.1.0/24, switch2 — 192.168.2.0/24.

  • На сервере Ubuntu качаем необходимые файлы. Разархивируем файл и переходим в каталог sonic/.
  • Запускаем ./install_docker_ovs.sh, чтобы установить docker и open-vswitch.
  • Запускаем ./load_image.sh, чтобы загрузить образ SONiC-P4. 

Запускаем ./start.sh, чтобы настроить стенд. Если все ок, появятся четыре докера.

CONTAINER ID

IMAGE

COMMAND

CREATED

STATUS

PORTS NAMES

db924306352f

ubuntu:14.04

"/bin/bash"

2 minutes ago

Up 2 minutes

host2

ae5e987dee8c

ubuntu:14.04

"/bin/bash"

2 minutes ago

Up 2 minutes

host1

aed19d76cd3a

docker-sonic-p4:latest

"/bin/bash"

2 minutes ago

Up 2 minutes

switch2

680f95a83512

docker-sonic-p4:latest

"/bin/bash"

2 minutes ago

Up 2 minutes

switch1

 

  • Ждем минуту до загрузки и запускаем ./test.sh, который пингует host2 с host1.

lgh@acs-p4:~/sonic$ ./test.sh                 

PING 192.168.2.2 (192.168.2.2) 56(84) bytes of data.

64 bytes from 192.168.2.2: icmp_seq=1 ttl=62 time=9.81 ms

64 bytes from 192.168.2.2: icmp_seq=2 ttl=62 time=14.9 ms

64 bytes from 192.168.2.2: icmp_seq=3 ttl=62 time=8.42 ms

64 bytes from 192.168.2.2: icmp_seq=4 ttl=62 time=14.7 ms

 
 
  • Проверяем BGP на switch1.

lgh@acs-p4:~/sonic$ docker exec -it switch1 bash

root@switch1:/# vtysh -c "show ip bgp sum"

BGP router identifier 192.168.1.1, local AS number 10001

RIB entries 3, using 336 bytes of memory

Peers 1, using 4568 bytes of memory
 

  • Запускаем ./stop.sh для очистки.

Настройка топологии в start.sh

Для настройки топологии в start.sh мы запускаем четыре контейнера докеров. В качестве примера возьмем команду для switch1:

sudo docker run --net=none --privileged --entrypoint /bin/bash
--name switch1 -it -d -v $PWD/switch1:/sonic docker-sonic-p4:latest

Мы указываем --net = none, чтобы механизм Docker не добавил свой интерфейс docker0, который может помешать тестируемой топологии. --privileged позволяет каждому контейнеру настраивать свои интерфейсы. -v $ PWD / switch1: / sonic монтирует папку конфигурации в контейнеры коммутатора.

Затем создаем три связи. В качестве примера возьмем связь между switch1 и switch2. Следующие команды соединяют интерфейс eth1 switch1 с интерфейсом eth1 switch2:

sudo ovs-vsctl add-br switch1_switch2

sudo ovs-docker add-port switch1_switch2 eth1 switch1

sudo ovs-docker add-port switch1_switch2 eth1 switch2

 

Также настраиваем IP-адрес интерфейса и маршруты по умолчанию на host1 и host2. Пример для host1:

sudo docker exec -d host1 ifconfig eth1 192.168.1.2/24 mtu 1400

sudo docker exec -d host1 ip route replace default via 192.168.1.1

 

Наконец, вызываем сценарий запуска для switch1 и switch2:

sudo docker exec -d switch1 sh /sonic/scripts/startup.sh

sudo docker exec -d switch2 sh /sonic/scripts/startup.sh

 

 

 

Конфигурация SONiC-P4

В start.sh мы смонтировали папку конфигурации в контейнер коммутатора в /sonic. Наиболее важные конфигурации находятся в /sonic/scripts/startup.sh, /sonic/etc/config_db/vlan_config.json и /sonic/etc/quagga/bgpd.conf

В /sonic/scripts/startup.sh запускаем все службы SONiC и сам программный коммутатор P4 следующей строкой:

simple_switch --log-console -i 1@eth1 -i 2@eth2 …

Это действие связывает интерфейс eth1 с портом 1 программного коммутатора P4, eth2 — с портом 2 и так далее. Эти интерфейсы ethX обычно называются интерфейсами передней панели и напрямую используются коммутаторами P4 для передачи пакетов плоскости данных.

Однако SONiC работает с интерфейсами другого типа, так называемыми хост-интерфейсами EthernetX. Они предназначены для плоскости управления SONiC и НЕ несут пакеты плоскости данных.

Мы настраиваем одноранговый IP и MTU на хост-интерфейсах. SONiC считывает конфигурации, такие как IP и MTU, с интерфейсов хоста, а затем настраивает эти значения на программном коммутаторе P4 с помощью SAI.

Сопоставление между интерфейсами хоста и портами коммутатора указано в /port_config.ini:

# alias         lanes

Ethernet0       1

Ethernet1       2

 

Вместе с командой simple_switch в /sonic/scripts/startup.sh мы настроили следующее отображение: Ethernet0 -> lane 1 -> eth1. По сути, это отображение между интерфейсами хоста и интерфейсами передней панели.

/sonic/etc/config_db/vlan_config.json настраивает интерфейсы коммутатора vlan, который мы используем в этом эксперименте, через интерфейс ConfigDB (подробности — в руководстве SONiC Configuration Database):

{

    "VLAN": {

        "Vlan15": {

            "members": [

                "Ethernet0"

            ], 

            "vlanid": "15"

        }, 

        "Vlan10": {

            "members": [

                "Ethernet1"

            ], 

            "vlanid": "10"

        }

    },

    "VLAN_MEMBER": {

        "Vlan15|Ethernet0": {

            "tagging_mode": "untagged"

        },

        "Vlan10|Ethernet1": {

            "tagging_mode": "untagged"

        }

    },

    "VLAN_INTERFACE": {

        "Vlan15|10.0.0.0/31": {},

        "Vlan10|192.168.1.1/24": {}

    }

}

 
 

/sonic/etc/quagga/bgpd.conf настраивает сеанс BGP на коммутаторе. Вот конфигурация BGP для switch1, который взаимодействует с switch2, используя одноранговый IP-адрес 10.0.0.0/31, и объявляет 192.168.1.0/24:

router bgp 10001                        

  bgp router-id 192.168.1.1             

  network 192.168.1.0 mask 255.255.255.0

  neighbor 10.0.0.1 remote-as 10002     

  neighbor 10.0.0.1 timers 1 3          

  neighbor 10.0.0.1 send-community      

  neighbor 10.0.0.1 allowas-in          

  maximum-paths 64                      

!                                       

access-list all permit any

 

Итак, пока это все. В этой первой части мы познакомились с языком P4, подходящими для него hardware-платформами и настроили коммутатор SONiC-P4. Во второй части – разберем сетевую архитектуру и поэкспериментируем. Так что подписывайтесь и оставайтесь с нами!

 

Promwad в телекоме

 

 

Наши проекты

 

IEEE C37.94 мультиплексор для системы Мы разработали схемотехнику, печатную плату и прошивку ПЛИС для мультиплексора IEEE C37.94, который служит частью системы телезащиты Smart Grid IEC-61850.

Микрокомпьютер на базе 88F6282 Мы разработали решение, которое позволяет значительно сократить время, затраты и риски при проектировании новых продуктов c интерфейсами WiFi и Gigabit Ethernet
Процессорный модуль на базе TI DM3730 Мы разработали процессорный модуль на базе процессора TI DM3730, которое позволяет значительно сократить время, затраты и риски при проектировании новых продуктов
Промышленные коммутаторы Мы разработали новую линейку из восьми промышленных Ethernet-коммутаторов 1Gb/10Gbps, работающих в температурном диапазоне от -20 до +70°C
Спутниковый SDR-модем Мы разработали программно-аппаратную платформу спутникового модема в форм-факторе 1U, разделив его на две платы: цифровую и аналоговую.
Покупки из видео ИИ-приложение Мы разработали приложение для ТВ-приставок на базе Android и Java. Распознавание фото и видео реализовали на базе нейросети белорусского стартапа Oyper
DSP + FPGA модуль для систем связи Мы спроектировали программно-аппаратную платформу для беспроводных систем связи с поддержкой 17 протоколов и стандартов
Коммутатор L2 на 8 и 16 портов Мы создали для заказчика референс-дизайн промышленных управляемых коммутаторов с портами 10Gbase-X и 10/100/1000Base-T
Роутер с контент-фильтром Мы разработали опытные образцы умного абонентского роутера (Smart CPE) со встроенным сервисом контент-фильтрации и блокировки рекламы
Коммутаторы второго уровня Серия устройств под ключ — разработаны управляемые коммутаторы второго уровня на базе набора микросхем Realtek 83xx и Linux kernel
OpenWRT портирование Мы проанализировали и протестировали несколько вариантов аппаратной платформы на базе чипсетов Mediatek, Qualcomm, Realtek и выбрали оптимальное решение
BYPASS сетевой анализатор Мы создали прототипы анализатора сетевого трафика для реализации функции BYPASS
Роутер для малого офиса Разработали и подготовили к производству модем-маршрутизатор с поддержкой всех видов офисной связи

MPEG2/4 встроенное ПОМы доработали встроенное программное обеспечение для многоканального транскодирования транспортных потоков MPEG2-TS в потоки формата H264

OpenWRT для RealtekМы разработали специализированный дистрибутив Embedded Linux для роутеров, маршрутизаторов и других сетевых устройств

VoIP абонентское устройство Разработано абонентское устройство для предоставления услуг голосовой связи по протоколу IP в широкополосных сетях

IPTV + T/C гибридная ТВ-приставка Мы спроектировали референс-дизайн ТВ-приставки, который предназначен для платформенной разработки и вывода на рынок цифровых устройств
Smartlabs IPTV-приставка Самый крупносерийный проект! Мы разработали IPTV-приставку с ОТТ и PVR — выбор абонентов Ростелекома и МТС

AK1100 тонкий клиентМы разработали компьютер для сетей с клиент-серверной архитектурой, который переносит основные процессы на удаленный сервер

IP-Plug ARM-мини-серверМы разработали первый в России коммерческий plug-компьютер — IP-Plug АК-Systems

JPEG2000 4-канальный регистраторМы разработали устройство видеорегистрации для оцифровки, хранения и выдачи по запросу аудио и видеопотоков от 4 аналоговых источников

IPTV STB DVB-T/S/S2Мы спроектировали цифровую IPTV-приставку с поддержкой форматов DVB-T и DVB-S/S2 на базе процессора SMP8654