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

Используемые термины:

OpenFlow — протокол управления передачей данных в сети. Описывает процесс взаимодействия контроллера и коммутатора, а также формат загружаемых в коммутатор правил.
Open vSwitch — программная реализация коммутатора, совместимого с протоколом OpenFlow. Используется для управления трафиком в системах виртуализации, например, OpenStack и oVirt (экспериментально).
802.1Q (VLAN) — механизм разграничения трафика как в пределах одного коммутатора, так и в локальной сети. Основан на внедрении в пакет данных тега (номера) VLAN
802.1p (QoS) — механизм управления приоритезацией трафика. Часть стандарта 802.1Q
Порт агрегации (trunk port) — порт коммутатора, соединенный с вышестоящим коммутатором. Порт агрегации разрешает отправку пакетов с любым номером VLAN
Порт доступа (access port) — порт коммутатора, разрешающий работу с пакетами только определенных VLAN.


Введение


В Сети есть много руководств по основам OpenFlow, но эта статья о другом. Тем не менее, для понимания данной статьи базовые знания об OpenFlow обязательны. Если вы не до конца понимаете, как работают правила OpenFlow, изучите пожалуйста основы, а затем возвращайтесь к этой статье.
Также вам потребуются знания основ Open vSwitch. Если вы никогда не использовали утилиты управления ovs-vsctl или ovs-ofctl, вам следует немного почитать про них перед тем, как мы продолжим.

Большая часть возможностей, описанных в этом руководстве, относятся к расширенной версии протокола OpenFlow, реализованной в Open vSwitch.Если вы используйте аппаратную реализацию в виде коммутатора на базе ASIC-чипа, это руководство вряд ли вам поможет.

Мы не будем подробно рассматривать всю подноготную технологий, о которых пойдет речь. За подробностями вы можете обратиться к документации Open vSwitch, в особенности – к файлам справки ovs-vsctl , а также к комментариям в файлах include/openflow/nicira-ext.h и include/openvswitch/meta-flow.h.

Приступая к работе


Будем считать, что в вашей системе уже установлены исполняемые файлы Open vSwitch. Какое-либо специализированное аппаратное обеспечение, кроме собственно компьютера, вам не потребуется. Вместо этого мы будем использовать скрипт ovs-sandbox, который можно найти здесь. Данный скрипт нужен для создания программно-определяемой сетевой среды на базе Open vSwitch.

Вы можете использовать ovs-sandbox тремя способами:

  • Если вы уже установили Open vSwitch на вашу систему, вы можете просто запустить скрипт ovs-sandbox, без указания каких-либо параметров.
  • Если Open vSwitch у вас не установлен (и вы не хотите устанавливать его), то можете собрать Open vSwitch без установки в соответствии с инструкциями для Linux, FreeBSD и NetSBD. Затем запустите ovs-sandbox и укажите ему каталог с Open vSwitch ./ovs-sandbox -b <каталог>
  • Как вариант, вы можете запустить команду make sandbox из каталога, в котором собран Open vSwitch

Скрипт делает следующее:

  1. ВНИМАНИЕ: удаляет все подкаталоги из подкаталога с именем sandbox текущего каталога и все файлы в этом подкаталоге
  2. Создает новый каталог «sandbox» в текущем каталоге.
  3. Устанавливает переменные среды, чтобы утилиты Open vSwitch использовали в качестве текущего каталога “sandbox”, а не каталог установки Open vSwitch
  4. Если вы собирали Open vSwitch, но не устанавливали его, копирует файлы справки в подкаталог «sandbox» и записывает имя этого подкаталога в переменную среды MANPATH. Это означает, что когда вы будете использовать, например, команду man ovs-vsctl, вы увидите справку именно для той версии Open vSwitch, которую вы собирали.
  5. Создает пустую базу данных конфигурации Open vSwitch в каталоге “sandbox”
  6. Запускает ovsdb-server в каталоге “sandbox”
  7. Запускает ovs-vswitchd в каталоге “sandbox” и передает ему параметры для специального тестового режима работы.
  8. Запускает сеанс интерактивной оболочки из каталога “sandbox”

Начиная с этого момента, вы можете запускать все привычные команды Open vSwitch из сеанса оболочки. Например, вы можете запустить ovs-vsctl и создать виртуальный коммутатор:

$ ovs-vsctl add-br br0

С точки зрения Open vSwitch, созданный вами коммутатор так же реален, как и любой другой. Например, вы можете подключить его к OpenFlow-контроллеру или использовать ovs-ofctl для проверки и изменения правил управления трафиком. С другой стороны, коммутатор не отображается в сетевой конфигурации хоста, так что утилиты ifconfig и ip не могут работать с ним. Кроме того, не будут работать и утилиты ping и tcpdump. Но у этого есть и хорошая сторона — вы не сможете сбить сетевые настройки хоста, изменяя настройки коммутатора в “песочнице”.

Когда вам потребуется завершить работу с Open vSwitch, просто наберите в оболочке команду “exit” или нажмите Ctrl+D. Это завершит процессы, запущенные скриптом ovs-sandbox, но оставит на месте сам каталог “sandbox” со всем его содержимым.

Каталог “sandbox” содержит файлы журналов для всех служб Open vSwitch, так что вы можете просмотреть их и после того, как завершили работу с Open vSwitch.

Использование отладчика GDB


Этот обучающий материал не требует применения отладчика GDB в обязательном порядке, тем не менее вы можете использовать GDB, чтобы разобраться во внутреннем устройстве утилит Open vSwitch.

GDB можно применять для отладки любого уже запущенного процесса при помощи команды gdb <process-id>.

Скрипт ovs-sandbox имеет параметр -g для запуска ovs-vswitchd под GDB. Параметр можно использовать в случае, если требуется поставить точки останова перед выполнением ovs-vswitchd или чтобы отладить сбой на раннем этапе запуска службы. Кроме того, существует параметр -d, который запускает под GDB службу ovsdb-server. Оба параметра можно использовать одновременно.

