Cisco ACI Preferred group, немножко inter-VRF leaking и L3Out

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

В последней статье мы обсудили детали реализации inter-VRF leaking с использованием двух самых обычных  EPG. Безусловно, L3Out тоже можно использовать похожим образом, например, для предоставления общего доступа в интернет. Впрочем, тут не обошлось без ложки дёгтя: есть один раздел в документе, описывающем контракты ACI, в котором мелким шрифтом написано следующее:

“Due to CSCvm63145, an EPG in a preferred group can consume an inter-VRF contract but cannot be a provider for an inter-VRF contract with an L3Out EPG as the consumer.”

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

"Вследствие CSCvm63145 EPG, будучи частью preferred group, может потреблять inter-VRF контракт, однако не может предоставлять такой контракт, если потребитель – L3Out."

И никакого дополнительного комментария, почему дела обстоят именно так. Если посмотреть описание бага, то можно найти чуть больше деталей: если EPG – provider для inter-VRF контракта, то он не может общаться внутри Preferred Group из-за некоего ограничивающего фильтра. Однако ведь подобные фильтры возникают при явном использовании контракта, не так ли? Спровоцируем баг и посмотрим, что же происходит на самом деле.

Host притворяется сразу тремя сущностями: предоставляет сервис (Provider), потребляет сервис (L3Out) и тихо-мирно ковыряется в носу в Preferred Group внутри TestVrf1 (TestEPG). Обмен префиксами между L3Out и Host происходит по OSPF. По задумке 2.2.2.2/32 использует сервис, расположенный по адресу 192.168.1.1. Provider и TestEPG находятся в одной подсети и, как следствие, в одном BD.

Определим Access Policy, чтобы разрешить подключение на физическом уровне фабрики:

resource "aci_vlan_pool" "TestPool" {
  name  = "TestPool"
  alloc_mode  = "static"
}
resource "aci_ranges" "TestRange" {
  vlan_pool_dn  = aci_vlan_pool.TestPool.id
  from = "vlan-1"
  to = "vlan-1000"
  alloc_mode = "static"
}
resource "aci_physical_domain" "PhysicalDomain" {
  name = "PhysicalDomain"
  relation_infra_rs_vlan_ns = aci_vlan_pool.TestPool.id
}
resource "aci_l3_domain_profile" "L3Domain" {
  name = "L3Domain"
  relation_infra_rs_vlan_ns = aci_vlan_pool.TestPool.id
}
resource "aci_attachable_access_entity_profile" "TestAAEP" {
    name = "TestAAEP"
}
resource "aci_aaep_to_domain" "PhysicalDomain-to-TestAAEP" {
  attachable_access_entity_profile_dn = aci_attachable_access_entity_profile.TestAAEP.id
  domain_dn = aci_physical_domain.PhysicalDomain.id
}
resource "aci_aaep_to_domain" "L3Domain-to-TestAAEP" {
  attachable_access_entity_profile_dn = aci_attachable_access_entity_profile.TestAAEP.id
  domain_dn = aci_l3_domain_profile.L3Domain.id
}
resource "aci_leaf_interface_profile" "TestInterfaceProfile" {
    name = "TestInterfaceProfile"
}
resource "aci_access_port_block" "TestAccessBlockSelector" {
  access_port_selector_dn = aci_access_port_selector.TestAccessPortSelector.id
  name = "TestAccessBlockSelector"
  from_card = "1"
  from_port = "2"
  to_card = "1"
  to_port = "4"
}
resource "aci_access_port_selector" "TestAccessPortSelector" {
    leaf_interface_profile_dn = aci_leaf_interface_profile.TestInterfaceProfile.id
    name = "TestAccessPortSelector"
    access_port_selector_type = "range"
    relation_infra_rs_acc_base_grp = aci_leaf_access_port_policy_group.TestAccessInterfacePolicy.id
}
resource "aci_leaf_access_port_policy_group" "TestAccessInterfacePolicy" {
    name = "TestAccessInterfaceProfile"
    relation_infra_rs_att_ent_p = aci_attachable_access_entity_profile.TestAAEP.id
}
resource "aci_leaf_profile" "TestSwitchProfile" {
  name = "TestSwitchProfile"
  leaf_selector {
    name = "LeafSelector"
    switch_association_type = "range"
    node_block {
      name  = "Block1"
      from_ = "101"
      to_   = "104"
    }
  }
  relation_infra_rs_acc_port_p = [aci_leaf_interface_profile.TestInterfaceProfile.id]
}

