Что такое DF и зачем он нужен?

Первый сценарий возникновения петли: представим, что удаленный PE маршрутизатор получает от CE BUM трафик (например банальный arp запрос) и рассылает его всем остальным PE-кам (предположим это PE3). Так как два PE маршрутизатора (PE1 и PE2) смотрят в один и тот же сегмент и они оба получают BUM трафик от PE3, то получится, что в этот сегмент прилетят две копии трафика, которые далее начнут петлять по все сети, включая и ядро:

Для борьбы с данным явлением в EVPN для каждого multihomed-сегмента выбирается Designated Forwarder — узел, который ответственный за форвардинг BUM трафика в сторону конкретного сегмента в конкретном влане. Все остальные маршрутизаторы, сколько бы их не было в сегменте, не имею права отправлять BUM трафик в сторону CE маршрутизатора/коммутатора (в данном ESI в данном влане).

Алгоритм выбора DF:

  1. Сначала все PE маршрутизаторы должны понять, сколько еще маршрутизаторов подключено к указанному сегменту и какие у них адреса (лупбеки);

  2. По истечении таймера (по умолчанию 3 секунды) формируется список всех PE маршрутизаторов, участвующих в выборе DF, начиная с наименьшего адреса и заканчивая наибольшим. Список адресов нумеруется с 0-ля;

  3. По формуле V mod N = i высчитывается DF для данного сегмента и влана;

  4. Маршрутизатор, выбранный DF-ом разблокриует передачу BUM трафика в сторону CE, а все остальные non-DF маршрутизаторы продолжают блокировать BUM трафика в сторону CE маршрутизатора/коммутатора в данном сегменте в данном влане.

В теории все просто, но давайте по порядку.

Сначала ответим на вопрос, как маршрутизаторы понимают, кто еще имеет линк в этом же сегмент (и есть ли такие маршрутизаторы вообще)? Для этого и существует маршрут типа 4. Посмотрим на данный маршрут:

[email protected]> show route table vSwitch-eVPN-1.evpn.0 match-prefix *4:6*
vSwitch-eVPN-1.evpn.0: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)

В таблице маршрутизации нашего EVPN инстанса таких маршрутов нет. Дело в том, что в данную таблицу попадают только те маршруты, расширенное community которых соответствует импорту, сконфигурированному в инстансе. Например для vSwitch-eVPN-1 равно:

[email protected]> show configuration routing-instances vSwitch-eVPN-1 vrf-target
target:42000:1;

Получается что у маршрута типа 4 какое то другое, отличное от сконфигурированного на импорт community. Это происходит из-за того, что маршруты в evpn могут генерироваться двумя способами — per-EVI и per-ESI.

per-EVI — этот маршрут сгенерирован определенным инстансом

per-ESI — этот маршрут сгенерирован не каким-то определенным инстансом, а маршрутизатором для всех инсатнсов имеющих линк в данном ESI. Один и тот же сегмент может быть включен в несколько evpn инстанций (к примеру в инстанс EVPN1 добавлен интерфейс ae3.777, а в EVPN2 — ae3.778, хоть это и разные юниты, но ESI конфигурится на весь интерфейс целиком, а значит у данных интерфейсов будет один и тот же ESI, хоть и находятся они в разных EVI).

Маршруты, генерируемые per-EVI должны иметь нативные RT и RD инстанса, из которого эти маршруты анонсируются (либо какие то ещё RT, если они дополнительно навешиваются экспортной политикой — то есть добавляются вручную администраторами). Маршруты типа 2 и 3 всегда генерируются per-EVI, а значит всегда имеют в составе маршрута нативную RD и RT инстанса, из которого данные маршруты анонсируются. А вот для маршрутов, генерируемых per-ESI все несколько сложнее и зависит от типа маршрута.

Но продолжим разбираться с маршрутом типа 4.

Данный маршрут всегда генерируется per-ESI. RD у данного маршрута будет сгенерировано автоматически маршрутизатором из router-id (если указан) или адреса лупбека (так как ни к одной из EVPN инстанций данный маршрут собственно и не относится). RT также не указывается в ручную, а генерируется из ESI и только те маршрутизаторы, имеющие один и тот же ESI импортируют данный маршрут. Именно поэтому в выводе, рассмотренном раннее в начале статьи не мы не видим, что multihomed сосед анонсирует нам маршрут типа 4.

