В последней статье мы обсудили детали реализации 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/
Добавить комментарий