Затем можно настроить tenant с нужными EPG и конструкциями сетевого уровня:

resource "aci_tenant" "TestTenant" {
    name = "TestTenant"
}
resource "aci_vrf" "TestVrf1" {
    tenant_dn = aci_tenant.TestTenant.id
    name = "TestVrf1"
}
resource "aci_vrf" "TestVrf2" {
    tenant_dn = aci_tenant.TestTenant.id
    name = "TestVrf2"
}
resource "aci_bridge_domain" "TestBD1" {
    tenant_dn = aci_tenant.TestTenant.id
    name  = "TestBD1"
    relation_fv_rs_ctx = aci_vrf.TestVrf1.id
}
resource "aci_subnet" "ProviderSubnet" {
    parent_dn = aci_application_epg.Provider.id
    ip = "192.168.1.1/32"
    scope = ["public", "shared"]
    ctrl = ["no-default-gateway"]
}
resource "aci_subnet" "TestEPGSubnet" {
    parent_dn = aci_bridge_domain.TestBD1.id
    ip = "192.168.1.254/24"
    scope = ["public", "shared"]
}
resource "aci_application_profile" "TestAP" {
    tenant_dn = aci_tenant.TestTenant.id
    name = "TestAP"
}
resource "aci_application_epg" "Provider" {
    application_profile_dn = aci_application_profile.TestAP.id
    name = "Provider"
    relation_fv_rs_bd = aci_bridge_domain.TestBD1.id
    pref_gr_memb = "include"
}
resource "aci_application_epg" "TestEPG" {
    application_profile_dn = aci_application_profile.TestAP.id
    name = "TestEPG"
    relation_fv_rs_bd = aci_bridge_domain.TestBD1.id
    pref_gr_memb = "include"
}
resource "aci_epg_to_domain" "ProviderDomain" {
    application_epg_dn = aci_application_epg.Provider.id
    tdn = aci_physical_domain.PhysicalDomain.id
}
resource "aci_epg_to_domain" "TestEPGDomain" {
    application_epg_dn = aci_application_epg.TestEPG.id
    tdn = aci_physical_domain.PhysicalDomain.id
}
resource "aci_bulk_epg_to_static_path" "ProviderStaticPath" {
  application_epg_dn = aci_application_epg.Provider.id
  static_path {
    interface_dn = "topology/pod-1/paths-101/pathep-[eth1/2]"
    encap = "vlan-100"
  }
}
resource "aci_bulk_epg_to_static_path" "TestEPGStaticPath" {
  application_epg_dn = aci_application_epg.TestEPG.id
  static_path {
    interface_dn = "topology/pod-1/paths-101/pathep-[eth1/2]"
    encap = "vlan-101"
  }
}

Зададим контракт, который разрешает любой тип трафика, и назначим его Provider:

resource "aci_contract" "TestContract" {
    tenant_dn = aci_tenant.TestTenant.id
    name = "TestContract"
    scope = "tenant"
}
resource "aci_contract_subject" "TestSubject" {
    contract_dn = aci_contract.TestContract.id
    name = "TestSubject"
}
resource "aci_contract_subject_filter" "PermitIPSubj" {
  contract_subject_dn = aci_contract_subject.TestSubject.id
  filter_dn = aci_filter.PermitIPFilter.id
}
resource "aci_filter" "PermitIPFilter" {
    tenant_dn = aci_tenant.TestTenant.id
    name = "PermitIPFilter"
}
resource "aci_filter_entry" "PermitIPFilterEntry" {
    filter_dn = aci_filter.PermitIPFilter.id
    name = "demo_entry"
    d_to_port = "unspecified"
    ether_t = "ip"
}
resource "aci_application_epg" "Provider" {
    application_profile_dn = aci_application_profile.TestAP.id
    name = "Provider"
    relation_fv_rs_bd = aci_bridge_domain.TestBD1.id
    relation_fv_rs_prov = [aci_contract.TestContract.id]
    pref_gr_memb = "include"
}

Теперь мы можем настроить Host и проверить связность с фабрикой. Это позволит нам убедиться в том, что предыдущие шаги выполнены успешно, а все обязательные параметры уже определены.

Host# show run vrf Provider
interface Ethernet1/1.100
  vrf member Provider
vrf context Provider
  ip route 0.0.0.0/0 192.168.1.254
  address-family ipv4 unicast
Host#
Host# show vrf Provider interface 
Interface                 VRF-Name                        VRF-ID  Site-of-Origin
Ethernet1/1.100           Provider                             3  --
Host#
Host# show run interface e1/1.100
interface Ethernet1/1.100
  encapsulation dot1q 100
  mac-address 0000.0000.0001
  vrf member Provider
  ip address 192.168.1.1/24
