SRv6. Часть 2.5. Контроллер для TE на BGP-LS.

КПЗ “контроллер трафика
Тут часть 1: https://likeabus.ru/manualstory/2026/09/02/srv6-frr-lab.html
Тут часть 2: https://likeabus.ru/manualstory/2026/09/06/srv6-frr-lab-te.html
Это будет часть 2.5, за полноценную третью не могу выдать.
Идея этой части в том, чтобы к текущей лабе с TE, добавить контроллер. Он при этом должен собирать инфу с сети через BGP-LS и дальше на её основе уметь управлять. Усложнять как-то сам путь трафика не хочу, по крайней мере в этой лабе, поэтому решил оставить ту же самую топологию, с теми же самыми двумя CE и одним VRF. Вот так выглядит:
Контроллер - такой же FRR, в таком же контейнере, в том же containerlab. На основе всей собранной инфы, он занимается тем, что через docker socket отдаёт команды в контейнеры с нодами, ну не для прода история очевидно. Лаба всё-таки.
Что ещё… А, так вот, контроллер должен ещё иметь какой-то интерфейс, через который собственно и будем им управлять.
Вот это вот всё, плюс все свои предыдущие наработки - я и скормил клоду. Он сожрал все мои токены. Я подождал ещё 5 часов и опять скормил, тут то он и выдал результат.
Вышло всё ровно так, как я и описал выше. На 8080 порту локалхоста поднялся сервис и в браузере это выглядит вот так:

Всё максимально просто и понятно, тыкаешь себе на рёбра, вводишь маршрут, выбираешь те PE куда добавить ну и коммитишь.
Всё это формируется в виде пресловутых команд ‘vtysh ляляля’ и сгружается на ноду.
Давайте покажу по-быстрому.
Сначала всё прибил что было:
likeabus@DESKTOP-BEF0PVR:~$ cd ~/srv6-frr-lab && sudo containerlab destroy -t te/srv6-lab2.clab.yml
[sudo] password for likeabus:
10:15:34 INFO Parsing & checking topology file=srv6-lab2.clab.yml
10:15:34 INFO Destroying lab name=srv6-lab2
Заодно и вообще пересобрал весь проект:
likeabus@DESKTOP-BEF0PVR:~$ cd ~ && rm -rf srv6-demo && git clone https://github.com/like-a-bus/srv6-frr-lab srv6-demo
Cloning into 'srv6-demo'...
remote: Enumerating objects: 164, done.
remote: Counting objects: 100% (164/164), done.
remote: Compressing objects: 100% (108/108), done.
remote: Total 164 (delta 74), reused 125 (delta 49), pack-reused 0 (from 0)
Receiving objects: 100% (164/164), 237.00 KiB | 2.21 MiB/s, done.
Resolving deltas: 100% (74/74), done.
likeabus@DESKTOP-BEF0PVR:~$
и запускаем лабу:
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$ bash te/setup.sh
==> Проверяю окружение
ядро поддерживает SRv6
==> Поднимаю лабу — 10 узлов, это дольше, чем в первой
10:33:35 INFO Containerlab started version=0.79.0
...
==> Жду сходимости IS-IS
pe1 видит локаторы остальных PE
==> Переустанавливаю адреса линков, чтобы TED собрала рёбра
==> Жду рёбер в TED
24 ребра за 12 с, топология для BGP-LS полная
==> Перерегистрирую BGP-LS на рефлекторах, чтобы bgpd забрал топологию
p1: 80 NLRI, попытка 1
p3: 80 NLRI, попытка 1
==> Жду топологию на ctrl
ctrl получил по 80 NLRI от p1 и p3
==> Жду сессии BGP до рефлекторов
все 8 сессий на месте
==> Пересобираю сессии с CE, чтобы экспорт в VPN отработал наверняка
==> Жду маршруты CE в VRF на дальней стороне
==> Рефлектор P1: маршруты VPNv4, при том что ни одной VRF у него нет
...
PING 10.2.0.1 (10.2.0.1) from 10.1.0.1: 56 data bytes
64 bytes from 10.2.0.1: seq=0 ttl=63 time=0.307 ms
64 bytes from 10.2.0.1: seq=1 ttl=63 time=0.267 ms
64 bytes from 10.2.0.1: seq=2 ttl=63 time=0.119 ms
--- 10.2.0.1 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.119/0.231/0.307 ms
...
Готово. 10 узлов, 8 iBGP к рефлекторам, 4 eBGP до CE, трафик через SRv6.
Погасить: sudo containerlab destroy -t srv6-lab2.clab.yml
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
Можно идти смотреть, что там у нас в вебке:

