![](https://habrastorage.org/getpro/habr/upload_files/a8a/296/6c1/a8a2966c1dcf41d06354479b6d1af1fc.png)
В конце 2023 года отечественный вендор B4com Tech анонсировал выпуск маршрутизаторов для провайдерского сегмента IP/MPLS. Среди новых моделей наше внимание привлекли те, которые поддерживают интерфейсы 100G, поскольку они стали стандартом подключения в современных центрах обработки данных (ЦОД).
В модельный ряд входят устройства MR-381 и MR-382, различающиеся интерфейсами, но построенные на одном чипе — Broadcom Qumran2C. Этот чип активно используется в таких маршрутизаторах, как Cisco NCS 5500/5700, Juniper ACX7000 и Edge-Core AGR400.
![B4com MR-381 B4com MR-381](https://habrastorage.org/getpro/habr/upload_files/a37/bbe/a3b/a37bbea3bc6ca11db09d6818abfc4a6b.png)
Программное обеспечение
На текущий момент используется B4comOS. Это операционная система, построенная изначально на OcNOS от IP Infusion, но с 2022 года полностью отделившаяся и развивающаяся самостоятельно.
Ниже — схема с описанием основных модулей, которые используются в ОС.
![Схема архитектуры ПО Схема архитектуры ПО](https://habrastorage.org/getpro/habr/upload_files/7a0/c88/f2e/7a0c88f2eb3aab659ed8cf90a044af72.png)
За время, пока я работаю с оборудованием этого вендора, не раз возникала потребность в подобной картинке. Например, периодически приходится обращаться к модулю HSL, который занимается программированием ASIC'а и, соответственно, может показывать информацию о FIB. Ещё интерес представляет модуль NSM, который отвечает за control-plane. Бывает так, что нужно и в него заглянуть. Поэтому хочется всё-таки не просто вводить какие-то команды и смотреть результаты вывода, а ещё и понимать, что происходит.
Первое наше знакомство с MR
С чипом мы уже знакомы и знаем его возможности. Но вопрос в том, как вендор смог адаптировать ASIC и насколько софт, скажем так, "сырой". В общем, в конце 2023 года мы взяли 2 коробки на тесты. Провели достаточно ограниченное тестирование на предмет использования этих маршрутизаторов в сети одного из наших заказчиков. Смотрели на базовый функционал: IS-IS, LDP, L3VPN (средствами MP-BGP VPNv4). Нагружали до 90 Gbps. Нашли мелкие нюансы, о которых знали заранее по работе с коммутаторами, и быстро их сами поправили. В целом, всё было нормально и без каких-либо подводных камней.
Однако следует понимать, что эти тесты были однобокими и удовлетворяли потребности только одного заказчика. А поскольку у нас их несколько, весной этого года возникла необходимость повторить тесты, но уже с более интересным и комплексным дизайном.
Речь идёт про EVPN-MPLS с Active-Active IRB и Anycast Gateway. Для тех, кто не знаком с этой темой, вот две полезные статьи: Статья 1 и Статья 2.
Тесты EVPN-MPLS
Топология
Наша тестовая среда включает два сегмента: EVPN-VXLAN и EVPN-MPLS.
![Схема среды для тестирования Схема среды для тестирования](https://habrastorage.org/getpro/habr/upload_files/ca0/f1d/78e/ca0f1d78ef15417333186ba56ce3ac2a.png)
Включения между сегментами выполнены для «проброса» двух сервисов: L3 и L2.
Для L3 используется стандартный Option-A, который с обоих сторон передаётся как type-5 EVPN, но на «стыке» разрывается и передаётся как чистый IPv4.
Пример конфигурации Option-A на PE и BL
Border-leaf
router bgp 300
!
address-family ipv4 vrf VRF-A
max-paths ebgp 2
neighbor 1.1.1.0 remote-as 500
neighbor 1.1.1.0 activate
no neighbor 1.1.1.0 send-community both
neighbor 1.1.1.2 remote-as 500
neighbor 1.1.1.2 activate
no neighbor 1.1.1.2 send-community both
neighbor 1.1.1.0 update-source 1.1.1.1
neighbor 1.1.1.2 update-source 1.1.1.3
exit-address-family
!
address-family ipv4 vrf VRF-B
max-paths ebgp 2
neighbor 2.2.2.0 remote-as 500
neighbor 2.2.2.0 activate
no neighbor 2.2.2.0 send-community both
neighbor 2.2.2.2 remote-as 500
neighbor 2.2.2.2 activate
no neighbor 2.2.2.2 send-community both
neighbor 2.2.2.0 update-source 2.2.2.1
neighbor 2.2.2.2 update-source 2.2.2.3
exit-address-family
!
PE
router bgp 500
!
address-family ipv4 vrf VRF-A
max-paths ebgp 2
neighbor 1.1.1.1 remote-as 300
neighbor 1.1.1.1 activate
neighbor 1.1.1.5 remote-as 301
neighbor 1.1.1.5 activate
neighbor 1.1.1.1 update-source 1.1.1.0
neighbor 1.1.1.5 update-source 1.1.1.4
exit-address-family
!
address-family ipv4 vrf VRF-B
max-paths ebgp 2
neighbor 2.2.2.1 remote-as 300
neighbor 2.2.2.1 activate
neighbor 2.2.2.5 remote-as 301
neighbor 2.2.2.5 activate
neighbor 2.2.2.1 update-source 2.2.2.0
neighbor 2.2.2.5 update-source 2.2.2.4
exit-address-family
!
exit
!
L2 включает более сложные сценарии. Оба сегмента используют EVPN-MH (EVPN Multi-Homing), можно назвать back-to-back evpn-mh (аналогично back-to-back vpc). Объединение выполнено в один общий LAG на 4 физических интерфейсах с разными ESI на парах.
Пример конфигурации L2 на PE и BL
Border-leaf
interface ce2
channel-group 1 mode active
lldp-agent
set lldp port-id-tlv if-name
exit
!
interface po1
switchport
load-interval 30
mtu 9216
evpn multi-homed system-mac 0000.1111.bbbb
port-channel load-balance rtag7
PE
interface ce22
channel‑group 1 mode active
lldp‑agent
set lldp port‑id‑tlv if‑name
exit
!
interface po1
switchport
load‑interval 30
mtu 9216
evpn multi‑homed system‑mac 0000.1111.aaaa
!
В стенде у нас 2500 таких растянутых L2-сетей, соответственно, такое же количество IRB-интерфейсов на обоих PE, всё это в режиме Active-Active с Anycast Gateway.
Пример конфигурации сервиса на PE
указываем MAC для AGW
evpn irb-forwarding anycast-gateway-mac 0011.2233.4455
создаём 2500 L2 сервисов
mac vrf vni_1000
rd 20.0.0.1:1000
route-target both 500:1000
...
mac vrf vni_3499
rd 20.0.0.1:3499
route-target both 500:3499
создаём 2500 интерфейсов
interface irb1000
ip vrf forwarding VRF-A
evpn irb-if-forwarding anycast-gateway-mac
ip address 30.0.0.1/24
...
interface irb3499
ip vrf forwarding VRF-A
evpn irb-if-forwarding anycast-gateway-mac
ip address 30.9.195.1/24
создаём 2500 evpn-id
evpn mpls id 1000
host-reachability-protocol evpn-bgp vni_1000
evpn irb irb1000
...
evpn mpls id 3499
host-reachability-protocol evpn-bgp vni_3499
evpn irb irb3499
создаём 2500 l2-subinterface
interface po1.1000 switchport
encapsulation dot1q 1000
rewrite pop
mtu 9216
access-if-evpn
map vpn-id 1000
...
interface po1.3499 switchport
encapsulation dot1q 3499
rewrite pop
mtu 9216
access-if-evpn
map vpn-id 3499
На BL настройка чуть проще, так как не требуется использовать IRB и L2-subinterface, растягиваем чистый L2 по EVPN-VXLAN домену.
Пример конфигурации сервиса на BL
создаём 2500 L2 сервисов
nvo vxlan id 11000 ingress-replication inner-vid-disabled
vxlan host-reachability-protocol evpn-bgp vni_11000
vni-name vni_11000
...
nvo vxlan id 13499 ingress-replication inner-vid-disabled
vxlan host-reachability-protocol evpn-bgp vni_13499
vni-name vni_13499
привязываем 2500 сервисов к целевому порту
nvo vxlan access-if port-vlan po1 1000
map vni-name vni_11000
...
nvo vxlan access-if port-vlan po1 3499
map vni-name vni_13499
Методика тестирования
Для генерации трафика использовался TRex.
В тесте запускались 2500 потоков, каждый с уникальным source IP и dot1q тегом, но общим destination IP. Это обеспечивает достаточную энтропию для балансировки и тестирует все 2500 IRB-интерфейсов, через которые маршрутизируется трафик.
Пример скрипта для генерации трафика
from trex_stl_lib.api import *
import argparse
import os
class STLS1(object):
def __init__(self):
self.mac_counter = 0 # Initialize a counter for generating MAC addresses
def generate_mac(self):
base_mac = [0x10, 0x70, 0x00, 0x00, 0x00, 0x00]
mac_suffix = self.mac_counter * 4096 # Increment by 4096 for each new MAC
for i in range(5, 1, -1):
base_mac[i] = mac_suffix & 0xFF
mac_suffix >>= 8
self.mac_counter += 1
return ':'.join('{:02x}'.format(b) for b in base_mac)
def create_stream(self, vlan_id, src_ip, src_mac, pkt_size=1400):
# Calculate the payload size needed to reach the desired packet size
header_size = len(Ether() / Dot1Q() / IP() / UDP())
payload_size = pkt_size - header_size
payload = 'x' * payload_size
return STLStream(
packet=STLPktBuilder(pkt=Ether(src=src_mac, dst="00:11:22:33:44:55") /
Dot1Q(prio=1, vlan=vlan_id) /
IP(src=src_ip, dst="10.200.0.5") /
UDP(dport=12, sport=1025) / payload),
mode=STLTXCont(pps=10),
# mac_dst_override_mode=STLStreamDstMAC_PKT # another way to explicitly take it
)
def get_streams(self, direction, tunables, **kwargs):
parser = argparse.ArgumentParser(description='Argparser for {}'.format(os.path.basename(__file__)),
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
args = parser.parse_args(tunables)
streams = []
# Loop to generate streams for varying VLAN IDs and source IP addresses
vlan_id = 1000
for i in range(0, 10): # second octet range
for j in range(0, 256): # third octet range
src_ip = f"30.{i}.{j}.10"
src_mac = self.generate_mac()
streams.append(self.create_stream(vlan_id, src_ip, src_mac))
vlan_id += 1 # Increment VLAN ID for each stream
if vlan_id > 1010:
break
if vlan_id > 1010:
break
return streams
# dynamic load - used for trex console or simulator
def register():
return STLS1()
Сервис L2 растянут до хоста, а на PE (Provider Edge) этот сервис терминируется через IRB-интерфейсы. Конфигурации этих интерфейсов были описаны ранее.
Как видно из вышеописанного, мы предполагаем нагрузочное тестирование и пускаем трафик через большой объем сервисов, в данном случае 2500 штук. Это позволяет заранее выявить узкие места и создать качественную архитектуру.
Что касается полосы пропускания, то мы используем сценарий постепенного набора с шагом в 1% от максимума на порту, то есть 1 Gbps. Вначале запускаем трафик на 1 Gbps, который приблизительно равномерно распределяется по всем созданным сервисам, и затем начинаем наращивать объем.
Мы отслеживаем состояние как control plane, так и data plane. С контрольной плоскостью все понятно: достаточно включить логи и наблюдать за активностью используемых протоколов. Они должны быть стабильными и не должны флапать или перестраиваться. В отношении data plane сначала анализируем счетчики на дашборде TRex, и если необходимо, смотрим аналогичную информацию на самих узлах.
Результат тестов
При базовой нагрузке (1 Gbps) проблем не возникает.
DF/Non-DF выбираются корректно. Принцип выбора соответствует RFC7432 и подробно разобран в статьях, которые я приложил выше.
PE1-MR381#sh evpn mpls
EVPN-MPLS Information
=================
Codes: NW - Network Port
AC - Access Port
(u) - Untagged
VPN-ID EVI-Name EVI-Type Type Interface ESI VLAN DF-Status Src-Addr Dst-Addr
_______________________________________________________________________________________________________________________________
1000 ---- L2 NW ---- ---- ---- ---- 20.0.0.1 20.0.0.3
1000 ---- -- AC po1.1000 00:00:00:11:11:aa:aa:00:00:00 ---- DF ---- ----
1001 ---- L2 NW ---- ---- ---- ---- 20.0.0.1 20.0.0.3
1001 ---- -- AC po1.1001 00:00:00:11:11:aa:aa:00:00:00 ---- NON-DF ---- ----
1002 ---- L2 NW ---- ---- ---- ---- 20.0.0.1 20.0.0.3
1002 ---- -- AC po1.1002 00:00:00:11:11:aa:aa:00:00:00 ---- DF ---- ----
.....
3497 ---- L2 NW ---- ---- ---- ---- 20.0.0.1 20.0.0.3
3497 ---- -- AC po1.3497 00:00:00:11:11:aa:aa:00:00:00 ---- NON-DF ---- ----
3498 ---- L2 NW ---- ---- ---- ---- 20.0.0.1 20.0.0.3
3498 ---- -- AC po1.3498 00:00:00:11:11:aa:aa:00:00:00 ---- DF ---- ----
3499 ---- L2 NW ---- ---- ---- ---- 20.0.0.1 20.0.0.3
3499 ---- -- AC po1.3499 00:00:00:11:11:aa:aa:00:00:00 ---- NON-DF ---- ----
Аналогичная история с Aliasing и ESI label.
PE1-MR381#sh evpn mpls label alias
S - Self
R - Remote
ESI PE-IP-ADDRESS TENANT ALIAS-LABEL
=============================================================================================
00:00:00:11:11:aa:aa:00:00:00 20.0.0.1(S) 1000 30
00:00:00:11:11:aa:aa:00:00:00 20.0.0.1(S) 1001 29
00:00:00:11:11:aa:aa:00:00:00 20.0.0.1(S) 1002 28
....
00:00:00:11:11:aa:aa:00:00:00 20.0.0.3(R) 3497 31959
00:00:00:11:11:aa:aa:00:00:00 20.0.0.3(R) 3498 31958
00:00:00:11:11:aa:aa:00:00:00 20.0.0.3(R) 3499 31961
PE1-MR381#sh evpn mpls label esi
S - Self
R - Remote
ESI PE-IP-ADDRESS ESI-LABEL
================================================================
00:00:00:11:11:aa:aa:00:00:00 20.0.0.1(S) 18
00:00:00:11:11:aa:aa:00:00:00 20.0.0.3(R) 18
Продолжая увеличивать нагрузку, мы заметили, что при использовании UDP в качестве заголовков трафик на PE неверно классифицируется и попадает в другую очередь, где в дальнейшем отбрасывается.
![Вывод с MR Вывод с MR](https://habrastorage.org/getpro/habr/upload_files/b91/85f/abf/b9185fabf8613c5ef15c4599999a2e15.png)
При использовании TCP подобной проблемы не возникает, и весь трафик классифицируется корректно.
После общения с вендором и корректировки данного поведения для UDP потоков с помощью патча, мы повторили тесты и успешно достигли целевой нагрузки.
![Вывод с TRex Вывод с TRex](https://habrastorage.org/getpro/habr/upload_files/37f/4ad/7b2/37f4ad7b245a537787522ad6604aa595.png)
Также счетчики на очередях показывают корректные значения.
![Вывод с MR Вывод с MR](https://habrastorage.org/getpro/habr/upload_files/4cb/ee3/785/4cbee378529084ec0c495aa9595fbfb6.png)
Выводы
Этот опыт показал важность комплексного тестирования. Без нагрузочных тестов мы могли бы перейти к следующей фазе и не обратить внимание на особенности реализации, что могло бы привести к проблемам на этапе ПНР или, что еще хуже, на ПСИ. В такой ситуации времени на разбор полетов уже не будет, и придется действовать быстро и решительно. Также мы проверили готовность вендора к быстрому реагированию на выявленные недостатки, что является важным фактором для дальнейшего взаимодействия.
Подписывайтесь на мой канал: https://t.me/like_a_bus_channel. Пишу о тестах, своем опыте и прочем, с чем сталкиваюсь на работе и не только!
eskhaliot
Ждём полноценный hvpls