Host#
Host# show run vrf TestEPG
interface Ethernet1/1.101
  vrf member TestEPG
vrf context TestEPG
  ip route 0.0.0.0/0 192.168.1.254
  address-family ipv4 unicast
Host#
Host# show vrf TestEPG interface
Interface                 VRF-Name                        VRF-ID  Site-of-Origin
Ethernet1/1.101           TestEPG                              5  --
Host#
Host# show run interface e1/1.101
interface Ethernet1/1.101
  encapsulation dot1q 101
  mac-address 0000.0000.0002
  vrf member TestEPG
  ip address 192.168.1.2/24

Subinterface наследуют MAC адрес родительского интерфейса по умолчанию. Поскольку физический интерфейс один и тот же, ACI будет некорректно считать, что оба IP принадлежат одному MAC и, как следствие, одному EPG. Простое решение – использовать разные MAC адреса, поэтому мы и задали их вручную.

Host# ping 192.168.1.254 vrf Provider
PING 192.168.1.254 (192.168.1.254): 56 data bytes
64 bytes from 192.168.1.254: icmp_seq=0 ttl=63 time=1.145 ms
64 bytes from 192.168.1.254: icmp_seq=1 ttl=63 time=0.898 ms
64 bytes from 192.168.1.254: icmp_seq=2 ttl=63 time=1.008 ms
64 bytes from 192.168.1.254: icmp_seq=3 ttl=63 time=0.97 ms
64 bytes from 192.168.1.254: icmp_seq=4 ttl=63 time=1.023 ms

--- 192.168.1.254 ping statistics ---
5 packets transmitted, 5 packets received, 0.00% packet loss
round-trip min/avg/max = 0.898/1.008/1.145 ms
Host#
Host# ping 192.168.1.254 vrf TestEPG
PING 192.168.1.254 (192.168.1.254): 56 data bytes
64 bytes from 192.168.1.254: icmp_seq=0 ttl=63 time=1.24 ms
64 bytes from 192.168.1.254: icmp_seq=1 ttl=63 time=0.961 ms
64 bytes from 192.168.1.254: icmp_seq=2 ttl=63 time=1.021 ms
64 bytes from 192.168.1.254: icmp_seq=3 ttl=63 time=0.744 ms
64 bytes from 192.168.1.254: icmp_seq=4 ttl=63 time=0.785 ms

--- 192.168.1.254 ping statistics ---
5 packets transmitted, 5 packets received, 0.00% packet loss
round-trip min/avg/max = 0.744/0.95/1.24 ms

Наконец, создадим L3Out и назначим ему определённый ранее контракт:

resource "aci_l3_outside" "L3Out" {
    tenant_dn = aci_tenant.TestTenant.id
    name = "L3Out"
    enforce_rtctrl = ["export", "import"]
    relation_l3ext_rs_ectx = aci_vrf.TestVrf2.id
    relation_l3ext_rs_l3_dom_att = aci_l3_domain_profile.L3Domain.id
}
resource "aci_logical_node_profile" "L3OutNodeProfile" {
    l3_outside_dn = aci_l3_outside.L3Out.id
    name = "L3OutNodeProfile"
}
resource "aci_logical_interface_profile" "L3OutLogicalInterfaceProfile" {
    logical_node_profile_dn = aci_logical_node_profile.L3OutNodeProfile.id
    name = "L3OutLogicalInterfaceProfile"
}
resource "aci_logical_node_to_fabric_node" "NodetoFabric" {
  logical_node_profile_dn = aci_logical_node_profile.L3OutNodeProfile.id
  tdn = "topology/pod-1/node-103"
  rtr_id = "1.1.1.1"
}
resource "aci_l3out_path_attachment" "InterfaceMapping" {
  logical_interface_profile_dn = aci_logical_interface_profile.L3OutLogicalInterfaceProfile.id
  target_dn = "topology/pod-1/paths-103/pathep-[eth1/3]"
  if_inst_t = "l3-port"
  encap = "unknown"
  addr = "192.168.2.254/24"
}
resource "aci_l3out_ospf_external_policy" "L3OutOSPF" {
  l3_outside_dn = aci_l3_outside.L3Out.id
  area_id = "0.0.0.0"
  area_type = "regular"
}
resource "aci_ospf_interface_policy" "L3OutOSPFPolicy" {
    tenant_dn = aci_tenant.TestTenant.id
    name = "L3OutOSPFPolicy"
    ctrl = ["mtu-ignore"]
    dead_intvl = "40"
    hello_intvl = "10"
}
resource "aci_l3out_ospf_interface_profile" "L3OutOSPFInterface" {
  logical_interface_profile_dn = aci_logical_interface_profile.L3OutLogicalInterfaceProfile.id
  relation_ospf_rs_if_pol = aci_ospf_interface_policy.L3OutOSPFPolicy.id
  auth_key = "key"
}
resource "aci_external_network_instance_profile" "Consumer" {
    l3_outside_dn = aci_l3_outside.L3Out.id
    name = "Consumer"
    relation_fv_rs_cons = [aci_contract.TestContract.id]
}
resource "aci_l3_ext_subnet" "ConsumerSubnet" {
  external_network_instance_profile_dn = aci_external_network_instance_profile.Consumer.id
  ip = "2.2.2.2/32"
  scope = ["import-rtctrl", "import-security", "shared-security", "shared-rtctrl"]
}