Имеется также параметр -e, который тоже запускает под GDB службу ovs-vswitchd, но не отображает приглашение gdb> и не ждет ввода команд, а сразу приступает к исполнению службы. Параметр -r заставляет службу ovsdb-server запускаться под GDB сразу же, подобно ovs-vswitchd.

Чтобы избежать некорректного поведения терминала при запуске GDB, скрипт ovs-sandbox открывает новый сеанс xterm для каждой сессии GDB. Для систем без графического сервера X Windows поддержка GDB отключена.

При запуске тестовой среды из make-файла скрипту можно передать параметр -g через переменную среды SANDBOXFLAGS. Таким образом, команда make sandbox SANDBOXFLAGS=-g запустит тестовую среду со службой ovs-vswitchd под отладчиком GDB в отдельном X-терминале.

Постановка задачи


Основная цель этого руководства — демонстрация обширных возможностей Open vSwitch по управлению трафиком. В ходе работы мы построим коммутатор, способный запоминать MAC-адреса и имеющий разделенные VLAN’ами порты доступа и агрегации. Если не рассматривать функции Open vSwitch, о которых мы поговорми чуть ниже, OpenFlow предлагает как минимум два пути для реализации такого коммутатора.

  • Использование контроллера OpenFlow для запоминания MAC-адресов в режиме постобработки трафика. Каждое новое соединение сначала анализируется контроллером, который формирует для него разрешающие правила. Всякий раз, когда новый MAC-адрес появляется на порту или “переезжает” из одного порта в другой, контроллер должен изменить соответствующую таблицу правил коммутатора.
  • “Обычное” (NORMAL) поведение. OpenFlow определяет такое поведение, как “работу обычного коммутатора без OpenFlow”. Если правило предусматривает такое поведение, то пакет проходит через коммутатор так, как будто на коммутаторе не настроен OpenFlow.

К сожалению, оба подхода не лишены недостатков. В первом случае использование контроллера приводит к существенному уменьшению полосы пропускания и увеличению задержек. Кроме того, контроллер плохо масштабируется для обслуживания большого количества коммутаторов, что особенно важно в среде с тысячами гипервизоров, на каждом из которых работает собственный OpenFlow-коммутатор. Естественно, запоминание MAC-адресов с использованием контроллера не будет работать, если контроллер вышел из строя, медленно обрабатывает запросы или недоступен вследствие сетевых проблем.

Во втором случае (NORMAL) мы имеем дело с проблемами иного рода. Во-первых, лишь небольшая часть ”обычного” поведения стандартизирована и по-разному работает на оборудовании различных производителей. Доступные функции и способы их конфигурирования (как правило, не через OpenFlow) у разных производителей тоже отличаются. Во-вторых, режим NORMAL не очень хорошо работает совместно с другими правилами OpenFlow. NORMAL реализует концепцию “все или ничего” и имеет весьма узкий потенциал для настройки своего поведения или взаимодействия с другими функциями.

Сценарий тестирования


Мы построим OpenFlow-коммутатор и настроим его таблицы для запоминания MAC-адресов и работы с VLAN-ами. Коммутатор будет иметь 4 порта:

  • p1 — порт агрегации для всех VLAN на порте 1 коммутатора
  • p2 — порт доступа для нетегированного VLAN 20 на порте 2 коммутатора
  • p3,p4 — порты доступа для нетегированного VLAN 30 на портах 2 и 3 коммутатора, соответственно

Имена портов не имеют значения, вы можете назвать их eth1-eth4 или другим образом по вашему желанию.
Коммутатор OpenFlow также имеет один порт, видимый на хосте как локальный интерфейс. Наш сценарий не предусматривает его использование.

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

  • Таблица 0: Входной контроль
  • Таблица 1: Обработка VLAN на входе
  • Таблица 2: Запоминание MAC и VLAN для входного порта
  • Таблица 3: Поиск порта для выходного MAC и VLAN
  • Таблица 4: Обработка пакета на выходе

Ниже будет показано, как создать такой коммутатор таблица за таблицей.

Вы можете копировать команды ovs-vsctl и ovs-ofctl с параметрами из примеров ниже и вставлять их в командную оболочку, созданную ovs-sandbox. Команда ovs-appctl с параметрами предназначена для тестов и ее не следует использовать отдельно от остальных примеров.

Установка


Запустим ovs-sandbox и в открывшейся интерактивной оболочке введем следующую команду:

$ ovs-vsctl add-br br0 -- set Bridge br0 fail-mode=secure

Эта команда создает новый виртуальный коммутатор br0 и переводит его в так называемый режим fail-secure. Для нас пока это будет значить только то, что коммутатор запустится с пустыми таблицами.

Если мы не сделаем этого, коммутатор запустится с единственным правилом, которое отрабатывает поведение NORMAL. Мы можем использовать эту возможность для построения такого же коммутатора, как и наш, но с ограничениями, описанными выше в разделе “Постановка задачи”

Новый коммутатор пока имеет только один порт — локальный br0. Нам нужно добавить порты p1, p2, p3 и p4. Команда for оболочки — один из способов сделать это:

$ for i in 1 2 3 4; do
    ovs-vsctl add-port br0 p$i -- set Interface p$i ofport_request=$i
    ovs-ofctl mod-port br0 p$i up
done

Команды не просто добавляют порты, но и устанавливают атрибут ofport_request, чтобы порт p1 соответствовал порту 1 OpenFlow, порт 2 — порту 2 OpenFlow и так далее.

Мы можем пропустить установку атрибута ofport_request и позволить OpenFlow самому выбирать порты, но в целях обучения мы присвоим номера портов самостоятельно, чтобы говорить, например, о порте 1 OpenFlow и знать, что он ссылается на порт p1.

Команда ovs-ofctl, используя запрос к OpenFlow, запускает интерфейсы, которые при создании выключены. К такому же эффекту приводит запуска команды ifconfig up, но интерфейсы тестовой среды не отображаются в хостовой ОС и ifconfig не может ими управлять.

