В одной из прошлых статей я описал способ реализации L3VPN поверх DMVPN, который позволяет организовать связность spoke-маршрутизаторов между собой напрямую, без необходимости отправлять трафик через hub. Одним из ключевых элементов этого способа было использование internal BGP labeled unicast (iBGP LU) для распределения транспортных меток. Поначалу я считал, что модифицировать схему с использованием eBGP не составит большого труда; однако, как показала практика, всё оказалось не так просто.

Большая часть технологий не зависит от типа BGP и, как следствие, остаётся неизменной, поэтому можно опустить детали и упростить схему DMVPN до L2-сегмента, поверх которого будет впоследствии настроен eBGP LU:

Switch1 эмулирует DMVPN-облако, на данном этапе детали его реализации нам безразличны. CSR3 выполняет роль, похожую по функциональности на hub – в его задачи входит распределение префиксов между spoke-маршрутизаторами R1 и R2. Обычно для лабы я использую образы Cisco 7200, но в этот раз мне понадобится более свежее ПО, хотя бы IOS XE Denali 16.3.1. Начальная конфигурация довольно проста:

CSR3(config)# interface Loopback0
CSR3(config-if)# ip address 3.3.3.3 255.255.255.255
CSR3(config)# interface GigabitEthernet1
CSR3(config-if)# ip address 192.168.0.3 255.255.255.0
CSR3(config-if)# mpls bgp forwarding
R1(config)# interface Loopback0
R1(config-if)# ip address 1.1.1.1 255.255.255.255
R1(config)# interface GigabitEthernet1
R1(config-if)# ip address 192.168.0.1 255.255.255.0
R1(config-if)# mpls bgp forwarding
R2(config)# interface Loopback0
R2(config-if)# ip address 2.2.2.2 255.255.255.255
R2(config)# interface GigabitEthernet1
R2(config-if)# ip address 192.168.0.2 255.255.255.0
R2(config-if)# mpls bgp forwarding

Поскольку мы планируем использовать eBGP, CSR3 не может быть route-reflector для R1 и R2. Однако все маршрутизаторы подключены к одному L2-сегменту, поэтому CSR3 не будет менять next-hop при передаче префиксов между соседями в этом сегменте. Стоит отметить, что, согласно BGP RFC, такое поведение опционально, а значит, может отличаться для другого ПО, оборудования, производителя и т.д.:

When sending a message to an external peer, X, and the peer is one IP hop away from the speaker:

<irrelevant part omitted>

Otherwise, if the route being announced was learned from an external peer, the speaker can use an IP address of any adjacent router (known from the received NEXT_HOP attribute) that the speaker itself uses for local route calculation in the NEXT_HOP attribute, provided that peer X shares a common subnet with this address. This is a second form of "third party" NEXT_HOP attribute.

Вольный перевод:

Если маршрут объявлен внешним соседом, маршрутизатор может использовать любой IP-адрес (из полученных через атрибут NEXT_HOP) в качестве NEXT_HOP при условии, что он использует этот IP-адрес для своей локальной таблицы маршрутизации и адрес соседа Х, которому будет послан маршрут, находится в той же подсети, что и IP-адрес в атрибуте NEXT_HOP.

И последнее перед непосредственной настройкой BGP: R1 и R2 используют один и тот же номер AS в целях масштабируемости, поэтому нам необходимо немного поправить логику BGP в части предотвращения петель маршрутизации. Вначале настроим обычный BGP IPv4 AF без LU:

CSR3(config)#router bgp 3
CSR3(config-router)# bgp router-id 3.3.3.3
CSR3(config-router)# bgp listen range 192.168.0.0/24 peer-group DMVPN
CSR3(config-router)# no bgp default ipv4-unicast
CSR3(config-router)# neighbor DMVPN peer-group
CSR3(config-router)# neighbor DMVPN remote-as 12
CSR3(config-router)# address-family ipv4
CSR3(config-router-af)# network 3.3.3.3 mask 255.255.255.255
CSR3(config-router-af)# neighbor DMVPN activate
R1(config)#router bgp 12
R1(config-router)# bgp router-id 1.1.1.1
R1(config-router)# no bgp default ipv4-unicast
R1(config-router)# neighbor 192.168.0.3 remote-as 3
R1(config-router)# address-family ipv4
R1(config-router-af)# network 1.1.1.1 mask 255.255.255.255
R1(config-router-af)# neighbor 192.168.0.3 activate
R1(config-router-af)# neighbor 192.168.0.3 allowas-in 
R2(config)#router bgp 12
R2(config-router)# bgp router-id 2.2.2.2
R2(config-router)# no bgp default ipv4-unicast
R2(config-router)# neighbor 192.168.0.3 remote-as 3
R2(config-router)# address-family ipv4
R2(config-router-af)# network 2.2.2.2 mask 255.255.255.255
R2(config-router-af)# neighbor 192.168.0.3 activate
R2(config-router-af)# neighbor 192.168.0.3 allowas-in 1