Поднимем OSPF на Host, чтобы получить маршруты от ACI:

Host# show run vrf Consumer
interface loopback0
  vrf member Consumer
interface Ethernet1/2
  vrf member Consumer
vrf context Consumer
  address-family ipv4 unicast
router ospf 1
  vrf Consumer
Host#
Host# show vrf B interface 
Interface                 VRF-Name                        VRF-ID  Site-of-Origin
loopback0                 Consumer                             4  --
Ethernet1/2               Consumer                             4  --
Host#
Host# show run interface lo0
interface loopback0
  vrf member Consumer
  ip address 2.2.2.2/32
  ip router ospf 1 area 0.0.0.0
Host#
Host# show run interface e1/2
interface Ethernet1/2
  no switchport
  vrf member Consumer
  ip address 192.168.2.1/24
  ip ospf mtu-ignore
  ip router ospf 1 area 0.0.0.0

На этом этапе контракт назначен только Provider и L3Out, поэтому между ними должна быть связность. TestEPG всё так же остаётся один в своей песочнице:

Host# ping 192.168.1.2 vrf Provider
PING 192.168.1.2 (192.168.1.2): 56 data bytes
36 bytes from 192.168.1.1: Destination Host Unreachable
Request 0 timed out
Request 1 timed out
Request 2 timed out
Request 3 timed out
Request 4 timed out
--- 192.168.1.2 ping statistics ---
5 packets transmitted, 0 packets received, 100.00% packet loss
Host#
Host# ping 192.168.1.1 vrf Consumer source 2.2.2.2
PING 192.168.1.1 (192.168.1.1) from 2.2.2.2: 56 data bytes
64 bytes from 192.168.1.1: icmp_seq=0 ttl=252 time=1.691 ms
64 bytes from 192.168.1.1: icmp_seq=1 ttl=252 time=1.489 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=252 time=1.529 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=252 time=1.525 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=252 time=1.533 ms

Чтобы трафик мог попасть с border leaf на Provider, на Leaf-103 должен быть статический маршрут до EPG, который определяет ClassID точки назначения, а так же целевой VNID.

Leaf-103# show ip route vrf TestTenant:TestVrf2
<output omitted>
1.1.1.1/32, ubest/mbest: 2/0, attached, direct
    *via 1.1.1.1, Lo6, [0/0], 00:08:30, direct
    *via 1.1.1.1, Lo6, [0/0], 00:08:30, local, local
2.2.2.2/32, ubest/mbest: 1/0
    *via 192.168.2.1, Eth1/3, [110/5], 00:07:41, ospf-default, intra
192.168.1.1/32, ubest/mbest: 1/0, attached, direct, pervasive
    *via 10.0.96.64%overlay-1, [1/0], 00:03:54, static, tag 4294967292
192.168.2.0/24, ubest/mbest: 1/0, attached, direct
    *via 192.168.2.254, Eth1/3, [0/0], 00:08:27, direct
192.168.2.254/32, ubest/mbest: 1/0, attached
    *via 192.168.2.254, Eth1/3, [0/0], 00:08:27, local, local
Leaf-103#
Leaf-103# show ip route vrf TestTenant:TestVrf2 192.168.1.1/32 det
<output omitted>
192.168.1.1/32, ubest/mbest: 1/0, attached, direct, pervasive
    *via 10.0.96.64%overlay-1, [1/0], 00:15:41, static, tag 4294967292
         recursive next hop: 10.0.96.64/32%overlay-1
         vrf crossing information:  VNID:0x288000 ClassId:0x1562 Flush#:0x3