Мы пока еще не настраивали VLAN’ы и запоминание MAC-адресов, поскольку планируем сделать это ниже при помощи таблиц управления трафиком.

Чтобы посмотреть результат нашей работы, можно воспользоваться командами ovs-vsctl show или ovs-ofctl show br0.

Создаем таблицу 0: Входной контроль


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

$ ovs-ofctl add-flow br0 "table=0, dl_src=01:00:00:00:00:00/01:00:00:00:00:00, actions=drop"

Коммутатор также не должен пересылать дальше пакеты протокола IEEE 802.1D Spanning Tree Protocol (STP), так что мы добавим правило для отбрасывания таких пакетов и пакетов других зарезервированных многоадресных протоколов:

$ ovs-ofctl add-flow br0 "table=0, dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0, actions=drop"

Мы можем добавить правила для выбраковки пакетов других протоколов по принципу, показанному выше.

Нам нужно еще одно правило с приоритетом ниже приоритета по-умолчанию, чтобы пакеты, которые не были отброшены вышестоящими правилами, отправились на следующую стадию — в таблицу 1 OpenFlow.

$ ovs-ofctl add-flow br0 "table=0, priority=0, actions=resubmit(,1)"

Действие resubmit — это часть расширения Open vSwitch для OpenFlow.

Проверяем таблицу 0


Если мы используем Open vSwitch для настройки физического или виртуального коммутатора, нам нужно тестировать его путем отправки пакетов разными путями, например, с помощью всем известных утилит ping и tcpdump или более специализированных средств типа Scapy. Для нашего виртуального коммутатора это затруднительно, поскольку он невидим для операционной системы.

Однако виртуальный коммутатор имеет несколько специальных инструментов для тестирования. Наиболее широкий функционал предлагает ofproto/trace. Принимая на вход имя коммутатора и описание трафика, ofproto/trace пошагово демонстрирует, какие правила будут им затронуты.

Пример 1


Попробуйте выполнить команду:

$ ovs-appctl ofproto/trace br0 in_port=1,dl_dst=01:80:c2:00:00:05

Вывод будет примерно таким:

Bridge: br0
Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:80:c2:00:00:05,dl_type=0x0000

Rule: table=0 cookie=0 dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0
OpenFlow actions=drop

Final flow: unchanged
Megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=01:80:c2:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
Datapath actions: drop

Первая строка показывает параметры обрабатываемого пакета в более детализированном виде, чем мы пишем в командной строке.

Вторые несколько строк показывают путь пакета внутри коммутатора br0. Мы видим, что в таблице 0 было найдено соответствующее правило с определенным приоритетом и заданным действием. Если пакет подпадает под несколько правил, таких строк будет несколько.

Последняя группа строк отображает результат. который мы не будем здесь подробно рассматривать.

Пример 2


Попробуем выполнить команду

$ ovs-appctl ofproto/trace br0 in_port=1,dl_dst=01:80:c2:00:00:10

Вывод ее будет таким:

Bridge: br0
Flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:80:c2:00:00:10,dl_type=0x0000

Rule: table=0 cookie=0 priority=0
OpenFlow actions=resubmit(,1)

        Resubmitted flow: in_port=1,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=01:80:c2:00:00:10,dl_type=0x0000
        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
        Resubmitted  odp: drop
        Resubmitted megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=01:80:c2:00:00:10/ff:ff:ff:ff:ff:f0,dl_type=0x0000
        Rule: table=254 cookie=0 priority=0,reg0=0x2
        OpenFlow actions=drop

Final flow: unchanged
Megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=01:80:c2:00:00:10/ff:ff:ff:ff:ff:f0,dl_type=0x0000
Datapath actions: drop

В этом случае пакет не соответствует какому-либо правилу с действием “drop” в таблице 0, так что он попадает под правило с действием resubmit. Действие resubmit перенаправляет пакет в таблицу 1, что показано в строке

OpenFlow actions=resubmit(,1)

Мы пока еще не добавили какие-либо правила в таблицу 1 OpenFlow, так что пакет не будет соответствовать ни одному правилу. Таким образом, пакет будет отброшен, так что со стороны результат будет таким же, как и в первом примере.

Создаем таблицу 1: Обработка VLAN на входе


Пакеты, которые приходит в таблицу 1, уже прошли все основные проверки в таблице 0. Таблица 1 предназначена для прикрепления пакетов к VLAN’ам. Проверка основана на сопоставлении VLAN’а и порта, через который пакеты попадают в коммутатор. Мы также будем использовать таблицу для прикрепления заголовка VLAN к пакету, который уходит в порт агрегации, что позволит отложить стадии обработка, относящиеся к VLAN, поскольку номер VLAN всегда будет частью заголовка (за исключением особых случаев).

Начнем с добавления правила с низким приоритетом, которое будет отбрасывать все пакеты. Перед ним мы добавим правила, пропускающие нужные нам пакеты. Пусть это будет правило типа “отбрасывать по-умолчанию”

$ ovs-ofctl add-flow br0 "table=1, priority=0, actions=drop"

Порт агрегации p1 (или OpenFlow порт 1, что проще) принимает любые пакеты вне зависимости, имеют ли они или нет заголовок VLAN и каков этот заголовок. Таким образом, мы можем добавить правило, которое перенаправляет весь трафик с порта 1 в следующую таблицу:

$ ovs-ofctl add-flow br0 "table=1, priority=99, in_port=1, actions=resubmit(,2)"

На портах доступа мы можем принимать любые пакеты без заголовка VLAN, присваивать им соответствующий VLAN и передавать на следующую ступень.

$ ovs-ofctl add-flows br0 - <<'EOF'
table=1, priority=99, in_port=2, vlan_tci=0, actions=mod_vlan_vid:20, resubmit(,2)
table=1, priority=99, in_port=3, vlan_tci=0, actions=mod_vlan_vid:30, resubmit(,2)
table=1, priority=99, in_port=4, vlan_tci=0, actions=mod_vlan_vid:30, resubmit(,2)
EOF

