
Меня зовут Борис Хасанов, я сетевой архитектор в MWS Cloud Platform. В этой статье рассмотрим структуру сущности, называемой binapi, возможности программирования VPP с её помощью. Покажем, как можно использовать binapi для конкретных случаев, таких как реализация программного оверлея в ЦОД и расширенного Traffic Engineering при помощи VPP. Также поделимся результатами наших тестов по программированию SR Policy через VPP. Статья рассчитана на подготовленных читателей. Желающих узнать или освежить в памяти, что такое SR Policy, адресую к своей прошлой статье.
Хочу выразить глубокую признательность коллегам-соавторам из MWS, много помогавшим мне при создании этой статьи, — это Руслан Ибрагимов, Артём Глазычев, Михаил Соколовский и Павел Ткач. Также большое спасибо многим другим коллегам из MWS, кто так или иначе помогал мне с этой статьей.
Напоминание про VPP
Есть огромное количество публичной документации по VPP (Vector Packet Processing — векторная обработка пакетов). Поэтому в этой статье дадим лишь необходимый минимум, нужный для дальнейшего понимания особенностей его использования и программирования.
VPP реализует программную векторную обработку пакетов (вектор — массив). Векторная обработка пакетов (подробнее см. тут) — это метод параллельной обработки нескольких пакетов с использованием следующих оптимизаций на уровне кода:
Параллельная обработка нескольких пакетов в одном цикле (например, quad-loop для 4 пакетов). Это увеличивает IPC (instructions per cycle) за счёт параллельного выполнения независимых операций.
Предвыборка данных для следующих пакетов во время обработки текущего, что уменьшает простои из-за промахов кеша.
Использование встраиваемых (inline) функций для избежания накладных расходов на вызовы.
Схожие механизмы применяются и в специализированных сетевых процессорах (NPU), но только в виде аппаратных конвейеров (pipeline).
VPP работает на принципе «графа обработки пакетов». Это даёт разработчикам возможность добавлять в граф новые узлы для обработки посредством плагинов, что обеспечивает гибкость и расширяемость.
В процессе работы VPP собирает все доступные пакеты из кольцевых буферов интерфейсов, формируя вектор пакетов. Затем применяется граф обработки, где каждый узел последовательно обрабатывает весь вектор пакетов.
Плагины могут:
Добавлять новые узлы графа;
Изменять структуру графа обработки пакетов;
Разрабатываться независимо от исходного кода VPP (как самостоятельные компоненты).
Другим ключевым преимуществом VPP является высокая производительность и поддержка DPDK.
Использование VPP в MWS
В MWS мы используем VPP для разных задач, одна из которых — создание оверлейной сети на базе SRv6 для публичного облака MWS Cloud Platform. Подробнее про неё рассказывали мои коллеги в 6-м эпизоде реалити для разработчиков Building the Cloud.
Опишем коротко механизм работы этого оверлея на примере двух ВМ. Итак, сетевой контроллер (CPL) через специальный API отправляет на сетевой агент на хосте параметры SR Policy, описывающей инструкции для форвардинга трафика от одной ВМ к другой: Binding SID, Endpoint, Segment List (далее в тексте буду часто использовать сокращение — SL), состоящий из одного END.SID удалённой ВМ. Сетевой агент затем передаёт SR Policy (программирует) VPP через binapi, про который речь пойдёт ниже. Этот процесс показан на рис. 1. Кроме того, отмечу, что CPL также программирует настройки VPN и сами пользовательские префиксы в VPN: он передаёт их сетевому агенту, а тот в свою очередь в VPP.

