Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру 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