Мы не будем писать правил. обрабатывающих пакеты с уже имеющимися метками VLAN (802.1Q), которые приходят на порты доступа, так что эти пакеты будут отброшены правилом по-умолчанию. Именно такое поведение мы и ожидаем от портов доступа.

В других случаях порты доступа пропускают пакеты, принадлежащие VLAN 0 (т.е. пакеты, отмеченные в соответствии с механизмом приоритезации трафика 802.1p). Чтобы разрешить такие пакеты, замените vlan_tci=0 на vlan_tci=0/0xfff в правиле выше.

Тестируем таблицу 1


Утилита ofproto/trace позволяет нам проверить добавленные правила для управления VLAN’ами на входе в коммутатор.

Пример 1. Пакет на порте агрегации


Проверяем пакет, который пришел в коммутатор с порта агрегации:

$ ovs-appctl ofproto/trace br0 in_port=1,vlan_tci=5

Из вывода команду следует, что пакет проходит таблицу 0, пересылается в таблицу 1, а затем — в таблицу 2 (в которой пока еще нет правил).

Bridge: br0
Flow: in_port=1,vlan_tci=0x0005,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000

Rule: table=0 cookie=0 priority=0
OpenFlow actions=resubmit(,1)

        Resubmitted flow: in_port=1,vlan_tci=0x0005,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000
        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
        Resubmitted  odp: drop
        Resubmitted megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
        Rule: table=1 cookie=0 priority=99,in_port=1
        OpenFlow actions=resubmit(,2)

                Resubmitted flow: unchanged
                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                Resubmitted  odp: drop
                Resubmitted megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                Rule: table=254 cookie=0 priority=0,reg0=0x2
                OpenFlow actions=drop

Final flow: unchanged
Megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
Datapath actions: drop

Пример 2. Корректный пакет на порте доступа


Тестируем корректный пакет (т.е. пакет без заголовка 802.1Q), приходящий на порт p2:

$ ovs-appctl ofproto/trace br0 in_port=2

Вывод этой команды похож на вывод предыдущей за исключением того, что пакету перед отправкой в таблицу 2 присваивается метка VLAN 20.

Bridge: br0
Flow: in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000

Rule: table=0 cookie=0 priority=0
OpenFlow actions=resubmit(,1)

        Resubmitted flow: in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000
        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
        Resubmitted  odp: drop
        Resubmitted megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
        Rule: table=1 cookie=0 priority=99,in_port=2,vlan_tci=0x0000
        OpenFlow actions=mod_vlan_vid:20,resubmit(,2)

                Resubmitted flow: in_port=2,dl_vlan=20,dl_vlan_pcp=0,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000
                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                Resubmitted  odp: drop
                Resubmitted megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                Rule: table=254 cookie=0 priority=0,reg0=0x2
                OpenFlow actions=drop

Final flow: in_port=2,dl_vlan=20,dl_vlan_pcp=0,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000
Megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
Datapath actions: drop

Пример 3. Некорректный пакет (с заголовком 801.2Q) на порте p2


$ ovs-appctl ofproto/trace br0 in_port=2,vlan_tci=5

Вывод команды показывает, что пакет поступает в таблицу 1 и отбрасывается правилом по-умолчанию.

Bridge: br0
Flow: in_port=2,vlan_tci=0x0005,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000

Rule: table=0 cookie=0 priority=0
OpenFlow actions=resubmit(,1)

        Resubmitted flow: in_port=2,vlan_tci=0x0005,dl_src=00:00:00:00:00:00,dl_dst=00:00:00:00:00:00,dl_type=0x0000
        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
        Resubmitted  odp: drop
        Resubmitted megaflow: recirc_id=0,in_port=2,vlan_tci=0x0005,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
        Rule: table=1 cookie=0 priority=0
        OpenFlow actions=drop

Final flow: unchanged
Megaflow: recirc_id=0,in_port=2,vlan_tci=0x0005,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
Datapath actions: drop

Создаем таблицу 2: запоминаем пару MAC+VLAN на входящем порте


Таблица 2 позволяет коммутатору запомнить, какой MAC-адрес отправителя соответствует указанному порту и VLAN-метке пакета.

Эта таблица — хорошая иллюстрация того, почему VLAN-метки добавляются к пакетам, пришедшим в коммутатор через порт доступа, именно в таблице 1. Мы хотим сопоставлять пару VLAN+MAC с портом вне зависимости от того, принадлежал ли уже пакет к VLAN, когда пришел в коммутатор, или же метка VLAN была проставлена в пакете самим коммутатором.

Потребуется всего одно правило, чтобы реализовать нашу задумку. Вот оно:

$ ovs-ofctl add-flow br0     "table=2 actions=learn(table=10, NXM_OF_VLAN_TCI[0..11],                            NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],                            load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]),                      resubmit(,3)"

Действие “learn” изменяет таблицу с правилами на основании содержимого пакета, который обрабатывается в данный момент. Это действие является расширением протокола OpenFlow, реализованного в Open vSwitch.

Параметры действия “learn” можно описать следующим образом:

table=10

Изменять таблицу 10. В этой таблице мы будем запоминать MAC-адреса

NXM_OF_VLAN_TCI[0..11]

Создать правило в таблице 10, которому будут соответствовать все пакеты с VLAN, равным VLAN текущего пакета. Это позволит сопоставить MAC с единственным номером VLAN, как это обычно делают коммутаторы с поддержкой VLAN

NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[]

Добавляет в созданное правило параметр “MAC-адрес получателя”, который будет равен MAC-адресу отправителя текущего пакета.

load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]
Предыдущие команды задавали параметры пакета, обрабатываемого новым правилом, а данная команда определяет действие над пакетом. Это действие загружает номер порта, с которого пришел пакет, в регистр 0 (специальное поле, определенное в Open vSwitch расширении протокола OpenFlow)
В реальном процессе запоминания MAC-адресов могут участвовать еще два параметра. Во-первых, действие “learn” должно задавать параметр hard_timeout для нового типа трафика, чтобы позволить коммутатору “забывать” MAC-адреса, если с данного адреса не поступает пакетов в течение некоторого разумного времени. Во-вторых, следует ограничить потребление ресурсов, ограничив число строк в таблице 10 при помощи таблицы Flow_Table в базе данных конфигурации Open vSwitch.