Все рёбра отрисовались, причем видно, что сейчас нет никаких TE условий, т.к. лаба свежая.
На всякий проверим с самих PEшек, так ли оно.. Вывод PE1/PE2/PE3/PE4
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$ for n in 1 2 3 4; do echo "== pe$n"; sudo docker exec clab-srv6-lab2-pe$n ip route show vrf RED 10.2.0.1/32; done
== pe1
10.2.0.1 nhid 40 proto bgp metric 20
nexthop encap seg6 mode encap segs 1 [ fc00:13:0:0:1:: ] via 172.20.20.1 dev eth0 weight 1
nexthop encap seg6 mode encap segs 1 [ fc00:14:0:0:1:: ] via 172.20.20.1 dev eth0 weight 1
== pe2
10.2.0.1 nhid 39 proto bgp metric 20
nexthop encap seg6 mode encap segs 1 [ fc00:13:0:0:1:: ] via 172.20.20.1 dev eth0 weight 1
nexthop encap seg6 mode encap segs 1 [ fc00:14:0:0:1:: ] via 172.20.20.1 dev eth0 weight 1
== pe3
10.2.0.1 nhid 29 via 192.168.23.1 dev eth3 proto bgp metric 20
== pe4
10.2.0.1 nhid 29 via 192.168.24.1 dev eth3 proto bgp metric 20
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$ for n in 1 2 3 4; do echo "== pe$n"; sudo docker exec clab-srv6-lab2-pe$n ip route show vrf RED 10.1.0.1/32; done
== pe1
10.1.0.1 nhid 30 via 192.168.11.1 dev eth3 proto bgp metric 20
== pe2
10.1.0.1 nhid 29 via 192.168.12.1 dev eth3 proto bgp metric 20
== pe3
10.1.0.1 nhid 40 proto bgp metric 20
nexthop encap seg6 mode encap segs 1 [ fc00:11:0:0:1:: ] via 172.20.20.1 dev eth0 weight 1
nexthop encap seg6 mode encap segs 1 [ fc00:12:0:0:1:: ] via 172.20.20.1 dev eth0 weight 1
== pe4
10.1.0.1 nhid 39 proto bgp metric 20
nexthop encap seg6 mode encap segs 1 [ fc00:11:0:0:1:: ] via 172.20.20.1 dev eth0 weight 1
nexthop encap seg6 mode encap segs 1 [ fc00:12:0:0:1:: ] via 172.20.20.1 dev eth0 weight 1
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
Ну да, никаким TE и не пахнет. Пробуем добавлять теперь маршрут с какими-то опциями. Я накликал так, чтобы трафик обошёл вообще P3. В обе стороны.
Путь до 10.2.0.1 через PE1 и PE2
Путь до 10.1.0.1 через PE3
Путь до 10.1.0.1 через PE4
То есть для PE1 и PE2 можно не делать уникальных описаний, главное чтобы трафик не попал на P3 уже находясь внутри ядра, а вот с PE3 и PE4 так не выйдет, т.к. они к нему подключены напрямую и ECMP без проблем может разложить, поэтому надо для каждого по отдельности описать, ну выше картинки собственно об этом.
Ну пойдём проверять :) Сначала просто пустим пакетики, идут вообще или нет:
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$ sudo docker exec clab-srv6-lab2-ce1 ping -c 5 -I 10.1.0.1 10.2.0.1
PING 10.2.0.1 (10.2.0.1) from 10.1.0.1: 56 data bytes
64 bytes from 10.2.0.1: seq=0 ttl=63 time=0.295 ms
64 bytes from 10.2.0.1: seq=1 ttl=63 time=0.162 ms
64 bytes from 10.2.0.1: seq=2 ttl=63 time=0.187 ms
64 bytes from 10.2.0.1: seq=3 ttl=63 time=0.175 ms
64 bytes from 10.2.0.1: seq=4 ttl=63 time=0.157 ms
--- 10.2.0.1 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.157/0.195/0.295 ms
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
Работает. Теперь можно собрать с PE инфу, чтобы посмотреть что там запрограммировалось:
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$ for n in 1 2 3 4; do echo "== pe$n"; sudo docker exec clab-srv6-lab2-pe$n ip route show vrf RED 10.2.0.1/32; done
== pe1
10.2.0.1 nhid 62 encap seg6 mode encap segs 4 [ fc00:1:0:0:2:: fc00:2:0:0:4:: fc00:4:0:0:4:: fc00:14:0:0:1:: ] dev eth3 proto 196 metric 20
== pe2
10.2.0.1 nhid 61 encap seg6 mode encap segs 4 [ fc00:1:0:0:2:: fc00:2:0:0:4:: fc00:4:0:0:4:: fc00:14:0:0:1:: ] dev eth3 proto 196 metric 20
== pe3
10.2.0.1 nhid 29 via 192.168.23.1 dev eth3 proto bgp metric 20
== pe4
10.2.0.1 nhid 29 via 192.168.24.1 dev eth3 proto bgp metric 20
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$ for n in 1 2 3 4; do echo "== pe$n"; sudo docker exec clab-srv6-lab2-pe$n ip route show vrf RED 10.1.0.1/32; done
== pe1
10.1.0.1 nhid 30 via 192.168.11.1 dev eth3 proto bgp metric 20
== pe2
10.1.0.1 nhid 29 via 192.168.12.1 dev eth3 proto bgp metric 20
== pe3
10.1.0.1 nhid 57 encap seg6 mode encap segs 5 [ fc00:13:0:0:2:: fc00:4:0:0:3:: fc00:2:0:0:2:: fc00:1:0:0:1:: fc00:12:0:0:1:: ] dev eth3 proto 196 metric 20
== pe4
10.1.0.1 nhid 51 encap seg6 mode encap segs 5 [ fc00:4:0:0:3:: fc00:2:0:0:2:: fc00:1:0:0:1:: fc00:14:0:0:3:: fc00:12:0:0:1:: ] dev eth3 proto 196 metric 20
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
Ага, ну вот видно на PE1/2 у нас в сторону 10.2.0.1 больше не балансируется и имеет несколько записей внутри seglist, а для PE3/4 такое же, но для префикса 10.1.0.1, т.е. в обратную сторону. P3 не должен вообще участвовать в обработке трафика.
Ну го соберём дамп и убедимся наверняка. Для этого ещё во второй части попросил дописать в скриптец capture.sh штуку, которая собирает трафик со всех интерфейсов в топологии, но если трафика не было, то удаляет этот pcap, т.е. p3 не должно быть вообще, ну и плюс по хопам можно поизучать:
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$ ls -l te/pcap/ | grep p3
-rw-r--r-- 1 tcpdump tcpdump 1234 Sep 12 15:06 p3-eth2.pcap
-rw-r--r-- 1 tcpdump tcpdump 1234 Sep 12 15:06 p3-eth3.pcap
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$
хм, а дамп то есть, т.е. трафик через них прошёл… ладно, придётся посмотреть детальнее
сначала проверил, что всё-таки в одну сторону идёт верно:
pe2-eth1 segleft=3 -> fc00:1:0:0:2:: (End.X p1 → p2)
p1-eth4 segleft=2 -> fc00:2:0:0:4:: (End.X p2 → p4)
p2-eth3 segleft=1 -> fc00:4:0:0:4:: (End.X p4 → pe4)
p4-eth2 segleft=0 -> fc00:14:0:0:1:: (DT4 на pe4)
тут всё норм, pe1/2 > p1 > p2 > p4 > pe4 > ce2, как и ожидалось
теперь смотрим в другую:
pe4-eth2 segleft=4 -> fc00:4:0:0:3:: (End.X p4 → p2)
p4-eth3 segleft=3 -> fc00:2:0:0:2:: (End.X p2 → p1)
p2-eth3 segleft=2 -> fc00:1:0:0:1:: (End.X p1 → p3)
p1-eth3 segleft=1 -> fc00:14:0:0:3:: (End.X pe4 → p4)
p3-eth3 segleft=1 -> fc00:14:0:0:3:: (End.X pe4 → p4 !! вот тут сегмент не поменялся)
pe4-eth1 segleft=1 -> fc00:14:0:0:3:: (вернулись на pe4, здесь сегмент отработает)
p4-eth2 segleft=0 -> fc00:12:0:0:1:: (DT4 на pe2)
p2-eth2 segleft=0 -> fc00:12:0:0:1::
строчка 5, где пакет прошёл через P3, запись в SRH не сменилась, пакет просто по андерлею прошёл, вот нарисовал себе картинку…
Не понятно, пошёл ещё раз смотреть что я там настроил, не должно же быть такого… В общем чуть поковырялся и нашёл :) ща покажу
если посмотреть на настроенный маршрут, то видно что он в целом то верный, НО есть нюансик
Ребро, которое должно стоять первым, стоит последним, ну очевидно что из-за этого и трафик то возвращается обратно по underlay для выполнения условий TE - обязательно, пройти через все сегменты в требуемом порядке.
Почему же он там появился? Да потому что я накликал в кривом порядке… Просто сначала натыкал одну часть, а потом в конце добавил вот этот кусочек, а ведь порядок тут играет ключевую роль)
Короче, удалил этот роут, вставил заново в правильном порядке и повторил все тесты.
че там в дампах:
likeabus@DESKTOP-BEF0PVR:~/srv6-demo$ ls -l te/pcap/ | grep p3
-rw-r--r-- 1 tcpdump tcpdump 443 Sep 13 11:13 p3-eth5.pcap
э, опять..!
пошёл смотреть снова…, но там оказалось BGP-LS только внутри) фильтры в скрипте по ipv6 собирал, вот он и попал сюда
всё норм в общем, нашего трафика нет
дамп туда:
pe2-eth1 segleft=3 -> fc00:1:0:0:2:: (End.X p1 → p2)
p1-eth4 segleft=2 -> fc00:2:0:0:4:: (End.X p2 → p4)
p2-eth3 segleft=1 -> fc00:4:0:0:4:: (End.X p4 → pe4)
p4-eth2 segleft=0 -> fc00:14:0:0:1:: (DT4 на pe4)
дамп обратно:
pe4-eth2 segleft=3 -> fc00:4:0:0:3:: (End.X p4 → p2)
p4-eth3 segleft=2 -> fc00:2:0:0:2:: (End.X p2 → p1)
p2-eth4 segleft=1 -> fc00:1:0:0:1:: (End.X p1 → pe2)
p1-eth2 segleft=0 -> fc00:12:0:0:1:: (DT4 на pe2)
Отлично! Всё как и хотелось.
В итоге:
- добавили контейнер ctrl, на котором подняли контроллер на петоне и его стандартной библиотеке http.server
- подняли на нём же FRR с BGP-LS и запирили с P1/P3
- локально в контейнере ctrl собираем через “vtysh -c” данные и рисуем в виде графа
- после отрисовки через api в docker.sock пихаем команды “ip route …”
- поняли что порядок выбора пути важен :)
- просто приятно провели время (ну я уж точно) :)
В общем если интересно, то всё там же https://github.com/like-a-bus/srv6-frr-lab, попробуйте.
Следить за новыми статьями — в канале.