Следует ожидать, что префиксы на R1 и R2 будут иметь адреса друг друга в качестве next-hop вместо адреса CSR3:

R1#show ip bgp         
BGP table version is 4, local router ID is 1.1.1.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
 *>  1.1.1.1/32       0.0.0.0                  0         32768 i
 *>  2.2.2.2/32       192.168.0.2                            0 3 12 i
 *>  3.3.3.3/32       192.168.0.3              0             0 3 i
R1#
R1#traceroute 2.2.2.2 source lo0
Type escape sequence to abort.
Tracing the route to 2.2.2.2
VRF info: (vrf in name/id, vrf out name/id)
  1 192.168.0.2 12 msec 8 msec 8 msec

Настало время включить BGP LU для IPv4 AF:

CSR3(config-router-af)#neighbor DMVPN send-label
R1(config-router-af)#neighbor 192.168.0.3 send-label
R2(config-router-af)#neighbor 192.168.0.3 send-label

Проверим, что BGP сошёлся так, как мы ожидаем:

R1#sho ip bgp
BGP table version is 12, local router ID is 1.1.1.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
 *   1.1.1.1/32       192.168.0.3                            0 3 12 i
 *>                   0.0.0.0                  0         32768 i
 *>  2.2.2.2/32       192.168.0.3                            0 3 12 i
 *>  3.3.3.3/32       192.168.0.3              0             0 3 i
R1#traceroute 2.2.2.2 source lo0
Type escape sequence to abort.
Tracing the route to 2.2.2.2
VRF info: (vrf in name/id, vrf out name/id)
  1 192.168.0.3 [MPLS: Label 16 Exp 0] 8 msec 8 msec 12 msec
  2 192.168.0.2 12 msec 20 msec 12 msec

Проблема, подходящая для отладки пятничным вечером: мы не только увеличили число маршрутов в таблице BGP (обратите внимание на 1.1.1.1/32), но и испортили путь – маршрут от R1 до R2 перестал быть оптимальным.

Возможно, вы помните из предыдущей статьи, что BGP назначает префиксу метку в том случае, когда считает себя next-hop для этого маршрута, что позволяет маршрутизатору стать частью LSP. Давайте явно укажем, что менять next-hop не следует:

CSR3(config-router-af)#neighbor DMVPN next-hop-unchanged 
%BGP: Can propagate the nexthop only to multi-hop EBGP neighbor or iBGP VRF CE lite

Безуспешно, что, впрочем, находится в полном согласии с Configuration Guide. Однако существует вторая вариация этой настройки – с помощью route-map, хотя она должна быть подвержена тем же ограничениям, что и команда на уровне процесса BGP:

CSR3(config)#route-map NH_UNCHANGED
CSR3(config-route-map)#set ip next-hop unchanged 
CSR3(config)#router bgp 3
CSR3(config-router)#address-family ipv4
CSR3(config-router-af)#neighbor DMVPN route-map NH_UNCHANGED out
R1#show ip bgp
BGP table version is 14, local router ID is 1.1.1.1
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal, 
              r RIB-failure, S Stale, m multipath, b backup-path, f RT-Filter, 
              x best-external, a additional-path, c RIB-compressed, 
Origin codes: i - IGP, e - EGP, ? - incomplete
RPKI validation codes: V valid, I invalid, N Not found

     Network          Next Hop            Metric LocPrf Weight Path
 *>  1.1.1.1/32       0.0.0.0                  0         32768 i
 *>  2.2.2.2/32       192.168.0.2                            0 3 12 i
 *>  3.3.3.3/32       192.168.0.3              0             0 3 i