Определенно, для понимания работы правила нам нужны примеры

Тестируем таблицу 2


Пример 1


Для начала выполним команду:

$ ovs-appctl ofproto/trace br0 in_port=1,vlan_tci=20,dl_src=50:00:00:00:00:01 -generate

Вывод команды покажет нам, что действие “learn” выполнено и в таблицу 10 добавлено новое правило.

Bridge: br0
Flow: in_port=1,vlan_tci=0x0014,dl_src=50:00:00:00:00:01,dl_dst=00:00:00:00:00:00,dl_type=0x0000

Rule: table=0 cookie=0 priority=0
OpenFlow actions=resubmit(,1)

        Resubmitted flow: in_port=1,vlan_tci=0x0014,dl_src=50:00:00:00:00:01,dl_dst=00:00:00:00:00:00,dl_type=0x0000
        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
        Resubmitted  odp: drop
        Resubmitted megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
        Rule: table=1 cookie=0 priority=99,in_port=1
        OpenFlow actions=resubmit(,2)

                Resubmitted flow: unchanged
                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                Resubmitted  odp: drop
                Resubmitted megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                Rule: table=2 cookie=0
                OpenFlow actions=learn(table=10,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]),resubmit(,3)

                        Resubmitted flow: unchanged
                        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                        Resubmitted  odp: drop
                        Resubmitted megaflow: recirc_id=0,in_port=1,vlan_tci=0x0014/0x0fff,dl_src=50:00:00:00:00:01,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                        Rule: table=254 cookie=0 priority=0,reg0=0x2
                        OpenFlow actions=drop

Final flow: unchanged
Megaflow: recirc_id=0,in_port=1,vlan_tci=0x0014/0x1fff,dl_src=50:00:00:00:00:01,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
Datapath actions: drop

Обратите внимание на параметр -generate. Как правило, ofproto/trace не влияет на систему: действие “output” на самом деле не отправляет пакеты, действие “learn” не изменяет таблицы и так далее. С параметром -generate утилита ofproto/trace вынуждает систему выполнять действие “learn”. Это важно, поскольку мы хотим видеть применение действия “learn” в таблице 10.

Выполним команду:

$ ovs-ofctl dump-flows br0 table=10

Которая покажет примерно следующее:

NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=4315.181s, table=10, n_packets=0, n_bytes=0, idle_age=4315, vlan_tci=0x0014/0x0fff,dl_dst=50:00:00:00:00:01 actions=load:0x1->NXM_NX_REG0[0..15]

Можно увидеть, что пакет с VLAN 20 и адресом отправителя 50:00:00:00:00:01 привел к созданию правила, которому соответствуют пакеты с VLAN 20 и адресом получателя 50:00:00:00:00:01. Правило загружает номер порта 1, с которого мы тестировали коммутатор, в регистр 0.

Пример 2


Выполним вторую команду:

$ ovs-appctl ofproto/trace br0 in_port=2,dl_src=50:00:00:00:00:01 -generate

Bridge: br0
Flow: in_port=2,vlan_tci=0x0000,dl_src=50:00:00:00:00:01,dl_dst=00:00:00:00:00:00,dl_type=0x0000

Rule: table=0 cookie=0 priority=0
OpenFlow actions=resubmit(,1)

        Resubmitted flow: in_port=2,vlan_tci=0x0000,dl_src=50:00:00:00:00:01,dl_dst=00:00:00:00:00:00,dl_type=0x0000
        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
        Resubmitted  odp: drop
        Resubmitted megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
        Rule: table=1 cookie=0 priority=99,in_port=2,vlan_tci=0x0000
        OpenFlow actions=mod_vlan_vid:20,resubmit(,2)

                Resubmitted flow: in_port=2,dl_vlan=20,dl_vlan_pcp=0,dl_src=50:00:00:00:00:01,dl_dst=00:00:00:00:00:00,dl_type=0x0000
                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                Resubmitted  odp: drop
                Resubmitted megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                Rule: table=2 cookie=0
                OpenFlow actions=learn(table=10,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]),resubmit(,3)

                        Resubmitted flow: unchanged
                        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                        Resubmitted  odp: drop
                        Resubmitted megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=50:00:00:00:00:01,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                        Rule: table=254 cookie=0 priority=0,reg0=0x2
                        OpenFlow actions=drop

Final flow: in_port=2,dl_vlan=20,dl_vlan_pcp=0,dl_src=50:00:00:00:00:01,dl_dst=00:00:00:00:00:00,dl_type=0x0000
Megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=50:00:00:00:00:01,dl_dst=00:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
Datapath actions: drop

Пакет, созданный этой командой, имеет те же VLAN и MAC, что и в предыдущем примере, хотя поступает с порта доступа и изначально не имеет метки VLAN. Пакет в предыдущем примере приходил с порта доступа и уже имел заголовок 802.1Q. Теперь выведем содержимое таблицы 10

$ ovs-ofctl dump-flows br0 table=10

NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=4355.977s, table=10, n_packets=0, n_bytes=0, idle_age=4355, hard_age=15, vlan_tci=0x0014/0x0fff,dl_dst=50:00:00:00:00:01 actions=load:0x2->NXM_NX_REG0[0..15]

Мы увидим, что правило изменилось, отражая тот факт, что пакет пришел с порта 2. Именно такого поведения мы и ожидали.

Создаем таблицу 3: поиск порта назначения


В этой таблице определяется порт назначения, куда коммутатор должен направить пакет, основываясь на MAC-адресе получателя и номере VLAN. Если в таблице 2 уже обрабатывались пакеты с такими же адресами отправителя и получателя, то у нас уже есть запомненный адрес получателя и мы сразу направим пакет туда.

В этом случае нам нужно только одно правило