Несложно догадаться, что 0x288000 (2654208) – это VNID, назначенный TestVrf1:

ClassID 0x1562 (5474) соответствует Provider EPG:

Глобальный pcTag (5475) назначен External EPG на L3Out. Вы же помните, что контракт всегда реализует consumer leaf? Так вот, ingress enforcement контракта (настройка VRF) требует обратного – выполнять контракт на compute leaf вместо border leaf. В нашем случае compute leaf является provider leaf; чтобы применить контракт, он должен знать pcTag на L3Out, а это значит, что L3Out EPG должен иметь глобальный pcTag.

Запутались? Вообще непонятно, где же в конечном счёте происходит применение контракта? Давайте разбираться. Заглянем в душу border leaf, чтобы понять, какие политики он реализует со своей стороны:

Leaf-103# show zoning-rule scope 2818048
+---------+--------+--------+----------+----------------+---------+---------+-------------------------+----------+------------------------+
| Rule ID | SrcEPG | DstEPG | FilterID |      Dir       |  operSt |  Scope  |           Name          |  Action  |        Priority        |
+---------+--------+--------+----------+----------------+---------+---------+-------------------------+----------+------------------------+
|   4102  |   0    |   0    | implarp  |    uni-dir     | enabled | 2818048 |                         |  permit  |   any_any_filter(17)   |
|   4099  |   0    |   0    | implicit |    uni-dir     | enabled | 2818048 |                         | deny,log |    any_any_any(21)     |
|   4098  |   0    |   15   | implicit |    uni-dir     | enabled | 2818048 |                         | deny,log |  any_vrf_any_deny(22)  |
|   4108  |  5474  |   0    | implicit |    uni-dir     | enabled | 2818048 |                         | deny,log | shsrc_any_any_deny(12) |
|   4111  |  5474  |  5475  |    4     | uni-dir-ignore | enabled | 2818048 | TestTenant:TestContract |  permit  |     fully_qual(7)      |
|   4110  |  5475  |  5474  |    4     |     bi-dir     | enabled | 2818048 | TestTenant:TestContract |  permit  |     fully_qual(7)      |
+---------+--------+--------+----------+----------------+---------+---------+-------------------------+----------+------------------------+

Пройдёмся по правилам фильтрации для TestVrf2:

  • ID 4102: разрешает ARP, from any to any;

  • ID 4099: запрещает любой трафик, from any to any;

  • ID 4098: запрещает любой трафик, from any to 0.0.0.0/0, который анонсирует L3Out внутрь фабрики (запись должна присутствовать в случае, если настроена Preferred Group);

  • ID 4108: запрещает любой трафик, from Provider (с глобальным pcTag) to any – всегда присутствует в consumer VRF, чтобы отбрасывать трафик, не попадающий в контракт (provider VRF не реализует обычно политику);

  • ID 4110-4111: разрешает трафик между Provider и L3Out EPG согласно фильтру 4.

 С border leaf покончено, самое время посмотреть на provider leaf.

Leaf-101# show ip route vrf TestTenant:TestVrf1 
<output omitted>
2.2.2.2/32, ubest/mbest: 1/0
    *via 10.0.88.68%overlay-1, [200/5], 00:20:18, bgp-65000, internal, tag 65000
192.168.1.0/24, ubest/mbest: 1/0, attached, direct, pervasive, dcs
    *via 10.0.96.64%overlay-1, [1/0], 00:16:31, static
192.168.1.1/32, ubest/mbest: 1/0, attached, direct, pervasive, dcs
    *via 10.0.96.64%overlay-1, [1/0], 00:17:58, static
192.168.1.254/32, ubest/mbest: 1/0, attached, pervasive
    *via 192.168.1.254, Vlan4, [0/0], 00:16:31, local, local

Leaf-101#
Leaf-101# show ip route vrf TestTenant:TestVrf1 2.2.2.2/32 det
<output omitted>
2.2.2.2/32, ubest/mbest: 1/0
    *via 10.0.88.68%overlay-1, [200/5], 00:20:28, bgp-65000, internal, tag 65000
         client-specific data: 1d      
         recursive next hop: 10.0.88.68/32%overlay-1
          BGP extended route information: BGP origin AS 65000 BGP peer AS 65000 rw-vnid: 0x2b0000 table-id: 0xe rw-mac: 0