Традиционно для программирования SR Policy используются протоколы PCEP и BGP SR (они подробно описаны в моей статье на Хабре, см. ссылку выше). Кроме того, в индустрии предложен подход по использованию gRPC для этой цели (см. draft-ali-spring-sr-policy-programming-rpc). VPP же имеет собственный API, используемый в том числе и для программирования SR Policy, про который мы поговорим далее.
Почему же собственный binapi в VPP так важен? Для SR Policy, в частности, он важен тем, что протоколы PCEP и BGP SR работают с control plane маршрутизатора, а VPP работает в плоскости форвардинга, т. е. по определению не может их поддерживать для динамической конфигурации SR Policy. Также binapi даёт возможность динамической (программной) конфигурации многих других функций на основе плагинов, о чём поговорим далее.
Но для начала давайте рассмотрим API VPP.
Краткое описание API VPP
VPP имеет API-модуль, позволяющий общаться с VPP через интерфейс разделяемой памяти (сокет), состоящий из трёх частей:
Общий код (common code) — низкоуровневый API.
Сгенерированный код (generated code) — высокоуровневый API (абстракции).
Генератор кода (code generator) — позволяет создавать собственный высокоуровневый API, например для кастомных плагинов.
API работает в двух режимах:
Блокирующий. При запуске операции выполнение кода приостанавливается до её завершения;
Неблокирующий. Операции разделены: каждый узел самостоятельно обрабатывает вектор пакетов.
API VPP — бинарный, что добавляет множество преимуществ и вызывает определённые ассоциации с gRPC (их будет ещё больше, когда мы рассмотрим сообщения API).
Разработчиками предложено несколько практических реализаций binapi на языках C, C++, Go. Далее сфокусируемся на Go (Go API или go-vpp на рис. 2):

Типы сообщений в binapi
Рассмотрим основные сообщения binapi. Видно явное сходство с gRPC (я сделал верхнеуровневое описание gRPC тут).
Request (запрос) — одиночный запрос на получение единичной информации;
Dump — запрос на получение множественной информации (серии объектов);
Detail — предоставление детальной информации по какому-то объекту;
Subs — подписка на асинхронные события (напрашивается сравнение с rpc:Subscribe в gNMI);
Ответы на запросы (в т. ч. events — как асинхронные события в подписке).
Логику взаимодействия сообщения я показал на рис. 3