$ ovs-ofctl add-flow br0 "table=3 priority=50 actions=resubmit(,10), resubmit(,4)"

Это правило сначала перенаправляет пакет в таблицу 10, в которой отрабатывает правило “learn”. Как мы говорили ранее, номера портов запомненных пакетов записываются в регистр 0. Если порт назначения для нашего пакета не запомнен, ни одно правило в таблице 10 не сработает, и действие “resubmit” закончится ничем. Поскольку регистры инициализируются нулевым значением, мы можем использовать значение 0 регистра №0 как индикатор того, что пакет на следующих стадиях обработки требуется разослать по всем портам.

Второе действие — перенаправление пакета в таблицу 4, на следующую стадию обработки.

Мы можем добавить еще одно правило, чтобы пропустить поиск в таблице 10 для многоадресных и широковещательных пакетов, поскольку их всегда требуется рассылать по всем портам.

$ ovs-ofctl add-flow br0 "table=3 priority=99 dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,4)"

В указанном выше правиле нет большой необходимости, поскольку широковещательные пакеты никогда не отображаются в таблице 10, так как мы поместили в таблицу 0 правило, отбрасывающее пакеты многоадресных протоколов.


Тестируем таблицу 3


Пример


Данная команда приводит к тому, что Open vSwitch запоминает адрес f0:00:00:00:00:01 на порте p1 VLAN’а 20:

$ ovs-appctl ofproto/trace br0  in_port=1,dl_vlan=20,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01 -generate

Вывод команды показывает, что получатель пакета неизвестен, поскольку в таблице 10 не было найдено соответствующей строки.

Bridge: br0
Flow: in_port=1,dl_vlan=20,dl_vlan_pcp=0,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000

Rule: table=0 cookie=0 priority=0
OpenFlow actions=resubmit(,1)

        Resubmitted flow: in_port=1,dl_vlan=20,dl_vlan_pcp=0,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000
        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
        Resubmitted  odp: drop
        Resubmitted megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=90:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
        Rule: table=1 cookie=0 priority=99,in_port=1
        OpenFlow actions=resubmit(,2)

                Resubmitted flow: unchanged
                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                Resubmitted  odp: drop
                Resubmitted megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=90:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                Rule: table=2 cookie=0
                OpenFlow actions=learn(table=10,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]),resubmit(,3)

                        Resubmitted flow: unchanged
                        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                        Resubmitted  odp: drop
                        Resubmitted megaflow: recirc_id=0,in_port=1,vlan_tci=0x0014/0x0fff,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                        Rule: table=3 cookie=0 priority=50
                        OpenFlow actions=resubmit(,10),resubmit(,4)

                                Resubmitted flow: unchanged
                                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                                Resubmitted  odp: drop
                                Resubmitted megaflow: recirc_id=0,in_port=1,vlan_tci=0x0014/0x0fff,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000
                                Rule: table=254 cookie=0 priority=0,reg0=0x2
                                OpenFlow actions=drop

                                Resubmitted flow: unchanged
                                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                                Resubmitted  odp: drop
                                Resubmitted megaflow: recirc_id=0,in_port=1,vlan_tci=0x0014/0x0fff,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000
                                Rule: table=254 cookie=0 priority=0,reg0=0x2
                                OpenFlow actions=drop

Final flow: unchanged
Megaflow: recirc_id=0,in_port=1,dl_vlan=20,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000
Datapath actions: drop

Если два способа убедиться, что адрес отправителя был запомнен. Наиболее простой — вывести содержимое таблицы 10 с помощью команды:

$ ovs-ofctl dump-flows br0 table=10

которая покажет примерно следующее:

NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=54.661s, table=10, n_packets=0, n_bytes=0, idle_age=54, vlan_tci=0x0014/0x0fff,dl_dst=f0:00:00:00:00:01 actions=load:0x1->NXM_NX_REG0[0..15]

Если вы запускали предыдущие примеры, или пробовали выполнить свои собственные команды, в выводе вы можете увидеть дополнительные строки. Эти строки не должны влиять на выполнение наших команд, Если они вас смущают, вы можете удалить их с помощью команды ovs-ofctl del-flows br0 table=10

Второй способ — это сгенерировать пакет, который задействует правило запоминания адресов. Например, мы можем сгенерировать пакет на порту p2, чей адрес назначения мы только что запомнили на порте p1.

$ ovs-appctl ofproto/trace br0 in_port=2,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01 -generate

Вывод команды приведен ниже. Обратите внимание на строки, которые отображают работу действия “resubmit(,10)”. По указанным строкам можно сделать вывод, что пакет соответствует правилу, созданному на основе первого MAC-адреса, который мы использовали, и это правило загружает номер порта p1 в регистр 0.

Bridge: br0
Flow: in_port=2,vlan_tci=0x0000,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01,dl_type=0x0000

Rule: table=0 cookie=0 priority=0
OpenFlow actions=resubmit(,1)

        Resubmitted flow: in_port=2,vlan_tci=0x0000,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01,dl_type=0x0000
        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
        Resubmitted  odp: drop
        Resubmitted megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=f0:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
        Rule: table=1 cookie=0 priority=99,in_port=2,vlan_tci=0x0000
        OpenFlow actions=mod_vlan_vid:20,resubmit(,2)

                Resubmitted flow: in_port=2,dl_vlan=20,dl_vlan_pcp=0,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01,dl_type=0x0000
                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                Resubmitted  odp: drop
                Resubmitted megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=f0:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                Rule: table=2 cookie=0
                OpenFlow actions=learn(table=10,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]),resubmit(,3)

                        Resubmitted flow: unchanged
                        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                        Resubmitted  odp: drop
                        Resubmitted megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                        Rule: table=3 cookie=0 priority=50
                        OpenFlow actions=resubmit(,10),resubmit(,4)

                                Resubmitted flow: unchanged
                                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                                Resubmitted  odp: drop
                                Resubmitted megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01,dl_type=0x0000
                                Rule: table=10 cookie=0 vlan_tci=0x0014/0x0fff,dl_dst=f0:00:00:00:00:01
                                OpenFlow actions=load:0x1->NXM_NX_REG0[0..15]

                                Resubmitted flow: reg0=0x1,in_port=2,dl_vlan=20,dl_vlan_pcp=0,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01,dl_type=0x0000
                                Resubmitted regs: reg0=0x1 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                                Resubmitted  odp: drop
                                Resubmitted megaflow: recirc_id=0,reg0=0/0xffff,in_port=2,vlan_tci=0x0000,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01,dl_type=0x0000
                                Rule: table=254 cookie=0 priority=0,reg0=0x2
                                OpenFlow actions=drop