Примечание: вообще то не совсем так, так как для RT берется только часть битов ESI и импортировать данный маршрут будут все PE-ки, имеющие одинаковые биты ESI, используемые для генерации RT.

Примечание: JunOS генерирует RD для маршрутов per-ESI используя нулевое значение во второй части RD: 62.0.0.1:0.

Так где же искать наш маршрут типа 4? В JunOS существуют несколько таблиц маршрутизации. Маршруты EVPN сначала попадают в таблицу bgp.evpn.0 и из нее уже импортируются в какие то другие таблицы маршрутизации (secondary tables). Поэтому данный маршрут можно найти в таблице bgp.evpn.0, из которой он экспортируется в таблицу __default_evpn__.evpn.0:

[email protected]> show route table __default_evpn__.evpn.0 match-prefix *4:6*
__default_evpn__.evpn.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
4:62.0.0.1:0::01:62.0.0.1/304 ES
*[EVPN/170] 02:57:15
Indirect
4:62.0.0.2:0::01:62.0.0.2/304 ES
*[BGP/170] 02:57:16, localpref 100, from 62.0.0.100
AS path: I, validation-state: unverified
> to 10.0.0.1 via ae0.1

Как я и сказал, RD сгенерировано маршрутизатором автоматически: 62.0.0.1:0 в маршруте от RZN-PE-1 (next-hop indirect, так как маршрут локален для данного маршрутиатора) и 62.0.0.2:0 в маршруте от RZN-PE-2. Маршрута от RZN-PE-3 нет, так как данный PE-маршрутизатор не является multihomed. Более того, так как на PE-3 нет такого ESI, то эти маршруты данный маршрутизатор не импортирует, хотя рефлектор добросовестно их ему отдает:

[email protected]> show route table __default_evpn__.evpn.0
[email protected]> show route advertising-protocol bgp 62.0.0.3 | match 4:62
4:62.0.0.1:0::01:62.0.0.1/304
4:62.0.0.2:0::01:62.0.0.2/304

Теперь разберем данный маршрут подробнее:

[email protected]> show route table __default_evpn__.evpn.0 match-prefix *4:6* next-hop 62.0.0.2 detail
__default_evpn__.evpn.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
4:62.0.0.2:0::01:62.0.0.2/304 ES (1 entry, 1 announced)
*BGP Preference: 170/-101
Route Distinguisher: 62.0.0.2:0
Next hop type: Indirect, Next hop index: 0
Address: 0xb1e55f0
Next-hop reference count: 20
Source: 62.0.0.100
Protocol next hop: 62.0.0.2
Indirect next hop: 0x2 no-forward INH Session ID: 0x0
State: Secondary Active Int Ext
Local AS: 42000.62 Peer AS: 42000.62
Age: 2:58:04 Metric2: 1
Validation State: unverified
Task: BGP_42000.62.62.0.0.100
Announcement bits (1): 0-__default_evpn__-evpn
AS path: I (Originator)
Cluster list: 62.0.0.100
Originator ID: 62.0.0.2
Communities: es-import-target:0-0-0-0-0-0
Import Accepted
Localpref: 100
Router ID: 62.0.0.100
Primary Routing Table bgp.evpn.0

Особо ничего криминального — большая часть полей свойственна BGP маршруту. Но вот в строке community мы обычно привыкли видеть например domain-id, origin или target community. Тут же совсем другие community.

Это специально зарезервированное для EVPN community. Как я и писал выше данный маршрут генерируется исключительно per-ESI и получить его должны только те PE-маршрутизаторы, которым этот маршрут нужен. А нужен он только тем PE-кам, которые имеют линк в том же ESI, что и отправитель маршрута. Поэтому community данного маршрута генерируется на основании ESI и имеет вид es-import-target:0-0-0-0-0-0.

В нашем случае все нули, и я специально взял такое ESI, чтобы показать, что это community может содержать только нули во второй своей части (первая часть зарезервирована и равна 0x06 (type) и 0х02 (sub type), кому интересно читаем RFC) и это никак не скажется на работоспособность EVPN. В итоге в нашей лабораторной сети только RZN-PE-1 и RZN-PE-2 импортируют данный маршрут.