R1#traceroute 2.2.2.2 source lo0
Type escape sequence to abort.
Tracing the route to 2.2.2.2
VRF info: (vrf in name/id, vrf out name/id)
  1 192.168.0.2 12 msec 16 msec 8 msec

По неведомой мне причине способ с route-map сработал, хотя и не должен был, согласно документации. В неопределённый момент в прошлом я находил заметку о том, что в случае с BGP LU отключить смену next-hop можно только с помощью route-map; к сожалению, мне не удалось найти этот документ при подготовке статьи, поэтому буду рад, если кто-то сможет поделиться ссылкой на него.

Итак, мы восстановили прямой путь между R1 и R2. Но что насчёт «лишней» записи в таблице BGP? Почему она появилась после включения LU, но её нет после применения route-map? На первую часть вопроса ответить несложно: причина появления дубликата префикса на R1 носит название «update-group» в Cisco IOS:

CSR3#sho ip bgp update-group 
BGP version 4 update-group 3, external, Address Family: IPv4 Unicast
  BGP Update version : 15/0, messages 0, active RGs: 1
  Route map for outgoing advertisements is NH_UNCHANGED
  Sending Prefix & Label
  Topology: global, highest version: 15, tail marker: 15
  Format state: Current working (OK, last minimum advertisement interval)
                Refresh blocked (not in list, last not in list)
  Update messages formatted 9, replicated 12, current 0, refresh 0, limit 1000
  Number of NLRIs in the update sent: max 1, min 0
  Minimum time between advertisement runs is 30 seconds
  Has 2 members:
   *192.168.0.1     *192.168.0.2 

Вкратце смысл таков: IOS формирует обновления BGP на группу соседей, а не на каждого соседа отдельно, что позволяет сэкономить ресурсы маршрутизатора (представьте обработку full-view). Раньше мы могли контролировать это поведение с помощью peer-groups, однако этот рычаг управления отобрали оптимизировали давным-давно. R1 и R2 входят в одну группу с точки зрения политик BGP, поэтому они получают одинаковый набор маршрутов, включая их собственные. CSR3 переписывал next-hop для BGP LU, поэтому собственный маршрут с новым next-hop был вполне корректным с точки зрения процессов BGP на R1 и R2. Но почему же собственные префиксы на попадают в таблицу BGP, когда next-hop неизмéнен? Расспросим очевидцев:

R1#debug ip bgp updates 
BGP updates debugging is on for address family: IPv4 Unicast 
R1#clear ip bgp * soft
<debug omitted>
*Dec  5 17:16:11.831: BGP(0): 192.168.0.3 rcv UPDATE about 1.1.1.1/32 -- DENIED due to: 
R1#NEXTHOP is our own address;
<debug omitted>

Ларчик просто открывался: если next-hop для префикса совпадает с адресом получившего его маршрутизатора, то последний просто отбрасывает такое сообщение. Очевидно, что то же самое происходит не только в случае eBGP, но и в случае iBGP (например, с использованием RR).

Вернёмся к MPLS:

R1#ping 2.2.2.2 source lo0
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 2.2.2.2, timeout is 2 seconds:
Packet sent with a source address of 1.1.1.1 
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 8/11/20 ms
R1#
R1#ping mpls ipv4 2.2.2.2/32 source 1.1.1.1
Sending 5, 100-byte MPLS Echos to 2.2.2.2/32, 
     timeout is 2 seconds, send interval is 0 msec:

Codes: '!' - success, 'Q' - request not sent, '.' - timeout,
  'L' - labeled output interface, 'B' - unlabeled output interface, 
  'D' - DS Map mismatch, 'F' - no FEC mapping, 'f' - FEC mismatch,
  'M' - malformed request, 'm' - unsupported tlvs, 'N' - no label entry, 
  'P' - no rx intf label prot, 'p' - premature termination of LSP, 
  'R' - transit router, 'I' - unknown upstream index,
  'X' - unknown return code, 'x' - return code 0

Type escape sequence to abort.
QQQQQ
Success rate is 0 percent (0/5)
R1#
R1#show ip cef 2.2.2.2/32 detail 
2.2.2.2/32, epoch 0, flags rib only nolabel, rib defined all labels
  recursive via 192.168.0.2
    attached to FastEthernet0/0