Final flow: reg0=0x1,in_port=2,dl_vlan=20,dl_vlan_pcp=0,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01,dl_type=0x0000
Megaflow: recirc_id=0,in_port=2,vlan_tci=0x0000,dl_src=90:00:00:00:00:01,dl_dst=f0:00:00:00:00:01,dl_type=0x0000
Datapath actions: drop

Если вы внимательно посмотрите на данные команды, то увидите, что они отличаются только тем, что MAC-адреса отправителя и получателя переставлены местами. Это значит, что если мы вновь запустим команду ovs-appctl

$ ovs-appctl ofproto/trace br0 in_port=1,dl_vlan=20,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01 -generate

то увидим, что было выполнено действие “load” в таблице 10 и адрес назначения был запомнен:

Bridge: br0
Flow: in_port=1,dl_vlan=20,dl_vlan_pcp=0,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000

Rule: table=0 cookie=0 priority=0
OpenFlow actions=resubmit(,1)

        Resubmitted flow: in_port=1,dl_vlan=20,dl_vlan_pcp=0,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000
        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
        Resubmitted  odp: drop
        Resubmitted megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=90:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
        Rule: table=1 cookie=0 priority=99,in_port=1
        OpenFlow actions=resubmit(,2)

                Resubmitted flow: unchanged
                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                Resubmitted  odp: drop
                Resubmitted megaflow: recirc_id=0,in_port=1,dl_src=00:00:00:00:00:00/01:00:00:00:00:00,dl_dst=90:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                Rule: table=2 cookie=0
                OpenFlow actions=learn(table=10,NXM_OF_VLAN_TCI[0..11],NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IN_PORT[]->NXM_NX_REG0[0..15]),resubmit(,3)

                        Resubmitted flow: unchanged
                        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                        Resubmitted  odp: drop
                        Resubmitted megaflow: recirc_id=0,in_port=1,vlan_tci=0x0014/0x0fff,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:00/ff:ff:ff:ff:ff:f0,dl_type=0x0000
                        Rule: table=3 cookie=0 priority=50
                        OpenFlow actions=resubmit(,10),resubmit(,4)

                                Resubmitted flow: unchanged
                                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                                Resubmitted  odp: drop
                                Resubmitted megaflow: recirc_id=0,in_port=1,vlan_tci=0x0014/0x0fff,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000
                                Rule: table=10 cookie=0 vlan_tci=0x0014/0x0fff,dl_dst=90:00:00:00:00:01
                                OpenFlow actions=load:0x2->NXM_NX_REG0[0..15]

                                Resubmitted flow: reg0=0x2,in_port=1,dl_vlan=20,dl_vlan_pcp=0,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000
                                Resubmitted regs: reg0=0x2 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0 reg8=0x0 reg9=0x0 reg10=0x0 reg11=0x0 reg12=0x0 reg13=0x0 reg14=0x0 reg15=0x0
                                Resubmitted  odp: drop
                                Resubmitted megaflow: recirc_id=0,reg0=0/0xffff,in_port=1,vlan_tci=0x0014/0x0fff,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000
                                Rule: table=254 cookie=0 priority=0,reg0=0x2
                                OpenFlow actions=drop

Final flow: reg0=0x2,in_port=1,dl_vlan=20,dl_vlan_pcp=0,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000
Megaflow: recirc_id=0,in_port=1,dl_vlan=20,dl_src=f0:00:00:00:00:01,dl_dst=90:00:00:00:00:01,dl_type=0x0000
Datapath actions: drop

Создаем таблицу 4: обработка на выходе


К началу работы с таблицей 4 у нас есть регистр 0, содержащий или номер порта, в который надо переслать пакет, или 0 если пакет нужно разослать по всем портам. Мы также знаем, что пакет содержит метку VLAN (802.1Q), даже если он пришел в коммутатор без метки через соответствующий порт доступа.

Задача последней стадии обработки — отправка пакета получателю. Эта задачу легко выполнить для порта агрегации p1:

$ ovs-ofctl add-flow br0 "table=4 reg0=1 actions=1"

Для отправки в порт получателя мы просто вырезаем заголовок VLAN перед отправкой пакета:

$ ovs-ofctl add-flows br0 - <<'EOF'
table=4 reg0=2 actions=strip_vlan,2
table=4 reg0=3 actions=strip_vlan,3
table=4 reg0=4 actions=strip_vlan,4
EOF

Для весьма небольшой части широковещательных, многоадресных и одноадресных пакетов MAC-адрес получателя не будет запомнен. В этом случае нам надо убедиться, что только выходные пакеты на портах имеют VLAN-метки и мы будем прикреплять заголовки 802.1Q при отправке их только через порты доступа, а не через порт агрегации:

$ ovs-ofctl add-flows br0 - <<'EOF'
table=4 reg0=0 priority=99 dl_vlan=20 actions=1,strip_vlan,2
table=4 reg0=0 priority=99 dl_vlan=30 actions=1,strip_vlan,3,4
table=4 reg0=0 priority=50            actions=1
EOF

Наши правила основаны на стандартном поведении OpenFlow, при котором выходное правило не отправляет пакет в тот порт, из которого он пришел.Так, если пакет пришел с порта p1, и мы запомнили порт назначения тоже как p1, то нам надо указать в наших действиях параметр actions=1, в этом случае коммутатор не будет отправлять пакет в порт, из которого он пришел. Многоадресные, широковещательные и одноадресные пакеты также подчиняются этому правилу.