Ситуация с compute leaf несколько отличается от того, что мы уже видели. За распространение внешних префиксов внутри фабрики отвечает MP-BGP. Обновления BGP содержат префикс и соответствующий VNID, поэтому специальный статический маршрут для перезаписи VNID не нужен. ClassID, по всей видимости, всё так же задан статически, так как в BGP соответствующей информации нет. Более того, соответствие между pcTag и префиксом можно получить совершенно другой командой:

Leaf-101# show system internal policy-mgr prefix 
Requested prefix data

Vrf-Vni VRF-Id Table-Id Table-State  VRF-Name                    Addr                                Class Shared Remote Complete Svc_ena
======= ======  =========== =======  ============================ ================================= ====== ====== ====== ======== ========
2752512 7      0x7           Up     common:default                                       0.0.0.0/0   15      False  False  False    False   
2752512 7      0x80000007    Up     common:default                                            ::/0   15      False  False  False    False   
2654208 15     0x8000000f    Up     TestTenant:TestVrf1                                       ::/0   15      False  False  False    False   
2654208 15     0xf           Up     TestTenant:TestVrf1                                  0.0.0.0/0   15      False  False  False    False   
2654208 15     0xf           Up     TestTenant:TestVrf1                                  2.2.2.2/32  5475    True   True   False    False   

Что насчёт контрактов? Применяет ли их и provider leaf, раз уж L3Out EPG получил глобальный pcTag?

Leaf-101# show zoning-rule scope 2654208
+---------+--------+--------+----------+----------------+---------+---------+-------------------------+-----------------+----------------------+
| Rule ID | SrcEPG | DstEPG | FilterID |      Dir       |  operSt |  Scope  |           Name          |      Action     |       Priority       |
+---------+--------+--------+----------+----------------+---------+---------+-------------------------+-----------------+----------------------+
|   4104  |   0    | 49153  | implicit |    uni-dir     | enabled | 2654208 |                         |      permit     |   any_dest_any(16)   |
|   4101  |   0    |   0    | implarp  |    uni-dir     | enabled | 2654208 |                         |      permit     |  any_any_filter(17)  |
|   4103  |   0    |   0    | implicit |    uni-dir     | enabled | 2654208 |                         |     deny,log    |   any_any_any(21)    |
|   4102  |   0    |   15   | implicit |    uni-dir     | enabled | 2654208 |                         |     deny,log    | any_vrf_any_deny(22) |
|   4113  |  5475  |  5474  |    4     |     bi-dir     | enabled | 2654208 | TestTenant:TestContract |      permit     |    fully_qual(7)     |
|   4115  |  5474  |   14   | implicit |    uni-dir     | enabled | 2654208 |                         | permit_override |    src_dst_any(9)    |
|   4111  |  5474  |  5475  |    4     | uni-dir-ignore | enabled | 2654208 | TestTenant:TestContract |      permit     |    fully_qual(7)     |
+---------+--------+--------+----------+----------------+---------+---------+-------------------------+-----------------+----------------------+

Похоже, что и compute leaf, и provider leaf реализуют контракты:

  • ID 4104: разрешает любой трафик, from any to TestBD1 – flooding внутри BD;

  • ID 4101: разрешает ARP, from any to any;

  • ID 4103: запрещает любой трафик, from any to any;

  • ID 4102: запрещает любой трафик, from any to 0.0.0.0/0, который анонсирует L3Out внутрь фабрики (запись должна присутствовать в случае, если настроена Preferred Group);

  • ID 4115: разрешает обратный трафик, from Provider to consumer VRF;

  • ID 4111, 4113: разрешает трафик между Provider и L3Out EPG согласно фильтру 4.

Впрочем, это не означает, что применение политики происходит дважды. Как только пакет успешно проходит все фильтры, коммутатор выставляет биты SP и DP в заголовке iVXLAN исходящего пакета, так что двойной работы никто не выполняет. Неочевидность точки применения политики – безусловно, лишнее использование TCAM – пожалуй, да, но излишней обработки пакетов не происходит.

Вернёмся к нашим баранам. Представим, что TestEPG жутко понадобилось начать общаться с Provider, но есть некое ограничение, которое делает использование контракта не самым удобным решением. В такой ситуации кажется, что Preferred Group является подходящим инструментом, поскольку члены такой группы могут общаться между собой без определения какого-либо контракта. На данном этапе EPG входят в группу, но сама функция не включена на уровне VRF, так что никакого эффекта мы не видим. Включим Preferred Group через GUI, так как я не нашёл соответствующей опции в Terraform (provider версии 2.5.2).

Сломалась ли связность, как предсказывал white paper?

