+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ #
+ # learn a remote EP in EPG 221
+ # packets coming from unknown remote EPs will be leant & redirected
+ #
+ vx_tun_l3 = VppGbpVxlanTunnel(
+ self, 444, rd1.rd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+ self.pg2.local_ip4)
+ vx_tun_l3.add_vpp_config()
+
+ c4 = VppGbpContract(
+ self, 402, epg_221.sclass, epg_220.sclass, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
+ sep1.ip4, sep1.epg.rd),
+ VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
+ sep2.ip4, sep2.epg.rd)]),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
+ sep3.ip6, sep3.epg.rd),
+ VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
+ sep4.ip6, sep4.epg.rd)])],
+ [ETH_P_IP, ETH_P_IPV6])
+ c4.add_vpp_config()
+
+ p = (Ether(src=self.pg7.remote_mac,
+ dst=self.pg7.local_mac) /
+ IP(src=self.pg7.remote_ip4,
+ dst=self.pg7.local_ip4) /
+ UDP(sport=1234, dport=48879) /
+ VXLAN(vni=444, gpid=441, flags=0x88) /
+ Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) /
+ IP(src="10.0.0.88", dst=ep1.ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ # unknown remote EP to local EP redirected
+ rxs = self.send_and_expect(self.pg7, [p], sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, "10.0.0.88")
+ self.assertEqual(rx[IP].dst, ep1.ip4)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self,
+ vx_tun_l3._sw_if_index,
+ ip="10.0.0.88"))
+
+ p = (Ether(src=self.pg7.remote_mac,
+ dst=self.pg7.local_mac) /
+ IP(src=self.pg7.remote_ip4,
+ dst=self.pg7.local_ip4) /
+ UDP(sport=1234, dport=48879) /
+ VXLAN(vni=444, gpid=441, flags=0x88) /
+ Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) /
+ IPv6(src="2001:10::88", dst=ep1.ip6) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ # unknown remote EP to local EP redirected (ipv6)
+ rxs = self.send_and_expect(self.pg7, [p], sep3.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep3.mac)
+ self.assertEqual(rx[IPv6].src, "2001:10::88")
+ self.assertEqual(rx[IPv6].dst, ep1.ip6)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self,
+ vx_tun_l3._sw_if_index,
+ ip="2001:10::88"))
+
+ #
+ # L3 switch from local to remote EP
+ #
+ p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
+ IP(src=ep1.ip4, dst="10.0.0.88") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))]
+ p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
+ IPv6(src=ep1.ip6, dst="2001:10::88") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))]
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, "10.0.0.88")
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep4.mac)
+ self.assertEqual(rx[IPv6].src, ep1.ip6)
+ self.assertEqual(rx[IPv6].dst, "2001:10::88")
+
+ #
+ # test the dst-ip hash mode
+ #
+ c5 = VppGbpContract(
+ self, 402, epg_220.sclass, epg_221.sclass, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
+ sep1.ip4, sep1.epg.rd),
+ VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
+ sep2.ip4, sep2.epg.rd)]),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
+ sep3.ip6, sep3.epg.rd),
+ VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
+ sep4.ip6, sep4.epg.rd)])],
+ [ETH_P_IP, ETH_P_IPV6])
+ c5.add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, "10.0.0.88")
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep3.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep3.mac)
+ self.assertEqual(rx[IPv6].src, ep1.ip6)
+ self.assertEqual(rx[IPv6].dst, "2001:10::88")
+
+ #
+ # a programmed remote SEP in EPG 320
+ #
+
+ # gbp vxlan tunnel for the remote SEP
+ vx_tun_l3_sep = VppGbpVxlanTunnel(
+ self, 555, rd1.rd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+ self.pg2.local_ip4)
+ vx_tun_l3_sep.add_vpp_config()
+
+ # remote SEP
+ sep5 = VppGbpEndpoint(self, vx_tun_l3_sep,
+ epg_320, None,
+ "12.0.0.10", "13.0.0.10",
+ "4001:10::10", "5001:10::10",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ mac=None)
+ sep5.add_vpp_config()
+
+ #
+ # local l3out redirect tests
+ #
+
+ # add local l3out
+ # the external bd
+ self.loop4.set_mac(self.router_mac)
+ b_lo4_ip4 = VppIpInterfaceBind(self, self.loop4, t4).add_vpp_config()
+ b_lo4_ip6 = VppIpInterfaceBind(self, self.loop4, t6).add_vpp_config()
+ ebd = VppBridgeDomain(self, 100)
+ ebd.add_vpp_config()
+ gebd = VppGbpBridgeDomain(self, ebd, rd1, self.loop4, None, None)
+ gebd.add_vpp_config()
+ # the external epg
+ eepg = VppGbpEndpointGroup(self, 888, 765, rd1, gebd,
+ None, gebd.bvi,
+ "10.1.0.128",
+ "2001:10:1::128",
+ VppGbpEndpointRetention(60))
+ eepg.add_vpp_config()
+ # add subnets to BVI
+ VppIpInterfaceAddress(
+ self,
+ gebd.bvi,
+ "10.1.0.128",
+ 24, bind=b_lo4_ip4).add_vpp_config()
+ VppIpInterfaceAddress(
+ self,
+ gebd.bvi,
+ "2001:10:1::128",
+ 64, bind=b_lo4_ip6).add_vpp_config()
+ # ... which are L3-out subnets
+ VppGbpSubnet(self, rd1, "10.1.0.0", 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=765).add_vpp_config()
+ VppGbpSubnet(self, rd1, "2001:10:1::128", 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=765).add_vpp_config()
+ # external endpoints
+ VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ eep1 = VppGbpEndpoint(self, self.vlan_100, eepg, None, "10.1.0.1",
+ "11.1.0.1", "2001:10:1::1", "3001:10:1::1",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
+ eep1.add_vpp_config()
+ VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ eep2 = VppGbpEndpoint(self, self.vlan_101, eepg, None, "10.1.0.2",
+ "11.1.0.2", "2001:10:1::2", "3001:10:1::2",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
+ eep2.add_vpp_config()
+
+ # external subnets reachable though eep1 and eep2 respectively
+ VppIpRoute(self, "10.220.0.0", 24,
+ [VppRoutePath(eep1.ip4, eep1.epg.bvi.sw_if_index)],
+ table_id=t4.table_id).add_vpp_config()
+ VppGbpSubnet(self, rd1, "10.220.0.0", 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220).add_vpp_config()
+ VppIpRoute(self, "10:220::", 64,
+ [VppRoutePath(eep1.ip6, eep1.epg.bvi.sw_if_index)],
+ table_id=t6.table_id).add_vpp_config()
+ VppGbpSubnet(self, rd1, "10:220::", 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220).add_vpp_config()
+ VppIpRoute(self, "10.221.0.0", 24,
+ [VppRoutePath(eep2.ip4, eep2.epg.bvi.sw_if_index)],
+ table_id=t4.table_id).add_vpp_config()
+ VppGbpSubnet(self, rd1, "10.221.0.0", 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4221).add_vpp_config()
+ VppIpRoute(self, "10:221::", 64,
+ [VppRoutePath(eep2.ip6, eep2.epg.bvi.sw_if_index)],
+ table_id=t6.table_id).add_vpp_config()
+ VppGbpSubnet(self, rd1, "10:221::", 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4221).add_vpp_config()
+
+ #
+ # l3out redirect to remote (known, then unknown) SEP
+ #
+
+ # packets from 1 external subnet to the other
+ p = [(Ether(src=eep1.mac, dst=self.router_mac) /
+ Dot1Q(vlan=100) /
+ IP(src="10.220.0.17", dst="10.221.0.65") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100)),
+ (Ether(src=eep1.mac, dst=self.router_mac) /
+ Dot1Q(vlan=100) /
+ IPv6(src="10:220::17", dst="10:221::65") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))]
+
+ # packets should be dropped in absence of contract
+ self.send_and_assert_no_replies(self.pg0, p)
+
+ # contract redirecting to sep5
+ VppGbpContract(
+ self, 402, 4220, 4221, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd,
+ sep5.ip4, sep5.epg.rd)]),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd,
+ sep5.ip6, sep5.epg.rd)])],
+ [ETH_P_IP, ETH_P_IPV6]).add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ # this should use the programmed remote leaf TEP
+ self.assertEqual(rx[VXLAN].vni, 555)
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[Dot1Q].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ # remote SEP: it is now an unknown remote SEP and should go
+ # to spine proxy
+ sep5.remove_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ # this should use the spine proxy TEP
+ self.assertEqual(rx[VXLAN].vni, epg_320.bd.uu_fwd.vni)
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[Dot1Q].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ #
+ # l3out redirect to local SEP
+ #
+
+ # change the contract between l3out to redirect to local SEPs
+ # instead of remote SEP
+ VppGbpContract(
+ self, 402, 4220, 4221, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
+ sep1.ip4, sep1.epg.rd)]),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
+ sep1.ip6, sep1.epg.rd)])],
+ [ETH_P_IP, ETH_P_IPV6]).add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p, sep1.itf)
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ rxip = rx[Ether].payload
+ txip = tx[Ether].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ #
+ # redirect remote EP to remote (known then unknown) SEP
+ #
+
+ # remote SEP known again
+ sep5.add_vpp_config()
+
+ # contract to redirect to learnt SEP
+ VppGbpContract(
+ self, 402, epg_221.sclass, epg_222.sclass, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd,
+ sep5.ip4, sep5.epg.rd)]),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
+ [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd,
+ sep5.ip6, sep5.epg.rd)])],
+ [ETH_P_IP, ETH_P_IPV6]).add_vpp_config()
+
+ # packets from unknown EP 221 to known EP in EPG 222
+ # should be redirected to known remote SEP
+ base = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) /
+ IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) /
+ UDP(sport=1234, dport=48879) /
+ VXLAN(vni=444, gpid=441, flags=0x88) /
+ Ether(src="00:22:22:22:22:44", dst=str(self.router_mac)))
+ p = [(base /
+ IP(src="10.0.1.100", dst=ep3.ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100)),
+ (base /
+ IPv6(src="2001:10::100", dst=ep3.ip6) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))]
+
+ # unknown remote EP to local EP redirected to known remote SEP
+ rxs = self.send_and_expect(self.pg7, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ # this should use the programmed remote leaf TEP
+ self.assertEqual(rx[VXLAN].vni, 555)
+ self.assertEqual(rx[VXLAN].gpid, epg_221.sclass)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[VXLAN][Ether].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ # endpoint learnt via the parent GBP-vxlan interface
+ self.assertTrue(find_gbp_endpoint(self,
+ vx_tun_l3._sw_if_index,
+ ip="10.0.1.100"))
+ self.assertTrue(find_gbp_endpoint(self,
+ vx_tun_l3._sw_if_index,
+ ip="2001:10::100"))
+
+ # remote SEP: it is now an unknown remote SEP and should go
+ # to spine proxy
+ sep5.remove_vpp_config()
+
+ # remote EP (coming from spine proxy) to local EP redirected to
+ # known remote SEP
+ rxs = self.send_and_expect(self.pg7, p, self.pg7)
+
+ for rx, tx in zip(rxs, p):
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ # this should use the spine proxy TEP
+ self.assertEqual(rx[VXLAN].vni, epg_320.bd.uu_fwd.vni)
+ self.assertEqual(rx[VXLAN].gpid, epg_221.sclass)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+ rxip = rx[VXLAN][Ether].payload
+ txip = tx[VXLAN][Ether].payload
+ self.assertEqual(rxip.src, txip.src)
+ self.assertEqual(rxip.dst, txip.dst)
+
+ #
+ # cleanup
+ #
+ self.pg7.unconfig_ip4()
+
+ def test_gbp_redirect_extended(self):
+ """ GBP Endpoint Redirect Extended """
+
+ self.vapi.cli("set logging class gbp level debug")
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ routed_dst_mac = "00:0c:0c:0c:0c:0c"
+ routed_src_mac = "00:22:bd:f8:19:ff"
+
+ learnt = [{'mac': '00:00:11:11:11:02',
+ 'ip': '10.0.1.2',
+ 'ip6': '2001:10::2'},
+ {'mac': '00:00:11:11:11:03',
+ 'ip': '10.0.1.3',
+ 'ip6': '2001:10::3'}]
+
+ #
+ # IP tables
+ #
+ t4 = VppIpTable(self, 1)
+ t4.add_vpp_config()
+ t6 = VppIpTable(self, 1, True)
+ t6.add_vpp_config()
+
+ # create IPv4 and IPv6 RD UU VxLAN-GBP TEP and bind them to the right
+ # VRF
+ rd_uu4 = VppVxlanGbpTunnel(
+ self,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ 114,
+ mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.
+ VXLAN_GBP_API_TUNNEL_MODE_L3))
+ rd_uu4.add_vpp_config()
+ VppIpInterfaceBind(self, rd_uu4, t4).add_vpp_config()
+
+ rd_uu6 = VppVxlanGbpTunnel(
+ self,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ 115,
+ mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.
+ VXLAN_GBP_API_TUNNEL_MODE_L3))
+ rd_uu6.add_vpp_config()
+ VppIpInterfaceBind(self, rd_uu6, t4).add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 2, 402, t4, t6, rd_uu4, rd_uu6)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+ self.loop1.set_mac(self.router_mac)
+ self.loop2.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ b_lo0_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ b_lo0_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+ b_lo1_ip4 = VppIpInterfaceBind(self, self.loop1, t4).add_vpp_config()
+ b_lo1_ip6 = VppIpInterfaceBind(self, self.loop1, t6).add_vpp_config()
+ b_lo2_ip4 = VppIpInterfaceBind(self, self.loop2, t4).add_vpp_config()
+ b_lo2_ip6 = VppIpInterfaceBind(self, self.loop2, t6).add_vpp_config()
+
+ #
+ # Pg7 hosts a BD's UU-fwd
+ #
+ self.pg7.config_ip4()
+ self.pg7.resolve_arp()
+
+ #
+ # a GBP bridge domains for the EPs
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0)
+ gbd1.add_vpp_config()
+
+ bd2 = VppBridgeDomain(self, 2)
+ bd2.add_vpp_config()
+ gbd2 = VppGbpBridgeDomain(self, bd2, rd1, self.loop1)
+ gbd2.add_vpp_config()
+
+ # ... and has a /32 and /128 applied
+ ip4_addr1 = VppIpInterfaceAddress(self, gbd1.bvi,
+ "10.0.0.128", 32,
+ bind=b_lo0_ip4).add_vpp_config()
+ ip6_addr1 = VppIpInterfaceAddress(self, gbd1.bvi,
+ "2001:10::128", 128,
+ bind=b_lo0_ip6).add_vpp_config()
+ ip4_addr2 = VppIpInterfaceAddress(self, gbd2.bvi,
+ "10.0.1.128", 32,
+ bind=b_lo1_ip4).add_vpp_config()
+ ip6_addr2 = VppIpInterfaceAddress(self, gbd2.bvi,
+ "2001:11::128", 128,
+ bind=b_lo1_ip6).add_vpp_config()
+
+ #
+ # The Endpoint-groups
+ #
+ epg_220 = VppGbpEndpointGroup(self, 220, 440, rd1, gbd1,
+ None, gbd1.bvi,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(60))
+ epg_220.add_vpp_config()
+ epg_221 = VppGbpEndpointGroup(self, 221, 441, rd1, gbd2,
+ None, gbd2.bvi,
+ "10.0.1.128",
+ "2001:11::128",
+ VppGbpEndpointRetention(60))
+ epg_221.add_vpp_config()
+
+ #
+ # a GBP bridge domains for the SEPs
+ #
+ bd_uu3 = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
+ self.pg7.remote_ip4, 116)
+ bd_uu3.add_vpp_config()
+
+ bd3 = VppBridgeDomain(self, 3)
+ bd3.add_vpp_config()
+ gbd3 = VppGbpBridgeDomain(self, bd3, rd1, self.loop2,
+ bd_uu3, learn=False)
+ gbd3.add_vpp_config()
+
+ ip4_addr3 = VppIpInterfaceAddress(self, gbd3.bvi,
+ "12.0.0.128", 32,
+ bind=b_lo2_ip4).add_vpp_config()
+ ip6_addr3 = VppIpInterfaceAddress(self, gbd3.bvi,
+ "4001:10::128", 128,
+ bind=b_lo2_ip6).add_vpp_config()
+
+ #
+ # self.logger.info(self.vapi.cli("show gbp bridge"))
+ # self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
+ # self.logger.info(self.vapi.cli("show gbp vxlan"))
+ # self.logger.info(self.vapi.cli("show int addr"))
+ #
+
+ #
+ # EPGs in which the service endpoints exist
+ #
+ epg_320 = VppGbpEndpointGroup(self, 320, 550, rd1, gbd3,
+ None, gbd3.bvi,
+ "12.0.0.128",
+ "4001:10::128",
+ VppGbpEndpointRetention(60))
+ epg_320.add_vpp_config()
+
+ #
+ # endpoints
+ #
+ ep1 = VppGbpEndpoint(self, self.pg0,
+ epg_220, None,
+ "10.0.0.1", "11.0.0.1",
+ "2001:10::1", "3001:10::1")
+ ep1.add_vpp_config()
+ ep2 = VppGbpEndpoint(self, self.pg1,
+ epg_221, None,
+ "10.0.1.1", "11.0.1.1",
+ "2001:11::1", "3001:11::1")
+ ep2.add_vpp_config()
+
+ #
+ # service endpoints
+ #
+ sep1 = VppGbpEndpoint(self, self.pg3,
+ epg_320, None,
+ "12.0.0.1", "13.0.0.1",
+ "4001:10::1", "5001:10::1")
+ sep2 = VppGbpEndpoint(self, self.pg4,
+ epg_320, None,
+ "12.0.0.2", "13.0.0.2",
+ "4001:10::2", "5001:10::2")
+
+ # sep1 and sep2 are not added to config yet
+ # they are unknown for now
+
+ #
+ # add routes to EPG subnets
+ #
+ VppGbpSubnet(self, rd1, "10.0.0.0", 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT
+ ).add_vpp_config()
+ VppGbpSubnet(self, rd1, "10.0.1.0", 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT
+ ).add_vpp_config()
+
+ #
+ # Local host to known local host in different BD
+ # with SFC contract (source and destination are in
+ # one node and service endpoint in another node)
+ #
+ p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
+ IP(src=ep1.ip4, dst=ep2.ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100)),
+ (Ether(src=ep2.mac, dst=str(self.router_mac)) /
+ IP(src=ep2.ip4, dst=ep1.ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))]
+ p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
+ IPv6(src=ep1.ip6, dst=ep2.ip6) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100)),
+ (Ether(src=ep2.mac, dst=str(self.router_mac)) /
+ IPv6(src=ep2.ip6, dst=ep1.ip6) /
+ UDP(sport=1234, dport=1230) /
+ Raw(b'\xa5' * 100))]
+
+ # should be dropped since no contract yet
+ self.send_and_assert_no_replies(self.pg0, [p4[0]])
+ self.send_and_assert_no_replies(self.pg0, [p6[0]])
+
+ #
+ # Add a contract with a rule to load-balance redirect via SEP1 and SEP2
+ # one of the next-hops is via an EP that is not known
+ #
+ rule4 = AclRule(is_permit=1, proto=17)
+ rule6 = AclRule(src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17)
+ acl = VppAcl(self, rules=[rule4, rule6])
+ acl.add_vpp_config()
+
+ #
+ # test the src-ip hash mode
+ #
+ c1 = VppGbpContract(
+ self, 402, epg_220.sclass, epg_221.sclass, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
+ sep1.ip4, sep1.epg.rd)]),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
+ sep1.ip6, sep1.epg.rd)])],
+ [ETH_P_IP, ETH_P_IPV6])
+ c1.add_vpp_config()
+
+ c2 = VppGbpContract(
+ self, 402, epg_221.sclass, epg_220.sclass, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
+ sep1.ip4, sep1.epg.rd)]),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
+ [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
+ sep1.ip6, sep1.epg.rd)])],
+ [ETH_P_IP, ETH_P_IPV6])
+ c2.add_vpp_config()
+
+ # ep1 <--> ep2 redirected through sep1
+ # sep1 is unknown
+ # packet is redirected to sep bd and then go through sep bd UU
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IP].src, ep1.ip4)
+ self.assertEqual(inner[IP].dst, ep2.ip4)
+
+ rxs = self.send_and_expect(self.pg1, p4[1] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IP].src, ep2.ip4)
+ self.assertEqual(inner[IP].dst, ep1.ip4)
+
+ rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IPv6].src, ep1.ip6)
+ self.assertEqual(inner[IPv6].dst, ep2.ip6)
+
+ rxs = self.send_and_expect(self.pg1, p6[1] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 116)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, sep1.mac)
+ self.assertEqual(inner[IPv6].src, ep2.ip6)
+ self.assertEqual(inner[IPv6].dst, ep1.ip6)
+
+ # configure sep1: it is now local
+ # packets between ep1 and ep2 are redirected locally
+ sep1.add_vpp_config()
+
+ rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ rxs = self.send_and_expect(self.pg1, p6[1] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, routed_src_mac)
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IPv6].src, ep2.ip6)
+ self.assertEqual(rx[IPv6].dst, ep1.ip6)
+
+ # packet coming from the l2 spine-proxy to sep1
+ p = (Ether(src=self.pg7.remote_mac,
+ dst=self.pg7.local_mac) /
+ IP(src=self.pg7.remote_ip4,
+ dst=self.pg7.local_ip4) /
+ UDP(sport=1234, dport=48879) /
+ VXLAN(vni=116, gpid=440, gpflags=0x08, flags=0x88) /
+ Ether(src=str(self.router_mac), dst=sep1.mac) /
+ IP(src=ep1.ip4, dst=ep2.ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ rxs = self.send_and_expect(self.pg7, [p] * 17, sep1.itf)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, sep1.mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ # contract for SEP to communicate with dst EP
+ c3 = VppGbpContract(
+ self, 402, epg_320.sclass, epg_221.sclass, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC)],
+ [ETH_P_IP, ETH_P_IPV6])
+ c3.add_vpp_config()
+
+ # temporarily remove ep2, so that ep2 is remote & unknown
+ ep2.remove_vpp_config()
+
+ # packet going back from sep1 to its original dest (ep2)
+ # as ep2 is now unknown (see above), it must go through
+ # the rd UU (packet is routed)
+
+ p1 = (Ether(src=sep1.mac, dst=self.router_mac) /
+ IP(src=ep1.ip4, dst=ep2.ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ rxs = self.send_and_expect(self.pg3, [p1] * 17, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 114)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # redirect policy has been applied
+ inner = rx[VXLAN].payload
+ self.assertEqual(inner[Ether].src, routed_src_mac)
+ self.assertEqual(inner[Ether].dst, routed_dst_mac)
+ self.assertEqual(inner[IP].src, ep1.ip4)
+ self.assertEqual(inner[IP].dst, ep2.ip4)
+
+ self.logger.info(self.vapi.cli("show bridge 3 detail"))
+ sep1.remove_vpp_config()
+
+ self.logger.info(self.vapi.cli("show bridge 1 detail"))
+ self.logger.info(self.vapi.cli("show bridge 2 detail"))
+
+ # re-add ep2: it is local again :)
+ ep2.add_vpp_config()
+
+ # packet coming back from the remote sep through rd UU
+ p2 = (Ether(src=self.pg7.remote_mac,
+ dst=self.pg7.local_mac) /
+ IP(src=self.pg7.remote_ip4,
+ dst=self.pg7.local_ip4) /
+ UDP(sport=1234, dport=48879) /
+ VXLAN(vni=114, gpid=441, gpflags=0x09, flags=0x88) /
+ Ether(src=str(self.router_mac), dst=self.router_mac) /
+ IP(src=ep1.ip4, dst=ep2.ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ rxs = self.send_and_expect(self.pg7, [p2], self.pg1)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, self.pg1.remote_mac)
+ self.assertEqual(rx[IP].src, ep1.ip4)
+ self.assertEqual(rx[IP].dst, ep2.ip4)
+
+ #
+ # bd_uu2.add_vpp_config()
+ #
+
+ #
+ # cleanup
+ #
+ c1.remove_vpp_config()
+ c2.remove_vpp_config()
+ c3.remove_vpp_config()
+ self.pg7.unconfig_ip4()
+
+ def test_gbp_l3_out(self):
+ """ GBP L3 Out """
+
+ ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
+ self.vapi.cli("set logging class gbp level debug")
+
+ routed_dst_mac = "00:0c:0c:0c:0c:0c"
+ routed_src_mac = "00:22:bd:f8:19:ff"
+
+ #
+ # IP tables
+ #
+ t4 = VppIpTable(self, 1)
+ t4.add_vpp_config()
+ t6 = VppIpTable(self, 1, True)
+ t6.add_vpp_config()
+
+ rd1 = VppGbpRouteDomain(self, 2, 55, t4, t6)
+ rd1.add_vpp_config()
+
+ self.loop0.set_mac(self.router_mac)
+
+ #
+ # Bind the BVI to the RD
+ #
+ b_ip4 = VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
+ b_ip6 = VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
+
+ #
+ # Pg7 hosts a BD's BUM
+ # Pg1 some other l3 interface
+ #
+ self.pg7.config_ip4()
+ self.pg7.resolve_arp()
+
+ #
+ # a multicast vxlan-gbp tunnel for broadcast in the BD
+ #
+ tun_bm = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
+ "239.1.1.1", 88,
+ mcast_itf=self.pg7)
+ tun_bm.add_vpp_config()
+
+ #
+ # a GBP external bridge domains for the EPs
+ #
+ bd1 = VppBridgeDomain(self, 1)
+ bd1.add_vpp_config()
+ gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, None, tun_bm)
+ gbd1.add_vpp_config()
+
+ #
+ # The Endpoint-groups in which the external endpoints exist
+ #
+ epg_220 = VppGbpEndpointGroup(self, 220, 113, rd1, gbd1,
+ None, gbd1.bvi,
+ "10.0.0.128",
+ "2001:10::128",
+ VppGbpEndpointRetention(4))
+ epg_220.add_vpp_config()
+
+ # the BVIs have the subnets applied ...
+ ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128",
+ 24, bind=b_ip4).add_vpp_config()
+ ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128",
+ 64, bind=b_ip6).add_vpp_config()
+
+ # ... which are L3-out subnets
+ l3o_1 = VppGbpSubnet(
+ self, rd1, "10.0.0.0", 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=113)
+ l3o_1.add_vpp_config()
+
+ #
+ # an external interface attached to the outside world and the
+ # external BD
+ #
+ VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
+ vlan_144 = VppDot1QSubint(self, self.pg0, 144)
+ vlan_144.admin_up()
+ # vlan_102 is not poped
+
+ #
+ # an unicast vxlan-gbp for inter-RD traffic
+ #
+ vx_tun_l3 = VppGbpVxlanTunnel(
+ self, 444, rd1.rd_id,
+ VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
+ self.pg2.local_ip4)
+ vx_tun_l3.add_vpp_config()
+
+ #
+ # External Endpoints
+ #
+ eep1 = VppGbpEndpoint(self, self.vlan_100,
+ epg_220, None,
+ "10.0.0.1", "11.0.0.1",
+ "2001:10::1", "3001::1",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
+ eep1.add_vpp_config()
+ eep2 = VppGbpEndpoint(self, self.vlan_101,
+ epg_220, None,
+ "10.0.0.2", "11.0.0.2",
+ "2001:10::2", "3001::2",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
+ eep2.add_vpp_config()
+ eep3 = VppGbpEndpoint(self, self.vlan_102,
+ epg_220, None,
+ "10.0.0.3", "11.0.0.3",
+ "2001:10::3", "3001::3",
+ ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
+ eep3.add_vpp_config()
+
+ #
+ # A remote external endpoint
+ #
+ rep = VppGbpEndpoint(self, vx_tun_l3,
+ epg_220, None,
+ "10.0.0.101", "11.0.0.101",
+ "2001:10::101", "3001::101",
+ ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
+ self.pg7.local_ip4,
+ self.pg7.remote_ip4,
+ mac=None)
+ rep.add_vpp_config()
+
+ #
+ # EP1 impersonating EP3 is dropped
+ #
+ p = (Ether(src=eep1.mac, dst="ff:ff:ff:ff:ff:ff") /
+ Dot1Q(vlan=100) /
+ ARP(op="who-has",
+ psrc="10.0.0.3", pdst="10.0.0.128",
+ hwsrc=eep1.mac, hwdst="ff:ff:ff:ff:ff:ff"))
+ self.send_and_assert_no_replies(self.pg0, p)
+
+ #
+ # ARP packet from External EPs are accepted and replied to
+ #
+ p_arp = (Ether(src=eep1.mac, dst="ff:ff:ff:ff:ff:ff") /
+ Dot1Q(vlan=100) /
+ ARP(op="who-has",
+ psrc=eep1.ip4, pdst="10.0.0.128",
+ hwsrc=eep1.mac, hwdst="ff:ff:ff:ff:ff:ff"))
+ rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0)
+
+ #
+ # ARP packet from host in remote subnet are accepted and replied to
+ #
+ p_arp = (Ether(src=eep3.mac, dst="ff:ff:ff:ff:ff:ff") /
+ Dot1Q(vlan=102) /
+ ARP(op="who-has",
+ psrc=eep3.ip4, pdst="10.0.0.128",
+ hwsrc=eep3.mac, hwdst="ff:ff:ff:ff:ff:ff"))
+ rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0)
+
+ #
+ # packets destined to unknown addresses in the BVI's subnet
+ # are ARP'd for
+ #
+ p4 = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IP(src="10.0.0.1", dst="10.0.0.88") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+ p6 = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IPv6(src="2001:10::1", dst="2001:10::88") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ rxs = self.send_and_expect(self.pg0, p4 * 1, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, "239.1.1.1")
+ self.assertEqual(rx[VXLAN].vni, 88)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # policy was applied to the original IP packet
+ self.assertEqual(rx[VXLAN].gpid, 113)
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ self.assertFalse(rx[VXLAN].gpflags.D)
+
+ inner = rx[VXLAN].payload
+
+ self.assertTrue(inner.haslayer(ARP))
+
+ #
+ # remote to external
+ #
+ p = (Ether(src=self.pg7.remote_mac,
+ dst=self.pg7.local_mac) /
+ IP(src=self.pg7.remote_ip4,
+ dst=self.pg7.local_ip4) /
+ UDP(sport=1234, dport=48879) /
+ VXLAN(vni=444, gpid=113, flags=0x88) /
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
+ IP(src="10.0.0.101", dst="10.0.0.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
+
+ #
+ # local EP pings router
+ #
+ p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IP(src=eep1.ip4, dst="10.0.0.128") /
+ ICMP(type='echo-request'))
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, eep1.mac)
+ self.assertEqual(rx[Dot1Q].vlan, 100)
+
+ #
+ # local EP pings other local EP
+ #
+ p = (Ether(src=eep1.mac, dst=eep2.mac) /
+ Dot1Q(vlan=100) /
+ IP(src=eep1.ip4, dst=eep2.ip4) /
+ ICMP(type='echo-request'))
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, eep1.mac)
+ self.assertEqual(rx[Ether].dst, eep2.mac)
+ self.assertEqual(rx[Dot1Q].vlan, 101)
+
+ #
+ # local EP pings router w/o vlan tag poped
+ #
+ p = (Ether(src=eep3.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=102) /
+ IP(src=eep3.ip4, dst="10.0.0.128") /
+ ICMP(type='echo-request'))
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, self.vlan_102.remote_mac)
+
+ #
+ # A ip4 subnet reachable through the external EP1
+ #
+ ip_220 = VppIpRoute(self, "10.220.0.0", 24,
+ [VppRoutePath(eep1.ip4,
+ eep1.epg.bvi.sw_if_index)],
+ table_id=t4.table_id)
+ ip_220.add_vpp_config()
+
+ l3o_220 = VppGbpSubnet(
+ self, rd1, "10.220.0.0", 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220)
+ l3o_220.add_vpp_config()
+
+ #
+ # An ip6 subnet reachable through the external EP1
+ #
+ ip6_220 = VppIpRoute(self, "10:220::", 64,
+ [VppRoutePath(eep1.ip6,
+ eep1.epg.bvi.sw_if_index)],
+ table_id=t6.table_id)
+ ip6_220.add_vpp_config()
+
+ l3o6_220 = VppGbpSubnet(
+ self, rd1, "10:220::", 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4220)
+ l3o6_220.add_vpp_config()
+
+ #
+ # A subnet reachable through the external EP2
+ #
+ ip_221 = VppIpRoute(self, "10.221.0.0", 24,
+ [VppRoutePath(eep2.ip4,
+ eep2.epg.bvi.sw_if_index)],
+ table_id=t4.table_id)
+ ip_221.add_vpp_config()
+
+ l3o_221 = VppGbpSubnet(
+ self, rd1, "10.221.0.0", 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4221)
+ l3o_221.add_vpp_config()
+
+ #
+ # ping between hosts in remote subnets
+ # dropped without a contract
+ #
+ p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IP(src="10.220.0.1", dst="10.221.0.1") /
+ ICMP(type='echo-request'))
+
+ self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ #
+ # contract for the external nets to communicate
+ #
+ rule4 = AclRule(is_permit=1, proto=17)
+ rule6 = AclRule(src_prefix=IPv6Network((0, 0)),
+ dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17)
+ acl = VppAcl(self, rules=[rule4, rule6])
+ acl.add_vpp_config()
+
+ #
+ # A contract with the wrong scope is not matched
+ #
+ c_44 = VppGbpContract(
+ self, 44, 4220, 4221, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ []),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [])],
+ [ETH_P_IP, ETH_P_IPV6])
+ c_44.add_vpp_config()
+ self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ c1 = VppGbpContract(
+ self, 55, 4220, 4221, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ []),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [])],
+ [ETH_P_IP, ETH_P_IPV6])
+ c1.add_vpp_config()
+
+ #
+ # Contracts allowing ext-net 200 to talk with external EPs
+ #
+ c2 = VppGbpContract(
+ self, 55, 4220, 113, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ []),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [])],
+ [ETH_P_IP, ETH_P_IPV6])
+ c2.add_vpp_config()
+ c3 = VppGbpContract(
+ self, 55, 113, 4220, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ []),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [])],
+ [ETH_P_IP, ETH_P_IPV6])
+ c3.add_vpp_config()
+
+ #
+ # ping between hosts in remote subnets
+ #
+ p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IP(src="10.220.0.1", dst="10.221.0.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, str(self.router_mac))
+ self.assertEqual(rx[Ether].dst, eep2.mac)
+ self.assertEqual(rx[Dot1Q].vlan, 101)
+
+ # we did not learn these external hosts
+ self.assertFalse(find_gbp_endpoint(self, ip="10.220.0.1"))
+ self.assertFalse(find_gbp_endpoint(self, ip="10.221.0.1"))
+
+ #
+ # from remote external EP to local external EP
+ #
+ p = (Ether(src=self.pg7.remote_mac,
+ dst=self.pg7.local_mac) /
+ IP(src=self.pg7.remote_ip4,
+ dst=self.pg7.local_ip4) /
+ UDP(sport=1234, dport=48879) /
+ VXLAN(vni=444, gpid=113, flags=0x88) /
+ Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
+ IP(src="10.0.0.101", dst="10.220.0.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
+
+ #
+ # ping from an external host to the remote external EP
+ #
+ p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IP(src="10.220.0.1", dst=rep.ip4) /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ rxs = self.send_and_expect(self.pg0, p * 1, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 444)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # the sclass of the ext-net the packet came from
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ # policy was applied to the original IP packet
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ # since it's an external host the reciever should not learn it
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ inner = rx[VXLAN].payload
+ self.assertEqual(inner[IP].src, "10.220.0.1")
+ self.assertEqual(inner[IP].dst, rep.ip4)
+
+ #
+ # An external subnet reachable via the remote external EP
+ #
+
+ #
+ # first the VXLAN-GBP tunnel over which it is reached
+ #
+ vx_tun_r1 = VppVxlanGbpTunnel(
+ self, self.pg7.local_ip4,
+ self.pg7.remote_ip4, 445,
+ mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.
+ VXLAN_GBP_API_TUNNEL_MODE_L3))
+ vx_tun_r1.add_vpp_config()
+ VppIpInterfaceBind(self, vx_tun_r1, t4).add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
+
+ #
+ # then the special adj to resolve through on that tunnel
+ #
+ n1 = VppNeighbor(self,
+ vx_tun_r1.sw_if_index,
+ "00:0c:0c:0c:0c:0c",
+ self.pg7.remote_ip4)
+ n1.add_vpp_config()
+
+ #
+ # the route via the adj above
+ #
+ ip_222 = VppIpRoute(self, "10.222.0.0", 24,
+ [VppRoutePath(self.pg7.remote_ip4,
+ vx_tun_r1.sw_if_index)],
+ table_id=t4.table_id)
+ ip_222.add_vpp_config()
+
+ l3o_222 = VppGbpSubnet(
+ self, rd1, "10.222.0.0", 24,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4222)
+ l3o_222.add_vpp_config()
+
+ #
+ # ping between hosts in local and remote external subnets
+ # dropped without a contract
+ #
+ p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IP(src="10.220.0.1", dst="10.222.0.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ rxs = self.send_and_assert_no_replies(self.pg0, p * 1)
+
+ #
+ # Add contracts ext-nets for 220 -> 222
+ #
+ c4 = VppGbpContract(
+ self, 55, 4220, 4222, acl.acl_index,
+ [VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ []),
+ VppGbpContractRule(
+ VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
+ VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
+ [])],
+ [ETH_P_IP, ETH_P_IPV6])
+ c4.add_vpp_config()
+
+ #
+ # ping from host in local to remote external subnets
+ #
+ p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IP(src="10.220.0.1", dst="10.222.0.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100))
+
+ rxs = self.send_and_expect(self.pg0, p * 3, self.pg7)
+
+ for rx in rxs:
+ self.assertEqual(rx[Ether].src, self.pg7.local_mac)
+ self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
+ self.assertEqual(rx[IP].src, self.pg7.local_ip4)
+ self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
+ self.assertEqual(rx[VXLAN].vni, 445)
+ self.assertTrue(rx[VXLAN].flags.G)
+ self.assertTrue(rx[VXLAN].flags.Instance)
+ # the sclass of the ext-net the packet came from
+ self.assertEqual(rx[VXLAN].gpid, 4220)
+ # policy was applied to the original IP packet
+ self.assertTrue(rx[VXLAN].gpflags.A)
+ # since it's an external host the reciever should not learn it
+ self.assertTrue(rx[VXLAN].gpflags.D)
+ inner = rx[VXLAN].payload
+ self.assertEqual(inner[Ether].dst, "00:0c:0c:0c:0c:0c")
+ self.assertEqual(inner[IP].src, "10.220.0.1")
+ self.assertEqual(inner[IP].dst, "10.222.0.1")
+
+ #
+ # make the external subnet ECMP
+ #
+ vx_tun_r2 = VppVxlanGbpTunnel(
+ self, self.pg7.local_ip4,
+ self.pg7.remote_ip4, 446,
+ mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.
+ VXLAN_GBP_API_TUNNEL_MODE_L3))
+ vx_tun_r2.add_vpp_config()
+ VppIpInterfaceBind(self, vx_tun_r2, t4).add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
+
+ n2 = VppNeighbor(self,
+ vx_tun_r2.sw_if_index,
+ "00:0c:0c:0c:0c:0c",
+ self.pg7.remote_ip4)
+ n2.add_vpp_config()
+
+ ip_222.modify([VppRoutePath(self.pg7.remote_ip4,
+ vx_tun_r1.sw_if_index),
+ VppRoutePath(self.pg7.remote_ip4,
+ vx_tun_r2.sw_if_index)])
+
+ #
+ # now expect load-balance
+ #
+ p = [(Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IP(src="10.220.0.1", dst="10.222.0.1") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100)),
+ (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IP(src="10.220.0.1", dst="10.222.0.1") /
+ UDP(sport=1222, dport=1235) /
+ Raw(b'\xa5' * 100))]
+
+ rxs = self.send_and_expect(self.pg0, p, self.pg7)
+
+ self.assertEqual(rxs[0][VXLAN].vni, 445)
+ self.assertEqual(rxs[1][VXLAN].vni, 446)
+
+ #
+ # Same LB test for v6
+ #
+ n3 = VppNeighbor(self,
+ vx_tun_r1.sw_if_index,
+ "00:0c:0c:0c:0c:0c",
+ self.pg7.remote_ip6)
+ n3.add_vpp_config()
+ n4 = VppNeighbor(self,
+ vx_tun_r2.sw_if_index,
+ "00:0c:0c:0c:0c:0c",
+ self.pg7.remote_ip6)
+ n4.add_vpp_config()
+
+ ip_222_6 = VppIpRoute(self, "10:222::", 64,
+ [VppRoutePath(self.pg7.remote_ip6,
+ vx_tun_r1.sw_if_index),
+ VppRoutePath(self.pg7.remote_ip6,
+ vx_tun_r2.sw_if_index)],
+ table_id=t6.table_id)
+ ip_222_6.add_vpp_config()
+
+ l3o_222_6 = VppGbpSubnet(
+ self, rd1, "10:222::", 64,
+ VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
+ sclass=4222)
+ l3o_222_6.add_vpp_config()
+
+ p = [(Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IPv6(src="10:220::1", dst="10:222::1") /
+ UDP(sport=1234, dport=1234) /
+ Raw(b'\xa5' * 100)),
+ (Ether(src=eep1.mac, dst=str(self.router_mac)) /
+ Dot1Q(vlan=100) /
+ IPv6(src="10:220::1", dst="10:222::1") /
+ UDP(sport=7777, dport=8881) /
+ Raw(b'\xa5' * 100))]