Тестируем таблицу 4


Пример 1


Проследим путь пакета, который пришел на порт p1 с VLAN 30:

$ ovs-appctl ofproto/trace br0 in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=30

Самое интересное мы увидим в последней строке вывода команды, в которой коммутатор удаляет заголовок 802.1Q и затем отправляет пакет в порты p3 и p4, которые являются выходными портами для VLAN:

Datapath actions: pop_vlan,3,4

То же самое случится, если мы проследим путь широковещательного пакета, отправленного на порт p3:

$ ovs-appctl ofproto/trace br0 in_port=3,dl_dst=ff:ff:ff:ff:ff:ff

тут мы видим, что пакет выходит из порта p1 с меткой 802.1Q и из порта p4 без нее.

Datapath actions: push_vlan(vid=30,pcp=0),1,pop_vlan,4

Open vSwitch мог бы упростить эту запись до 4,push_vlan(vid=30,pcp=0), но он недостаточно умен для этого

Следующие команды тоже посылают широковещательные пакеты, которые отбрасываются, потому что такой VLAN может существовать только на порте p1.

$ ovs-appctl ofproto/trace br0 in_port=1,dl_dst=ff:ff:ff:ff:ff:ff
$ ovs-appctl ofproto/trace br0 in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=55

Попробуйте послать несколько широковещательных пакетов сами:

$ ovs-appctl ofproto/trace br0 in_port=1,dl_dst=ff:ff:ff:ff:ff:ff,dl_vlan=20
$ ovs-appctl ofproto/trace br0 in_port=2,dl_dst=ff:ff:ff:ff:ff:ff
$ ovs-appctl ofproto/trace br0 in_port=4,dl_dst=ff:ff:ff:ff:ff:ff

Такое же поведение можно наблюдать с многоадресными и одноадресными пакетами, чей адрес получателя на может быть запомнен:

$ ovs-appctl ofproto/trace br0 in_port=4,dl_dst=01:00:00:00:00:00
$ ovs-appctl ofproto/trace br0 in_port=1,dl_dst=90:12:34:56:78:90,dl_vlan=20
$ ovs-appctl ofproto/trace br0 in_port=1,dl_dst=90:12:34:56:78:90,dl_vlan=30

Пример 2: запоминание MAC-адресов


Давайте поступим так же, как мы делали в случае таблицы 3. Для начала запомним MAC-адрес на порте p1 для VLAN 30

$ ovs-appctl ofproto/trace br0 in_port=1,dl_vlan=30,dl_src=10:00:00:00:00:01,dl_dst=20:00:00:00:00:01 -generate

В последней строке вывода вы увидите, что адрес получателя пакета неизвестен, так что он будет отправлен на порты p3 и p4, на которых существует VLAN 30

Datapath actions: pop_vlan,3,4

Затем поменяем местами MAC-адреса отправителя и получателя и запомним адрес получателя на порте p4

$ ovs-appctl ofproto/trace br0 in_port=4,dl_src=20:00:00:00:00:01,dl_dst=10:00:00:00:00:01 -generate

Последняя строка вывода команды показывает, что порт назначения для этого пакета — p1, что является результатом выполнения предыдущей команды

Datapath actions: push_vlan(vid=30,pcp=0),1

Запустим нашу первую команду еще раз:

$ ovs-appctl ofproto/trace br0     in_port=1,dl_vlan=30,dl_src=10:00:00:00:00:01,dl_dst=20:00:00:00:00:01 -generate

Мы увидим, что ее результатом будет не широковещательная рассылка, а отправка пакета только в один порт назначения — p4.

Datapath actions: pop_vlan,4

Теперь наш коммутатор готов к использованию.
Поделиться с друзьями
-->

Комментарии (4)


  1. sohmstyle
    05.04.2017 00:05
    +1

    Интересная статья. Спасибо!

    По вашей статье у меня почему-то сложилось ощущение, что OVS и OF не готовы к высоким нагрузкам.
    Но я не специалист по OVS и OF, имел только небольшой опыт работы с ними при работе с OpenStack.

    Помимо OpenStack, где ещё по-вашему мнению стоит использовать OVS и OF?


    1. lovecraft
      06.04.2017 11:23
      +1

      Готов он к продакшену или нет — это философский вопрос, важнее, что его уже применяют в продакшене, даже с багами (уже пофиксили). Кроме того, альтернативы-то нет, только колхоз с ebtables/iptables, который превращается в ад, когда у вас много хостов и VM переезжают с одного хоста на другой автоматически с миграцией сетевых правил.

      По поводу производительности — на скорую руку сделал тест на mininet для одного коммутатора:

      *** Iperf: testing TCP bandwidth between h1 and h2
      *** Results: ['5.57 Gbits/sec', '5.56 Gbits/sec']
      
      

      при 50% загрузке десктопного процессора.

      Благодаря правилам OVS можно гибко настроить, чтобы дропать пакеты от злонамеренных клиентов.
      По поводу применения — OVS спроектирован под системы виртуализации, как VMWare VDS и аналогичный продукт от MS, вряд ли его можно применять где-то за пределами таких сред. OF как протокол поддерживается определенным количеством аппаратных коммутаторов, но бюджетными эти решения не назовешь, плюс надо разбираться с ограничениями, которые накладывает аппаратная платформа. Я, честно говоря, с такой инфраструктурой не сталкивался


  1. Aremka5089
    06.04.2017 09:38
    +1

    Спасибо, хороший материал.


  1. satandyh
    07.04.2017 20:44

    Может я чего-то не понимаю, но вроде бы должно быть так.
    Access port — в 802.1q порт принимающий и передающий только нетегированный трафик, который маркируется в соответствии с строго одним определённым vlan.
    Trunk port — в 802.1q порт принимающий и передающий нетегированный трафик (native vlan) или тегированный трафик, который содержит тэги только определенных vlan (называемых разрешенными на данном порту, т.е. не всех vlan).
    Поправьте определения.