Host# ping 192.168.1.1 vrf Provider source 2.2.2.2
PING 192.168.1.1 (192.168.1.1) from 2.2.2.2: 56 data bytes
64 bytes from 192.168.1.1: icmp_seq=0 ttl=252 time=1.832 ms
64 bytes from 192.168.1.1: icmp_seq=1 ttl=252 time=1.254 ms
64 bytes from 192.168.1.1: icmp_seq=2 ttl=252 time=1.285 ms
64 bytes from 192.168.1.1: icmp_seq=3 ttl=252 time=1.529 ms
64 bytes from 192.168.1.1: icmp_seq=4 ttl=252 time=1.579 ms

--- 192.168.1.1 ping statistics ---
5 packets transmitted, 5 packets received, 0.00% packet loss
round-trip min/avg/max = 1.254/1.495/1.832 ms
Host#
Host# ping 192.168.1.254 vrf TestEPG
PING 192.168.1.254 (192.168.1.254): 56 data bytes
64 bytes from 192.168.1.254: icmp_seq=0 ttl=63 time=1.256 ms
64 bytes from 192.168.1.254: icmp_seq=1 ttl=63 time=0.943 ms
64 bytes from 192.168.1.254: icmp_seq=2 ttl=63 time=1.002 ms
64 bytes from 192.168.1.254: icmp_seq=3 ttl=63 time=1.02 ms
64 bytes from 192.168.1.254: icmp_seq=4 ttl=63 time=0.993 ms

--- 192.168.1.254 ping statistics ---
5 packets transmitted, 5 packets received, 0.00% packet loss
round-trip min/avg/max = 0.943/1.042/1.256 ms
Host#
Host# ping 192.168.1.2 vrf Provider
PING 192.168.1.2 (192.168.1.2): 56 data bytes
Request 0 timed out
Request 1 timed out
Request 2 timed out
Request 3 timed out
Request 4 timed out

--- 192.168.1.2 ping statistics ---
5 packets transmitted, 0 packets received, 100.00% packet loss

2.2.2.2/32 всё ещё может добраться до 192.168.1.1/32, а вот от Preferred Group толку нет. Уберём Provider из контракта:

Host# ping 192.168.1.2 vrf A
PING 192.168.1.2 (192.168.1.2): 56 data bytes
64 bytes from 192.168.1.2: icmp_seq=0 ttl=254 time=1.926 ms
64 bytes from 192.168.1.2: icmp_seq=1 ttl=254 time=1.484 ms
64 bytes from 192.168.1.2: icmp_seq=2 ttl=254 time=1.248 ms
64 bytes from 192.168.1.2: icmp_seq=3 ttl=254 time=1.272 ms
64 bytes from 192.168.1.2: icmp_seq=4 ttl=254 time=1.521 ms

--- 192.168.1.2 ping statistics ---
5 packets transmitted, 5 packets received, 0.00% packet loss
round-trip min/avg/max = 1.248/1.49/1.926 ms

Вуаля, очнулась связность в Preferred Group, хоть и ценой невозможности предоставить контракт L3Out из другого VRF.

+---------+--------+--------+----------+---------+---------+---------+------+----------+----------------------------+
| Rule ID | SrcEPG | DstEPG | FilterID |   Dir   |  operSt |  Scope  | Name |  Action  |          Priority          |
+---------+--------+--------+----------+---------+---------+---------+------+----------+----------------------------+
|   4104  |   0    | 49153  | implicit | uni-dir | enabled | 2654208 |      |  permit  |      any_dest_any(16)      |
|   4101  |   0    |   0    | implarp  | uni-dir | enabled | 2654208 |      |  permit  |     any_any_filter(17)     |
|   4103  |   0    |   0    | implicit | uni-dir | enabled | 2654208 |      |  permit  | grp_any_any_any_permit(20) |
|   4102  |   0    |   15   | implicit | uni-dir | enabled | 2654208 |      | deny,log | grp_any_dest_any_deny(19)  |
|   4114  | 32770  |   0    | implicit | uni-dir | enabled | 2654208 |      | deny,log |  grp_src_any_any_deny(18)  |
+---------+--------+--------+----------+---------+---------+---------+------+----------+----------------------------+

Читатель мог заметить небольшие изменения в таблице с фильтрами. Посмотрим на правило ID 4103: вместо deny оно использует permit. Это следствие Preferred Group: трафик внутри VRF разрешён по умолчанию. Если бы у нас были ещё EPG, которые не входили бы в Preferred Group, то их трафик был бы явным образом запрещён. Трафик, приходящий из L3Out, использует pcTag, назначенный VRF; такой трафик не является частью Preferred Group, поэтому его также нужно явно запретить – так возикает правило ID 4114.