R1#
R1#sho mpls forwarding-table 
Local      Outgoing   Prefix           Bytes Label   Outgoing   Next Hop    
Label      Label      or Tunnel Id     Switched      interface 

Как легко заметить, связности по MPLS между R1 и R2 нет. Причина этого проста – отсутствуют метки. Мы используем BGP для распространения меток, поэтому, должно быть, что-то пошло не так с обновлениями BGP. Посмотрим на R1:

R1#show ip bgp labels 
   Network          Next Hop      In label/Out label
   1.1.1.1/32       0.0.0.0         imp-null/nolabel
   2.2.2.2/32       192.168.0.2     nolabel/nolabel
   3.3.3.3/32       192.168.0.3     nolabel/nolabel

R1 не получил меток от CSR3 ни напрямую (назначены CSR3), ни косвенно (назначены R2, CSR3 всего лишь пересылает). Однако с точки зрения CSR3 все метки на месте:

CSR3#show ip bgp labels 
   Network          Next Hop      In label/Out label
   1.1.1.1/32       192.168.0.1     nolabel/imp-null
   2.2.2.2/32       192.168.0.2     nolabel/imp-null
   3.3.3.3/32       0.0.0.0         nolabel/nolabel

Похоже на то, что CSR3 перестал отправлять своим соседям метки вместе с префиксами после применения route-map. Попробуем исправить положение так же, как мы сделали для next-hop:

CSR3(config)#route-map NH_UNCHANGED permit 10 
CSR3(config-route-map)#set mpls-label 
CSR3#clear ip bgp * soft

Проверим, поменялось ли что-нибудь на R1?

R1#show ip bgp labels 
   Network          Next Hop      In label/Out label
   1.1.1.1/32       0.0.0.0         imp-null/nolabel
   2.2.2.2/32       192.168.0.2     nolabel/imp-null
   3.3.3.3/32       192.168.0.3     nolabel/nolabel

Теперь R1 получает верную метку для префикса 2.2.2.2/32, однако, в обновлении для 3.3.3.3/32 она отсутствует; CSR3 игнорирует метку для этого префикса при добавлении его в BGP:

CSR3#show ip bgp labels
   Network          Next Hop      In label/Out label
   1.1.1.1/32       192.168.0.1     nolabel/imp-null
   2.2.2.2/32       192.168.0.2     nolabel/imp-null
   3.3.3.3/32       0.0.0.0         nolabel/nolabel

Disclaimer: всё, что написано далее, получено методом проб и ошибок без опоры на официальную документацию.

Итак, используем чёрную магию route-map ещё раз. 3.3.3.3/32 попадает в единственную запись NH_UNCHANGED, однако, у него нет другого next-hop, кроме CSR3, поэтому команда “next-hop unchanged” может вызывать исключение при обработке, что в свою очередь предотвращает добавление метки к обновлению BGP. Используем prefix-list для определения префиксов, которые DMVPN hub добавляет в BGP:

CSR3(config)#ip prefix-list LOCAL permit 3.3.3.3/32          
CSR3(config)#route-map NH_UNCHANGED permit 5                 
CSR3(config-route-map)#match ip address prefix-list LOCAL              
CSR3(config-route-map)#set mpls-label 
CSR3#clear ip bgp * soft
R1#show ip bgp labels 
   Network          Next Hop      In label/Out label
   1.1.1.1/32       0.0.0.0         imp-null/nolabel
   2.2.2.2/32       192.168.0.2     nolabel/imp-null
   3.3.3.3/32       192.168.0.3     nolabel/imp-null

Вуаля! Все метки на месте. Может ли R1 построить LSP до 2.2.2.2/32?

R1#ping mpls ipv4 2.2.2.2/32 source 1.1.1.1
Sending 5, 100-byte MPLS Echos to 2.2.2.2/32, 
     timeout is 2 seconds, send interval is 0 msec:

Codes: '!' - success, 'Q' - request not sent, '.' - timeout,
  'L' - labeled output interface, 'B' - unlabeled output interface, 
  'D' - DS Map mismatch, 'F' - no FEC mapping, 'f' - FEC mismatch,
  'M' - malformed request, 'm' - unsupported tlvs, 'N' - no label entry, 
  'P' - no rx intf label prot, 'p' - premature termination of LSP, 
  'R' - transit router, 'I' - unknown upstream index,
  'X' - unknown return code, 'x' - return code 0