Цель исследования
Мы поставили задачу провести исследование возможности расширенного использования SR Policy в MWS, а именно:
Провижининга нескольких SID в SL, чтобы иметь возможность отправлять трафик определённых сервисов/пользователей не кратчайшим (с точки зрения BGP Best-Path алгоритма), а каким-то иным путём;
Провижининга в SR Policy (правильнее будет сказать — в Candidate Path заданной SR Policy: см. ссылки на мою презентацию и статью выше для деталей) нескольких SL с разными весами для последующей взвешенной балансировки (wECMP).
Подготовка и запуск окружения
В MWS используется свой форк VPP c некоторыми модификациями. Для ускорения развёртывания коллеги сделали скрипт gen.sh, который:
устанавливает переменные окружения: входные и выходные папки, адрес репозитория VPP, необходимые плагины для генерации binapi (из внешнего файла — plugins.txt);
клонирует репозиторий vpp (с гита либо из локальной копии исходника);
запускает генератор binapi;
удаляет из сгенерированного лишние плагины (не указанные в plugins.txt);
удаляет исходник vpp.
Так выглядит код gen.sh
$ cat go/src/srv6-vpp/scripts/gen.sh
#!/bin/bash
# scripts/govpp/gen.sh
# Enable debugging and exit immediately if a command exits with a non-zero status
set -euxo pipefail
# Specify source and destination paths
INPUT_DIR="/usr/share/vpp/api"
# В OUTPUT_DIR будет Гошный код для SR API
OUTPUT_DIR="./vpp/binapi/"
VPP_REPO="git@git.mws-team.ru:mws/network/FDio/vpp.git"
# Define an array of desired plugins
DESIRED_PLUGINS=$(cat scripts/plugins.txt)
# Function to clean previously generated clients
clean_output_dir() {
echo "Cleaning previously generated clients..."
rm -rf ${OUTPUT_DIR}
}
# Function to clone the VPP repository and generate govpp binapi
clone_and_generate() {
echo "Cloning VPP repository and generating govpp binapi..."
# git clone ${VPP_REPO}
binapi-generator --input-dir=${INPUT_DIR} --output-dir=${OUTPUT_DIR}
}
# Function to remove unused generated code
remove_unused_generated_code() {
echo "Removing unused generated code..."
TMP_DIR=$(mktemp -d)
for PLUGIN in ${DESIRED_PLUGINS[@]}; do
if [ -d ${OUTPUT_DIR}${PLUGIN} ]; then
mv ${OUTPUT_DIR}${PLUGIN} ${TMP_DIR}
fi
done
rm -rf ${OUTPUT_DIR}*/
mv ${TMP_DIR}/* ${OUTPUT_DIR}
rmdir ${TMP_DIR}
}
# Function to remove the VPP repository
clean_vpp_repo() {
echo "Removing VPP repository..."
rm -rf ${INPUT_DIR}
}
# Main process
main() {
clean_output_dir
clone_and_generate
remove_unused_generated_code
# clean_vpp_repo
echo "Done!"
}
# Call the main function
main
Список плагинов, для которых будут сгенерированы binapi, задаётся в специальном файле plugins.txt. Ниже показан пример нужных мне плагинов для проверки SR Policy.
bhassanov@cali-11:~$ cat go/src/srv6-vpp/scripts/plugins.txt
interface
interface_types
ip
ip_types
sr
Для корректной работы binapi на Go требуется версия компилятора не ниже 1.23.
Gen.sh требует иметь уже установленный генератор binapi, который, в свою очередь, из забирает из репозитория vpp специальные json-файлы с описанием API, разбирает их через питоновский пакет ply и генерирует шаблонные клиенты на Go для работы с этим API.
При запуске генератора binapi у меня возникла странная ошибка про невозможность найти файл:
:~/go/src/srv6$ go install go.fd.io/govpp/cmd/binapi-generator@latest
go: downloading github.com/sirupsen/logrus v1.9.3
go: downloading github.com/spf13/pflag v1.0.6
go: downloading golang.org/x/text v0.24.0
go: downloading github.com/bennyscetbun/jsongo v1.1.2
go: downloading golang.org/x/sys v0.31.0
# go.fd.io/govpp/cmd/binapi-generator
/usr/local/go/pkg/tool/linux_amd64/link: running gcc failed: exit status 1
/usr/bin/gcc -m64 -Wl,-z,now -Wl,-z,nocopyreloc -o $WORK/b001/exe/a.out -rdynamic /tmp/go-link-2754728408/go.o /tmp/go-link-2754728408/000000.o /tmp/go-link-2754728408/000001.o /tmp/go-link-2754728408/000002.o /tmp/go-link-2754728408/000003.o /tmp/go-link-2754728408/000004.o /tmp/go-link-2754728408/000005.o /tmp/go-link-2754728408/000006.o /tmp/go-link-2754728408/000007.o /tmp/go-link-2754728408/000008.o /tmp/go-link-2754728408/000009.o /tmp/go-link-2754728408/000010.o /tmp/go-link-2754728408/000011.o /tmp/go-link-2754728408/000012.o /tmp/go-link-2754728408/000013.o /tmp/go-link-2754728408/000014.o /tmp/go-link-2754728408/000015.o /tmp/go-link-2754728408/000016.o /tmp/go-link-2754728408/000017.o /tmp/go-link-2754728408/000018.o /tmp/go-link-2754728408/000019.o /tmp/go-link-2754728408/000020.o /tmp/go-link-2754728408/000021.o /tmp/go-link-2754728408/000022.o /tmp/go-link-2754728408/000023.o /tmp/go-link-2754728408/000024.o -O2 -g -lresolv -O2 -g -ldl -O2 -g -lpthread -O2 -g
/usr/bin/ld: cannot find crtbeginS.o: No such file or directory
collect2: error: ld returned 1 exit status
Проблему удалось преодолеть таким образом:
:~/go/src/srv6$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin:/usr/local/go/bin
bhassanov@cali-11:~/go/src/srv6$ find /usr/lib -name "crtbeginS.o"
/usr/lib/gcc/x86_64-linux-gnu/12/crtbeginS.o
:~/go/src/srv6$ export LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12:$LIBRARY_PATH <== вот и решение
~/go/src/srv6$ go install go.fd.io/govpp/cmd/binapi-generator@v0.12.0
~/go/src/srv6$
Далее для запуска gen.sh необходимо создать виртуальное Python-окружение, активировать его и проинсталлировать пакет ply:
:~/go/src/srv6$ mkdir -p .venv
~/go/src/srv6$ python3 -m venv .venv
The virtual environment was not created successfully because ensurepip is not
available. On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.
apt install python3.10-venv
You may need to use sudo with that command. After installing the python3-venv
package, recreate your virtual environment.
Failing command: /home/bhassanov/go/src/srv6/.venv/bin/python3
:~/go/src/srv6$ sudo apt install python3.10-venv
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
[SNIP]
:~/go/src/srv6$ python3 -m venv .venv
~/go/src/srv6$ . .venv/bin/activate
(.venv) :~/go/src/srv6$ pip3 install ply
Collecting ply
Downloading ply-3.11-py2.py3-none-any.whl (49 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 49.6/49.6 KB 732.2 kB/s eta 0:00:00
Installing collected packages: ply
Successfully installed ply-3.11
Далее, наконец, запускаем gen.sh:
(.venv) :~/go/src/srv6$ ./scripts/gen.sh
+ INPUT_DIR=./vpp/
+ OUTPUT_DIR=./vpp/binapi/
+ VPP_REPO=git@git.mws-team.ru:mws/network/FDio/vpp.git
++ cat scripts/plugins.txt
+ DESIRED_PLUGINS='interface
interface_types
ip
ip_typess
sr
sr_types'
+ main
+ clean_output_dir
+ echo 'Cleaning previously generated clients...'
Cleaning previously generated clients...
+ rm -rf ./vpp/binapi/
+ clone_and_generate
+ echo 'Cloning VPP repository and generating govpp binapi...'
Cloning VPP repository and generating govpp binapi...
+ binapi-generator --input=./vpp/ --output-dir=./vpp/binapi/
INFO[0001] Generating 258 files
+ remove_unused_generated_code
+ echo 'Removing unused generated code...'
Removing unused generated code...
++ mktemp -d
+ TMP_DIR=/tmp/tmp.LzgPWLRRWN
+ for PLUGIN in ${DESIRED_PLUGINS[@]}
[SNIP]
+ mv /tmp/tmp.LzgPWLRRWN/interface /tmp/tmp.LzgPWLRRWN/interface_types /tmp/tmp.LzgPWLRRWN/ip /tmp/tmp.LzgPWLRRWN/sr /tmp/tmp.LzgPWLRRWN/sr_types ./vpp/binapi/
+ rmdir /tmp/tmp.LzgPWLRRWN
+ echo 'Done!'
Done!
(.venv) ~/go/src/srv6$ exit
logout
Ещё одна проблема, с которой я встретился уже при запуске прототипа кода, — это доступ к VPP API сокету:
:~/go/src/srv6-vpp$ export LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/12:$LIBRARY_PATH
~/go/src/srv6-vpp$ go run main.go
panic: initiating vpp connect, socket-path : dial unix /run/vpp/api.sock: connect: permission denied
goroutine 1 [running]:
main.main()
/home/user/go/src/srv6-vpp/main.go:16 +0x19c
exit status 2
Проблема заключалась в отсутствии прав у пользователя (меня, в частности) на доступ, что я быстро поправил:
~/go/src/srv6-vpp$ ls -l /run/vpp/api.sock
srwxrwxr-x 1 root vpp 0 Jun 17 11:48 /run/vpp/api.sock
:~/go/src/srv6-vpp$ sudo usermod -aG vpp $USER
~/go/src/srv6-vpp$ newgrp vpp
:~/go/src/srv6-vpp$ groups
vpp sudo user
~/go/src/srv6-vpp$ go run main.go
[] <nil>
Тестирование провижининга SRv6 Policy в VPP
Для быстрого тестирования я собрал вот такую простейшую топологию:

Из-за нюансов создания тестовых ВМ я не мог полностью отдать сетевой интерфейс ВМ в VPP, т.к. для этого его надо убрать из конфигурации Linux, добавить в конфигурацию VPP и рестартовать последний, поэтому сначала решил использовать tap-интерфейсы, для чего надо отключить dpdk-плагин и включить tap-плагин в конфигурации VPP:
:~$ cat /etc/vpp/startup.conf
unix {
nodaemon
log /var/log/vpp/vpp.log
full-coredump
cli-listen /run/vpp/cli.sock
gid vpp
}
api-trace {
on
}
api-segment {
gid vpp
}
socksvr {
default
}
cpu {
main-core 1
}
plugins {
path /usr/lib/x86_64-linux-gnu/vpp_plugins
plugin dpdk_plugin.so { disable } # Отключаем DPDK
plugin tap_plugin.so { enable } # Включаем поддержку TAP
}
~$
А так выглядит пример конфигурации tap-интерфейса в VPP:
vpp# create tap
tap0
vpp# set interface rx-mode tap0 adaptive
vpp# set interface ip address tap0 2001::1/120
vpp# set interface state tap0 up
vpp# show interface
Name Idx State MTU (L3/IP4/IP6/MPLS) Counter Count
local0 0 down 0/0/0/0
tap0 1 up 9000/0/0/0 tx packets 1
tx bytes 150
vpp# exit
Однако затем я решил пойти другим путём (мне он показался более простым): я сделал хост-интерфейс на VPP с параметрами, идентичными параметрам хостового интерфейса ВМ:
vpp# create host-interface name enp1s0
host-enp1s0
vpp# set interface mac address host-enp1s0 52:54:00:15:7f:57
vpp# set int ip address host-enp1s0 2a02:5501:30:400a::bbb1/64
vpp# set interface state host-enp1s0 up
vpp# ip route add 2a02:5501:30:400a::/64 via 2a02:5501:30:400a::bbb1 host-enp1s0
~$ ping 2a02:5501:30:400a::bbb3
PING 2a02:5501:30:400a::bbb3(2a02:5501:30:400a::bbb3) 56 data bytes
64 bytes from 2a02:5501:30:400a::bbb3: icmp_seq=1 ttl=64 time=0.245 ms
64 bytes from 2a02:5501:30:400a::bbb3: icmp_seq=2 ttl=64 time=0.125 ms
64 bytes from 2a02:5501:30:400a::bbb3: icmp_seq=3 ttl=64 time=0.150 ms
64 bytes from 2a02:5501:30:400a::bbb3: icmp_seq=4 ttl=64 time=0.124 ms
64 bytes from 2a02:5501:30:400a::bbb3: icmp_seq=5 ttl=64 time=0.118 ms
64 bytes from 2a02:5501:30:400a::bbb3: icmp_seq=6 ttl=64 time=0.137 ms
^C
--- 2a02:5501:30:400a::bbb3 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5133ms
rtt min/avg/max/mdev = 0.118/0.149/0.245/0.043 ms
Задачи создания прототипа кода на Go
Предупреждение: я не являюсь разработчиком на языке Go (да и вообще разработчиком ПО), это мой первый эксперимент с этим языком, поэтому это лишь пример возможной реализации без претензий на самый лучший (оптимальный) вариант кода.
Наш прототип должен:
прочитать конфигурацию SR Policy из входного json-файла;
установить связь с VPP;
создать в VPP заданную SR Policy;
проверить результат её создания.
В прототипе мы используем два раздельных Go-модуля: main и srv6.
Srv6.go описывает основные функции (методы) для чтения, создания, модификации и управления SRv6 Policy плюс различные проверки. Например: SrPolicyAdd для добавления новой SRv6-политики использует структуры данных:
SR — основной тип, предоставляющий методы для работы с SRv6;
SRv6Policy — представляет SRv6-политику с BSID и списком сегментов.
А также следующие методы:
NewSR — конструктор для создания нового экземпляра SR;
AddPolicy — добавляет новую SRv6-политику в VPP;
ipToVpp — вспомогательная функция для преобразования IP-адреса в формат VPP.
Основные задачи main.go:
Главный исполняемый модуль для управления SRv6-политиками.
Читает конфигурацию из JSON-файла.
Выполняет операции (добавление, изменение, удаление) с SRv6-политиками.
Main.go использует модуль srv6 как библиотеку для работы с VPP:
Создаёт соединение с VPP через govpp.
Инициализирует клиент SRv6 через srv6.NewSR.
Выполняет операции, используя методы клиента SRv6.
Использует методы модуля srv6, которые в свою очередь вызывают:
SrPolicyAdd (при добавлении политики);
SrPolicyDel (при удалении политики, вызывается через DeleteSrPolicy);
SrPolicyMod (при изменении политики, реализовано как delete + add);
SrPoliciesDump (при проверке существования политики и получении информации).
Основные структуры данных main.go:
SegmentList — список сегментов с весом (для JSON);
Operation — описывает операцию над SRv6-политикой;
Config — контейнер для списка операций.
Ниже приведён пример работы прототипа, который провижинит SRv6 Policy в VPP:
:~/go/src/srv6-vpp/v2$ cat config_spray.json
{
"operations": [
{
"action": "add",
"bsid": "2a02:5501:30:400a::bbb8",
"policy_type": "spray",
"behavior": "encap",
"segment_lists": [
{
"segments": ["2a02:5501:30:400a::bbb3", "2a02:5501:30:400a::bbb4"],
"weight": 30
},
{
"segments": ["2a02:5501:30:400a::bbb3", "2a02:5501:30:400a::bbb4", "2a02:5501:30:400a::bbb3", "2a02:5501:30:400a::bbb4"],
"weight": 70
}
],
"encap_src": "2001:db8::1",
"fib_table": 0
}
]
}
:~/go/src/srv6-vpp/v2$ go run main.go config_spray.json
2025/06/29 20:01:48 Successfully added spray policy (BSID: 2a02:5501:30:400a::bbb8)
~/go/src/srv6-vpp/v2$ sudo vppctl sh sr policies
SR policies:
[0].- BSID: 2a02:5501:30:400a::bbb8
Behavior: Encapsulation
EncapSrcIP: ::
Type: Spray
FIB table: 0
Segment Lists:
[0].- < 2a02:5501:30:400a::bbb3, 2a02:5501:30:400a::bbb4 > weight: 30
[1].- < 2a02:5501:30:400a::bbb3, 2a02:5501:30:400a::bbb4, 2a02:5501:30:400a::bbb3, 2a02:5501:30:400a::bbb4 > weight: 0
-----------
С этим вариантом кода выяснилась проблема, что при попытке запровижинить политику с несколькими Segment Lists при указании веса для wECMP для второго и следующих SL он всегда устанавливался в 0 (см. сниппет кода выше).
При этом через CLI такая операция вполне проходила:
sr policy mod bsid 2a02:5501:30:400a::bbb8 sl 0 weight 30 segments 2a02:5501:30:400a::bbb3 2a02:5501:30:400a::bbb4
sr policy mod bsid 2a02:5501:30:400a::bbb8 sl 2 weight 70 segments 2a02:5501:30:400a::bbb3 2a02:5501:30:400a::bbb4 2a02:5501:30:400a::bbb3 2a02:5501:30:400a::bbb4
Как выяснилось, текущая версия binapi с функцией SrPolicyAdd не позволяет за один «проход» добавлять несколько SL с разными весами и единственный workaround на текущий момент — это:
Создать её с одним SL с заданным весом;
Модифицировать эту политику, добавив следующий SL со своим весом и т. д.
Пришлось модифицировать код прототипа для поддержки такого варианта. Ознакомиться можно по ссылке на публичный репозиторий: https://github.com/BorisH2018/srv6-vpp/tree/master
Как итог, удалось успешно запровижинить такую тестовую SR Policy с SL, с разными весами:
:~/go/src/srv6-vpp/v2$ go run main.go config_spray.json
2025/07/02 07:12:09 Starting policy operation for BSID: 2a02:5501:30:400a::bbb8
2025/07/02 07:12:09 Policy type: default, Encap: true
2025/07/02 07:12:09 Setting encap_src to: 2a02:5501:30:400a::bbb1
2025/07/02 07:12:09 Configured segment lists (note: VPP will sort by weight descending):
2025/07/02 07:12:09 List 0:
2025/07/02 07:12:09 Segments: [2a02:5501:30:400a::bbb3 2a02:5501:30:400a::bbb4]
2025/07/02 07:12:09 Weight: 30
2025/07/02 07:12:09 List 1:
2025/07/02 07:12:09 Segments: [2a02:5501:30:400a::bbb3 2a02:5501:30:400a::bbb4 2a02:5501:30:400a::bbb3 2a02:5501:30:400a::bbb4]
2025/07/02 07:12:09 Weight: 70
2025/07/02 07:12:09 Total weight across all segment lists: 100
2025/07/02 07:12:09 Existing policy found - deleting...
2025/07/02 07:12:09 Policy successfully deleted after 1 attempts
2025/07/02 07:12:09 Current VPP SR state:
2025/07/02 07:12:09 SR policies:
2025/07/02 07:12:09 Creating new SRv6 policy...
2025/07/02 07:12:09 Policy configuration:
{
"BSID": "2a02:5501:30:400a::bbb8",
"SegmentLists": [
{
"Segments": [
"2a02:5501:30:400a::bbb3",
"2a02:5501:30:400a::bbb4"
],
"Weight": 30
},
{
"Segments": [
"2a02:5501:30:400a::bbb3",
"2a02:5501:30:400a::bbb4",
"2a02:5501:30:400a::bbb3",
"2a02:5501:30:400a::bbb4"
],
"Weight": 70
}
],
"EncapSrc": "2a02:5501:30:400a::bbb1",
"FibTable": 0,
"IsEncap": true,
"PolicyType": "default"
}
2025/07/02 07:12:10 Policy created in 200.84536ms
2025/07/02 07:12:10 Performing API verification...
2025/07/02 07:12:10 API verification successful
2025/07/02 07:12:10 BSID: 2a02:5501:30:400a::bbb8
2025/07/02 07:12:10 IsEncap: true
2025/07/02 07:12:10 Total weight: 100
2025/07/02 07:12:10 Segment lists count: 2
2025/07/02 07:12:10 Performing CLI verification...
2025/07/02 07:12:10 VPP CLI output:
SR policies:
[0].- BSID: 2a02:5501:30:400a::bbb8
Behavior: Encapsulation
EncapSrcIP: 2a02:5501:30:400a::bbb1
Type: Default
FIB table: 0
Segment Lists:
[0].- < 2a02:5501:30:400a::bbb3, 2a02:5501:30:400a::bbb4, 2a02:5501:30:400a::bbb3, 2a02:5501:30:400a::bbb4 > weight: 70
[1].- < 2a02:5501:30:400a::bbb3, 2a02:5501:30:400a::bbb4 > weight: 30
-----------
2025/07/02 07:12:10 Policy type 'default' verified
2025/07/02 07:12:10 CLI verification: all critical parameters confirmed
2025/07/02 07:12:10 Successfully added default policy (BSID: 2a02:5501:30:400a::bbb8)
Выводы
В этом небольшом исследовании удалось разобраться с принципом динамического программирования VPP через binapi и провести успешное исследование двух ключевых вопросов:
Возможности провижинить в VPP SR Policy c SL с более чем одним SID (вплоть до ограничения по MSD);
Возможности провижинить в VPP SR Policy c Multiple SLs с различными весами для wECMP.
Данный вариант позволит более эффективно управлять трафиком между VM и уйти от зависимости от кратчайшего пути между VM для определённых пользователей или сервисов, т. е. иметь возможность организовывать более эффективный Traffic Engineering.
Надеюсь, эта статья поможет вам быстрее освоиться с binapi и эффективнее использовать возможности VPP в своих инфраструктурных решениях.
Более подробно про использование SRv6 в MWS рассказал на IETF-123. Посмотреть можно по ссылке.
Читайте интересные статьи про MWS Cloud Platform:
Gosha04ye
SR Policy тут расписан понятно, но в реальных сетях редко встретишь полноценное внедрение. Упирается в поддержку железа и человеческий фактор.