Вернёмся на шаг назад, когда контракт был ещё в силе:

Leaf-101# show zoning-rule scope 2654208
+---------+--------+--------+----------+----------------+---------+---------+-------------------------+-----------------+----------------------------+
| Rule ID | SrcEPG | DstEPG | FilterID |      Dir       |  operSt |  Scope  |           Name          |      Action     |          Priority          |
+---------+--------+--------+----------+----------------+---------+---------+-------------------------+-----------------+----------------------------+
|   4104  |   0    | 49153  | implicit |    uni-dir     | enabled | 2654208 |                         |      permit     |      any_dest_any(16)      |
|   4101  |   0    |   0    | implarp  |    uni-dir     | enabled | 2654208 |                         |      permit     |     any_any_filter(17)     |
|   4103  |   0    |   0    | implicit |    uni-dir     | enabled | 2654208 |                         |      permit     | grp_any_any_any_permit(20) |
|   4102  |   0    |   15   | implicit |    uni-dir     | enabled | 2654208 |                         |     deny,log    | grp_any_dest_any_deny(19)  |
|   4113  |  5475  |  5474  |    4     |     bi-dir     | enabled | 2654208 | TestTenant:TestContract |      permit     |       fully_qual(7)        |
|   4115  |  5474  |   14   | implicit |    uni-dir     | enabled | 2654208 |                         | permit_override |       src_dst_any(9)       |
|   4111  |  5474  |  5475  |    4     | uni-dir-ignore | enabled | 2654208 | TestTenant:TestContract |      permit     |       fully_qual(7)        |
|   4112  |  5474  |   0    | implicit |    uni-dir     | enabled | 2654208 |                         |     deny,log    |  grp_src_any_any_deny(18)  |
|   4114  | 32770  |   0    | implicit |    uni-dir     | enabled | 2654208 |                         |     deny,log    |  grp_src_any_any_deny(18)  |
+---------+--------+--------+----------+----------------+---------+---------+-------------------------+-----------------+----------------------------+

Если совместить фильтры, соответствующие применению контракта и включению Preferred Group, можно заметить одну «лишнюю» запись – ID 4112. Это и есть герой сегодняшней статьи: трафик от Provider к TestEPG попадает под это правило и отправляется в цифровой рай досрочно. Именно такой фильтр и указан в оригинальном описании дефекта. В white paper есть объяснение похожего фильтра, однако его приоритет отличается от того, что нам нужно (src_any_any_deny vs grp_src_any_any_deny). Мне так и не удалось найти объяснение, что именно означает это правило или почему оно появляется в описанном сценарии.

Впрочем, практического смысла в выполненном упражнении нет: ограничение явным образом упомянуто в документации. Сложные системы (такие, как ACI) нужно использовать только согласно одобренным сценариям, а не полагаться на разумность решения или общеприменимые соображения. Сложность только в том, чтобы найти такие сценарии, которые ещё и полностью отвечают требованиям, или же внимательно прочесть всю документацию по продукту. Что касается статьи, я надеюсь, мне удалось добавить немного контекста к дефекту, чтобы сузить его от загадочного ограничения в white paper до одной-единственной строчки в zoning table.

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

Канал в Телеграме: https://t.me/networking_it_ru

Источник: https://habr.com/ru/post/712012/


Интересные статьи

Интересные статьи

Привет! На связи Данила Соловьев, руководитель направления PHP в AGIMA. Для проджект-менеджеров и джуниор-разработчиков я подготовил небольшой гайд по тому, как ускорять работу крупных проектов на Бит...
Настройка любой площадки для CMS — это рутинный процесс, который должен быть доведен до автоматизма в каждой уважающей себя компании. А потому частенько воспринимается, как восход солнца — это происхо...
Мне было необходимо делать 2 раза в сутки бэкап сайта на «1С-Битрикс: Управление сайтом» (файлов и базы mysql) и хранить историю изменений за 90 дней. Сайт расположен на VDS под уп...
Приступая к животрепещущей теме резервного копирования на «Битрикс», прежде всего хотелось бы поблагодарить разработчиков, реализовавших автоматическое резервное копирование в облачное хранилище в вер...
Сегодня мы поговорим о статической маршрутизации и рассмотрим три темы: что такое статическая маршрутизация, как она настраивается и какая у неё альтернатива. Вы видите топологию сети, которая вк...