А где же сам ESI спросите вы? Идентификатор указан непосредственно в самом маршруте: 4:62.0.0.2:0::01:62.0.0.2/304 ES, просто пустые октеты (нули) опускаются (подобно ipv6). И как не трудно догадаться если два маршрутизатора будут иметь линки в в разных сегментах, но их идентификаторы будут равны 00:00:00:00:00:00:00:00:00:01 у первого и 10:00:00:00:00:00:00:00:00:01 у второго, то маршруты будут импортированы обоими маршрутизаторами. По community происходит только первоначальная фильтрация — сам же маршрут будет использован только если ESI, указанный в маршруте совпадает с ESI на самом маршрутизаторе, который этот маршрут получил, иначе — маршрут будет отброшен.

Как только маршрутизатор понимает, что он является multihomed (понимает он это по ненулевому значению ESI в конфигурации интерфейсов), он начинает отправлять маршрут типа 4, чтобы все соседние маршрутизаторы знали о его присутствии в сети.

После того, как маршруты разлетелись по сети, RZN-PE-1 и RZN-PE-2 узнают, что они подключены к одному и тому же ESI. Оба маршрутизатора ждут в течении 3 секунд (по дефолту) маршрутов типа 4 от остальных PE-маршрутизаторов, после чего, на основании полученных от соседей маршрутов типа 4 составляют список всех подключенных к данному сегменту узлов, причем на всех узлах данный список будет одинаков, в нашем случае такой:

62.0.0.1 i=0 62.0.0.2 i=1

После этого маршрутизаторы начинают высчитывать, кто будет DF по формуле V mod N = i, где V — номер влана, а N количество PE маршрутизаторов в сегменте. Тот PE маршрутизатор, номер которого будет результатом вычисления и становится DF данного сегмента в данном влане. Как вы понимаете, для каждого влана будет свой DF — получается некая балансировка BUM трафика.

В лаборатории сконфигурено три EVPN инстанса, каждому инстансу соответствует один влан, я использовал вланы 777, 778 и 779. Так как multihomed PE-ек у нас 2, то количество узлов равно 2. Получаем, что в данном сегменте DF-ом для вланов 777 и 779 будет выбран RZN-PE-2, а для 778 — RZN-PE-1, что легко проверить:

[email protected]> show configuration routing-instances | display set | match interface
set routing-instances vSwitch-eVPN-1 interface ae3.777
set routing-instances vSwitch-eVPN-2 interface ae3.778
set routing-instances vSwitch-eVPN-3 interface ae3.779
[email protected]> show configuration interfaces ae3 | display set | match vlan-id
set interfaces ae3 unit 777 family bridge vlan-id-list 777
set interfaces ae3 unit 778 family bridge vlan-id-list 778
set interfaces ae3 unit 779 family bridge vlan-id-list 779
[email protected]> show evpn instance vSwitch-eVPN-1 designated-forwarder
Instance: vSwitch-eVPN-1
Number of ethernet segments: 1
ESI: 00:00:00:00:00:00:00:00:00:01
Designated forwarder: 62.0.0.2
[email protected]> show evpn instance vSwitch-eVPN-2 designated-forwarder
Instance: vSwitch-eVPN-2
Number of ethernet segments: 1
ESI: 00:00:00:00:00:00:00:00:00:01
Designated forwarder: 62.0.0.1
[email protected]> show evpn instance vSwitch-eVPN-3 designated-forwarder
Instance: vSwitch-eVPN-3
Number of ethernet segments: 1
ESI: 00:00:00:00:00:00:00:00:00:01
Designated forwarder: 62.0.0.2

Данная схема имеет свои плюсы и минусы. Самый очевидный минус — это сам механизм выбора DF. Как только в сегменте появляется новый маршрутизатор, имеющий линк в сторону ESI X или происходит падение/восстановление линка на маршрутизаторе в сторону ESI X, то для данного сегмента происходит пересчет DF. Причем самой плохой ситуацией будет пропадание линка в сторону ESI X на DF маршрутизаторе. Так как остальные маршрутизаторы блокируют передачу BUM трафика в сторону CE устройства, то на время детектирования падения DF и высчитывание нового DF BUM трафик будет дропаться всеми PE-маршрутизаторами сегмента, так как в данный момент времени они все будут non-DF. Но в сейчас есть драфт [RFC][3], который описывает новую процедуру выбора DF. Но пока что все работает так, как описано.