Type escape sequence to abort.
QQQQQ
Success rate is 0 percent (0/5)
R1#
R1#show ip cef 2.2.2.2/32 det
2.2.2.2/32, epoch 0, flags rib defined all labels
  recursive via 192.168.0.2
    attached to FastEthernet0/0
R1#
R1#show mpls forwarding-table 2.2.2.2   
Local      Outgoing   Prefix           Bytes Label   Outgoing   Next Hop    
Label      Label      or Tunnel Id     Switched      interface              
None       No Label   2.2.2.2/32       0             Fa0/0      192.168.0.2 

LSP не сформирован: R1 не устанавливает метки в LFIB несмотря на то, что получил их по BGP. Вся информация, необходимая для построения LSP, у R1 есть, кроме, возможно, соответствия сценарию применения: R1 должен передавать трафик дальше, используя LDP, т. к. P-маршрутизатор, который использует лишь BGP – явление противоестественное. Хотел бы отдельно отметить, что это лишь догадки, основанные на рабочей конфигурации. Настало время раскрыть заклинание:

R1(config)#int f0/0.1
R1(config-subif)#mpls ip
R1(config)#router ospf 1
R1(config-router)#redistribute bgp 12 subnets

Такая нехитрая настройка заставляет R1 установить метки в LFIB, при этом база данных OSPF остаётся пустой, т.к. процессу не принадлежит ни одного интерфейса. Последний факт позволяет не настраивать фильтрацию при redistribution:

R1#show mpls forwarding
Local      Outgoing   Prefix           Bytes Label   Outgoing   Next Hop    
Label      Label      or Tunnel Id     Switched      interface              
16         Pop Label  2.2.2.2/32       0             Fa0/0      192.168.0.2 
17         Pop Label  192.168.0.3/32   0             Fa0/0      192.168.0.3 
18         Pop Label  3.3.3.3/32       0             Fa0/0      192.168.0.3 
R1#show ip ospf database

            OSPF Router with ID (1.1.1.1) (Process ID 1)

Удаление процесса OSPF после установки меток не приводит к очистке LFIB, однако, новые метки в LFIB уже не попадают – прекрасный повод применить знания EEM DevOps :) То же поведение можно наблюдать и для iBGP: метки не попадают в LFIB, пока «процессы-доноры», OSPF и LDP, не будут инициализированы.

Впрочем, довольно копаться во внутренностях IOS, давайте посмотрим, поднялся ли LSP:

R1#ping mpls ipv4 2.2.2.2/32 source 1.1.1.1
Sending 5, 100-byte MPLS Echos to 2.2.2.2/32, 
     timeout is 2 seconds, send interval is 0 msec:

Codes: '!' - success, 'Q' - request not sent, '.' - timeout,
  'L' - labeled output interface, 'B' - unlabeled output interface, 
  'D' - DS Map mismatch, 'F' - no FEC mapping, 'f' - FEC mismatch,
  'M' - malformed request, 'm' - unsupported tlvs, 'N' - no label entry, 
  'P' - no rx intf label prot, 'p' - premature termination of LSP, 
  'R' - transit router, 'I' - unknown upstream index,
  'X' - unknown return code, 'x' - return code 0

Type escape sequence to abort.
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 4/16/40 ms

Наконец, LSP между R1 и R2 заработал, остальная часть настроек L3VPN совпадает с предыдущей схемой. Наибольшей проблемой при использовании eBGP для реализации L3VPN поверх DMVPN является необходимость использования команды “next-hop unchanged”, поддержка которой для labeled unicast, по-видимому, отсутствует. Очевидно, что использование этой команды в подобной схеме умножает техническую поддержку производителя на ноль. Даже несмотря на то, что мне удалось заставить эту функцию работать в рамках подготовки статьи, нет гарантий, что это поведение не изменится в будущих версиях ПО; я бы подвергал такому испытанию только лабораторный стенд.

В целом выходит, что на данный момент eBGP не подходит для построения LSP напрямую между spoke для использования в рамках L3VPN поверх DMVPN. И последнее: любознательный читатель, мы сделали домашку!

Спасибо за рецензию: Анастасии Куралевой

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


  1. Vigogne
    05.01.2022 19:55

    Какое извращение! ФУ!!! Продолжайте! :)))