Стоит упомянуть, что выбор DF для vlan-aware и vlan-bundle методов немного различаются. Так как виртуальный свич может терминировать несколько бридж-доменов, то выбор DF в этом случае происходит не для каждого влана отдельно, а для всех вланов сразу, а в расчетах используется наименьший сконфигурированный номер влана. Для примера добавим в виртульный свич вланы 30,778 и 779. Нетрудно подсчитать, что если взять за основу наименьший номер влана — 30, то DF-ом для сего данного сегмента будет PE1 — 62.0.0.1:

[email protected]> show evpn instance vSwitch-eVPN-1 extensive | match "domain|extended|forwarder"
Number of bridge domains: 4
VLAN Domain ID Intfs / up IRB intf Mode MAC sync IM route label
30 1 1 Extended Enabled 300384
777 1 1 irb.1 Extended Enabled 300384
778 1 1 Extended Enabled 300384
779 1 1 Extended Enabled 300384
Designated forwarder: 62.0.0.1
Backup forwarder: 62.0.0.2
Last designated forwarder update: May 24 08:12:13

А теперь удалим 30-й влан. Теперь наименьший номер влана это 777, то есть теперь DF-ом должен стать PE2 — 62.0.0.2:

[email protected]> show evpn instance vSwitch-eVPN-1 extensive | match "domain|extended|forwarder"
Number of bridge domains: 4
VLAN Domain ID Intfs / up IRB intf Mode MAC sync IM route label
777 1 1 irb.1 Extended Enabled 300384
778 1 1 Extended Enabled 300384
779 1 1 Extended Enabled 300384
Designated forwarder: 62.0.0.2
Backup forwarder: 62.0.0.1
Last designated forwarder update: May 24 08:14:52

Примечание: в выводах выше, да и в литературе встречается понятие Backup forwarder или Backup Designated forwarder (BDF). BDF это просто еще одно название non-DF маршрутизатора. В EVPN нет бекапного форвардера (как например в OSPF DR и BDR) — DF только один, все остальные non-DF или BDF. В случае появления в сегменте еще одного маршрутизатора или при падении одного из маршрутизаторов сегмента происходят перевыборы DF.

Теперь мы выяснили зачем нужен маршрут типа 4 и как он выглядит.

Но выбрав DF мы победили только один тип петель. Но петля может все равно возникнуть если CE маршрутизатор начнет отправлять трафик сторону non-DF маршрутизатора. Для примера, если RZN-PE-1, является non-DF получит BUM трафик от RZN-SW-1 то мы получим петлю, так как: RZN-PE-1, получив BUM трафик от CE, отправит этот трафик в сторону других PE-шек, включая RZN-PE-2. RZN-PE-2, являясь DF для данного сегмента, без зазрения совести отправляет трафик обратно в сторону RZN-SW-1, откуда он и пришел. В итоге получилась петля:

И пока петлю не разорвать трафик так и будет летать туда-сюда.

Чтобы этого избежать нам понадобится маршрут типа 1, так же сгенерированный per-ESI.

Но в отличии от маршрутов типа 4, в маршрутах типа 1, сгенерированных хоть per-ESI, хоть per-EVI указывается нативное community инстанса или нескольких инстансов, которым данный маршрут интересен (а почему, мы узнаем чуть позже):

[email protected]> show route table vSwitch-eVPN-1.evpn.0 match-prefix *1:6*
vSwitch-eVPN-1.evpn.0: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both
1:62.0.0.1:1::01::0/304 AD/EVI
*[EVPN/170] 1d 00:09:01
Indirect
1:62.0.0.2:0::01::FFFF:FFFF/304 AD/ESI
*[BGP/170] 03:20:10, localpref 100, from 62.0.0.100
AS path: I, validation-state: unverified
> to 10.0.0.1 via ae0.1
1:62.0.0.2:1::01::0/304 AD/EVI
*[BGP/170] 1d 00:09:01, localpref 100, from 62.0.0.100
AS path: I, validation-state: unverified
> to 10.0.0.1 via ae0.1