acl: API cleanup
[vpp.git] / src / plugins / gbp / test / test_gbp.py
1 #!/usr/bin/env python3
2
3 from socket import AF_INET, AF_INET6, inet_pton, inet_ntop
4 import unittest
5 from ipaddress import ip_address, IPv4Network, IPv6Network
6
7 from scapy.packet import Raw
8 from scapy.layers.l2 import Ether, ARP, Dot1Q
9 from scapy.layers.inet import IP, UDP, ICMP
10 from scapy.layers.inet6 import IPv6, ICMPv6ND_NS, ICMPv6NDOptSrcLLAddr, \
11     ICMPv6ND_NA, ICMPv6EchoRequest
12 from scapy.utils6 import in6_getnsma, in6_getnsmac
13 from scapy.layers.vxlan import VXLAN
14 from scapy.data import ETH_P_IP, ETH_P_IPV6, ETH_P_ARP
15
16 from framework import VppTestCase, VppTestRunner
17 from vpp_object import VppObject
18 from vpp_interface import VppInterface
19 from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpTable, \
20     VppIpInterfaceAddress, VppIpInterfaceBind, find_route, FibPathProto, \
21     FibPathType
22 from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort, \
23     VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port, VppL2Vtr
24 from vpp_sub_interface import L2_VTR_OP, VppDot1QSubint
25 from vpp_ip import DpoProto, get_dpo_proto
26 from vpp_papi import VppEnum, MACAddress
27 from vpp_vxlan_gbp_tunnel import find_vxlan_gbp_tunnel, INDEX_INVALID, \
28     VppVxlanGbpTunnel
29 from vpp_neighbor import VppNeighbor
30 from vpp_acl import AclRule, VppAcl
31 try:
32     text_type = unicode
33 except NameError:
34     text_type = str
35
36 NUM_PKTS = 67
37
38
39 def find_gbp_endpoint(test, sw_if_index=None, ip=None, mac=None,
40                       tep=None, sclass=None):
41     if ip:
42         vip = ip
43     if mac:
44         vmac = MACAddress(mac)
45
46     eps = test.vapi.gbp_endpoint_dump()
47
48     for ep in eps:
49         if tep:
50             src = tep[0]
51             dst = tep[1]
52             if src != str(ep.endpoint.tun.src) or \
53                dst != str(ep.endpoint.tun.dst):
54                 continue
55         if sw_if_index:
56             if ep.endpoint.sw_if_index != sw_if_index:
57                 continue
58         if sclass:
59             if ep.endpoint.sclass != sclass:
60                 continue
61         if ip:
62             for eip in ep.endpoint.ips:
63                 if vip == str(eip):
64                     return True
65         if mac:
66             if vmac == ep.endpoint.mac:
67                 return True
68
69     return False
70
71
72 def find_gbp_vxlan(test, vni):
73     ts = test.vapi.gbp_vxlan_tunnel_dump()
74     for t in ts:
75         if t.tunnel.vni == vni:
76             return True
77     return False
78
79
80 class VppGbpEndpoint(VppObject):
81     """
82     GBP Endpoint
83     """
84
85     @property
86     def mac(self):
87         return str(self.vmac)
88
89     @property
90     def ip4(self):
91         return self._ip4
92
93     @property
94     def fip4(self):
95         return self._fip4
96
97     @property
98     def ip6(self):
99         return self._ip6
100
101     @property
102     def fip6(self):
103         return self._fip6
104
105     @property
106     def ips(self):
107         return [self.ip4, self.ip6]
108
109     @property
110     def fips(self):
111         return [self.fip4, self.fip6]
112
113     def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6,
114                  flags=0,
115                  tun_src="0.0.0.0",
116                  tun_dst="0.0.0.0",
117                  mac=True):
118         self._test = test
119         self.itf = itf
120         self.epg = epg
121         self.recirc = recirc
122
123         self._ip4 = ip4
124         self._fip4 = fip4
125         self._ip6 = ip6
126         self._fip6 = fip6
127
128         if mac:
129             self.vmac = MACAddress(self.itf.remote_mac)
130         else:
131             self.vmac = MACAddress("00:00:00:00:00:00")
132
133         self.flags = flags
134         self.tun_src = tun_src
135         self.tun_dst = tun_dst
136
137     def add_vpp_config(self):
138         res = self._test.vapi.gbp_endpoint_add(
139             self.itf.sw_if_index,
140             [self.ip4, self.ip6],
141             self.vmac.packed,
142             self.epg.sclass,
143             self.flags,
144             self.tun_src,
145             self.tun_dst)
146         self.handle = res.handle
147         self._test.registry.register(self, self._test.logger)
148
149     def remove_vpp_config(self):
150         self._test.vapi.gbp_endpoint_del(self.handle)
151
152     def object_id(self):
153         return "gbp-endpoint:[%d==%d:%s:%d]" % (self.handle,
154                                                 self.itf.sw_if_index,
155                                                 self.ip4,
156                                                 self.epg.sclass)
157
158     def query_vpp_config(self):
159         return find_gbp_endpoint(self._test,
160                                  self.itf.sw_if_index,
161                                  self.ip4)
162
163
164 class VppGbpRecirc(VppObject):
165     """
166     GBP Recirculation Interface
167     """
168
169     def __init__(self, test, epg, recirc, is_ext=False):
170         self._test = test
171         self.recirc = recirc
172         self.epg = epg
173         self.is_ext = is_ext
174
175     def add_vpp_config(self):
176         self._test.vapi.gbp_recirc_add_del(
177             1,
178             self.recirc.sw_if_index,
179             self.epg.sclass,
180             self.is_ext)
181         self._test.registry.register(self, self._test.logger)
182
183     def remove_vpp_config(self):
184         self._test.vapi.gbp_recirc_add_del(
185             0,
186             self.recirc.sw_if_index,
187             self.epg.sclass,
188             self.is_ext)
189
190     def object_id(self):
191         return "gbp-recirc:[%d]" % (self.recirc.sw_if_index)
192
193     def query_vpp_config(self):
194         rs = self._test.vapi.gbp_recirc_dump()
195         for r in rs:
196             if r.recirc.sw_if_index == self.recirc.sw_if_index:
197                 return True
198         return False
199
200
201 class VppGbpExtItf(VppObject):
202     """
203     GBP ExtItfulation Interface
204     """
205
206     def __init__(self, test, itf, bd, rd, anon=False):
207         self._test = test
208         self.itf = itf
209         self.bd = bd
210         self.rd = rd
211         self.flags = 1 if anon else 0
212
213     def add_vpp_config(self):
214         self._test.vapi.gbp_ext_itf_add_del(
215             1, self.itf.sw_if_index, self.bd.bd_id, self.rd.rd_id, self.flags)
216         self._test.registry.register(self, self._test.logger)
217
218     def remove_vpp_config(self):
219         self._test.vapi.gbp_ext_itf_add_del(
220             0, self.itf.sw_if_index, self.bd.bd_id, self.rd.rd_id, self.flags)
221
222     def object_id(self):
223         return "gbp-ext-itf:[%d]%s" % (self.itf.sw_if_index,
224                                        " [anon]" if self.flags else "")
225
226     def query_vpp_config(self):
227         rs = self._test.vapi.gbp_ext_itf_dump()
228         for r in rs:
229             if r.ext_itf.sw_if_index == self.itf.sw_if_index:
230                 return True
231         return False
232
233
234 class VppGbpSubnet(VppObject):
235     """
236     GBP Subnet
237     """
238
239     def __init__(self, test, rd, address, address_len,
240                  type, sw_if_index=None, sclass=None):
241         self._test = test
242         self.rd_id = rd.rd_id
243         a = ip_address(address)
244         if 4 == a.version:
245             self.prefix = IPv4Network("%s/%d" % (address, address_len),
246                                       strict=False)
247         else:
248             self.prefix = IPv6Network("%s/%d" % (address, address_len),
249                                       strict=False)
250         self.type = type
251         self.sw_if_index = sw_if_index
252         self.sclass = sclass
253
254     def add_vpp_config(self):
255         self._test.vapi.gbp_subnet_add_del(
256             1,
257             self.rd_id,
258             self.prefix,
259             self.type,
260             sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff,
261             sclass=self.sclass if self.sclass else 0xffff)
262         self._test.registry.register(self, self._test.logger)
263
264     def remove_vpp_config(self):
265         self._test.vapi.gbp_subnet_add_del(
266             0,
267             self.rd_id,
268             self.prefix,
269             self.type)
270
271     def object_id(self):
272         return "gbp-subnet:[%d-%s]" % (self.rd_id, self.prefix)
273
274     def query_vpp_config(self):
275         ss = self._test.vapi.gbp_subnet_dump()
276         for s in ss:
277             if s.subnet.rd_id == self.rd_id and \
278                     s.subnet.type == self.type and \
279                     s.subnet.prefix == self.prefix:
280                 return True
281         return False
282
283
284 class VppGbpEndpointRetention(object):
285     def __init__(self, remote_ep_timeout=0xffffffff):
286         self.remote_ep_timeout = remote_ep_timeout
287
288     def encode(self):
289         return {'remote_ep_timeout': self.remote_ep_timeout}
290
291
292 class VppGbpEndpointGroup(VppObject):
293     """
294     GBP Endpoint Group
295     """
296
297     def __init__(self, test, vnid, sclass, rd, bd, uplink,
298                  bvi, bvi_ip4, bvi_ip6=None,
299                  retention=VppGbpEndpointRetention()):
300         self._test = test
301         self.uplink = uplink
302         self.bvi = bvi
303         self.bvi_ip4 = bvi_ip4
304         self.bvi_ip6 = bvi_ip6
305         self.vnid = vnid
306         self.bd = bd
307         self.rd = rd
308         self.sclass = sclass
309         if 0 == self.sclass:
310             self.sclass = 0xffff
311         self.retention = retention
312
313     def add_vpp_config(self):
314         self._test.vapi.gbp_endpoint_group_add(
315             self.vnid,
316             self.sclass,
317             self.bd.bd.bd_id,
318             self.rd.rd_id,
319             self.uplink.sw_if_index if self.uplink else INDEX_INVALID,
320             self.retention.encode())
321         self._test.registry.register(self, self._test.logger)
322
323     def remove_vpp_config(self):
324         self._test.vapi.gbp_endpoint_group_del(self.sclass)
325
326     def object_id(self):
327         return "gbp-endpoint-group:[%d]" % (self.vnid)
328
329     def query_vpp_config(self):
330         epgs = self._test.vapi.gbp_endpoint_group_dump()
331         for epg in epgs:
332             if epg.epg.vnid == self.vnid:
333                 return True
334         return False
335
336
337 class VppGbpBridgeDomain(VppObject):
338     """
339     GBP Bridge Domain
340     """
341
342     def __init__(self, test, bd, rd, bvi, uu_fwd=None,
343                  bm_flood=None, learn=True,
344                  uu_drop=False, bm_drop=False,
345                  ucast_arp=False):
346         self._test = test
347         self.bvi = bvi
348         self.uu_fwd = uu_fwd
349         self.bm_flood = bm_flood
350         self.bd = bd
351         self.rd = rd
352
353         e = VppEnum.vl_api_gbp_bridge_domain_flags_t
354
355         self.flags = e.GBP_BD_API_FLAG_NONE
356         if not learn:
357             self.flags |= e.GBP_BD_API_FLAG_DO_NOT_LEARN
358         if uu_drop:
359             self.flags |= e.GBP_BD_API_FLAG_UU_FWD_DROP
360         if bm_drop:
361             self.flags |= e.GBP_BD_API_FLAG_MCAST_DROP
362         if ucast_arp:
363             self.flags |= e.GBP_BD_API_FLAG_UCAST_ARP
364
365     def add_vpp_config(self):
366         self._test.vapi.gbp_bridge_domain_add(
367             self.bd.bd_id,
368             self.rd.rd_id,
369             self.flags,
370             self.bvi.sw_if_index,
371             self.uu_fwd.sw_if_index if self.uu_fwd else INDEX_INVALID,
372             self.bm_flood.sw_if_index if self.bm_flood else INDEX_INVALID)
373         self._test.registry.register(self, self._test.logger)
374
375     def remove_vpp_config(self):
376         self._test.vapi.gbp_bridge_domain_del(self.bd.bd_id)
377
378     def object_id(self):
379         return "gbp-bridge-domain:[%d]" % (self.bd.bd_id)
380
381     def query_vpp_config(self):
382         bds = self._test.vapi.gbp_bridge_domain_dump()
383         for bd in bds:
384             if bd.bd.bd_id == self.bd.bd_id:
385                 return True
386         return False
387
388
389 class VppGbpRouteDomain(VppObject):
390     """
391     GBP Route Domain
392     """
393
394     def __init__(self, test, rd_id, scope, t4, t6, ip4_uu=None, ip6_uu=None):
395         self._test = test
396         self.rd_id = rd_id
397         self.scope = scope
398         self.t4 = t4
399         self.t6 = t6
400         self.ip4_uu = ip4_uu
401         self.ip6_uu = ip6_uu
402
403     def add_vpp_config(self):
404         self._test.vapi.gbp_route_domain_add(
405             self.rd_id,
406             self.scope,
407             self.t4.table_id,
408             self.t6.table_id,
409             self.ip4_uu.sw_if_index if self.ip4_uu else INDEX_INVALID,
410             self.ip6_uu.sw_if_index if self.ip6_uu else INDEX_INVALID)
411         self._test.registry.register(self, self._test.logger)
412
413     def remove_vpp_config(self):
414         self._test.vapi.gbp_route_domain_del(self.rd_id)
415
416     def object_id(self):
417         return "gbp-route-domain:[%d]" % (self.rd_id)
418
419     def query_vpp_config(self):
420         rds = self._test.vapi.gbp_route_domain_dump()
421         for rd in rds:
422             if rd.rd.rd_id == self.rd_id:
423                 return True
424         return False
425
426
427 class VppGbpContractNextHop():
428     def __init__(self, mac, bd, ip, rd):
429         self.mac = mac
430         self.ip = ip
431         self.bd = bd
432         self.rd = rd
433
434     def encode(self):
435         return {'ip': self.ip,
436                 'mac': self.mac.packed,
437                 'bd_id': self.bd.bd.bd_id,
438                 'rd_id': self.rd.rd_id}
439
440
441 class VppGbpContractRule():
442     def __init__(self, action, hash_mode, nhs=None):
443         self.action = action
444         self.hash_mode = hash_mode
445         self.nhs = [] if nhs is None else nhs
446
447     def encode(self):
448         nhs = []
449         for nh in self.nhs:
450             nhs.append(nh.encode())
451         while len(nhs) < 8:
452             nhs.append({})
453         return {'action': self.action,
454                 'nh_set': {
455                     'hash_mode': self.hash_mode,
456                     'n_nhs': len(self.nhs),
457                     'nhs': nhs}}
458
459     def __repr__(self):
460         return '<VppGbpContractRule action=%s, hash_mode=%s>' % (
461             self.action, self.hash_mode)
462
463
464 class VppGbpContract(VppObject):
465     """
466     GBP Contract
467     """
468
469     def __init__(self, test, scope, sclass, dclass, acl_index,
470                  rules, allowed_ethertypes):
471         self._test = test
472         if not isinstance(rules, list):
473             raise ValueError("'rules' must be a list.")
474         if not isinstance(allowed_ethertypes, list):
475             raise ValueError("'allowed_ethertypes' must be a list.")
476         self.scope = scope
477         self.acl_index = acl_index
478         self.sclass = sclass
479         self.dclass = dclass
480         self.rules = rules
481         self.allowed_ethertypes = allowed_ethertypes
482         while (len(self.allowed_ethertypes) < 16):
483             self.allowed_ethertypes.append(0)
484
485     def add_vpp_config(self):
486         rules = []
487         for r in self.rules:
488             rules.append(r.encode())
489         r = self._test.vapi.gbp_contract_add_del(
490             is_add=1,
491             contract={
492                 'acl_index': self.acl_index,
493                 'scope': self.scope,
494                 'sclass': self.sclass,
495                 'dclass': self.dclass,
496                 'n_rules': len(rules),
497                 'rules': rules,
498                 'n_ether_types': len(self.allowed_ethertypes),
499                 'allowed_ethertypes': self.allowed_ethertypes})
500         self.stats_index = r.stats_index
501         self._test.registry.register(self, self._test.logger)
502
503     def remove_vpp_config(self):
504         self._test.vapi.gbp_contract_add_del(
505             is_add=0,
506             contract={
507                 'acl_index': self.acl_index,
508                 'scope': self.scope,
509                 'sclass': self.sclass,
510                 'dclass': self.dclass,
511                 'n_rules': 0,
512                 'rules': [],
513                 'n_ether_types': len(self.allowed_ethertypes),
514                 'allowed_ethertypes': self.allowed_ethertypes})
515
516     def object_id(self):
517         return "gbp-contract:[%d:%d:%d:%d]" % (self.scope,
518                                                self.sclass,
519                                                self.dclass,
520                                                self.acl_index)
521
522     def query_vpp_config(self):
523         cs = self._test.vapi.gbp_contract_dump()
524         for c in cs:
525             if c.contract.scope == self.scope \
526                and c.contract.sclass == self.sclass \
527                and c.contract.dclass == self.dclass:
528                 return True
529         return False
530
531     def get_drop_stats(self):
532         c = self._test.statistics.get_counter("/net/gbp/contract/drop")
533         return c[0][self.stats_index]
534
535     def get_permit_stats(self):
536         c = self._test.statistics.get_counter("/net/gbp/contract/permit")
537         return c[0][self.stats_index]
538
539
540 class VppGbpVxlanTunnel(VppInterface):
541     """
542     GBP VXLAN tunnel
543     """
544
545     def __init__(self, test, vni, bd_rd_id, mode, src):
546         super(VppGbpVxlanTunnel, self).__init__(test)
547         self._test = test
548         self.vni = vni
549         self.bd_rd_id = bd_rd_id
550         self.mode = mode
551         self.src = src
552
553     def add_vpp_config(self):
554         r = self._test.vapi.gbp_vxlan_tunnel_add(
555             self.vni,
556             self.bd_rd_id,
557             self.mode,
558             self.src)
559         self.set_sw_if_index(r.sw_if_index)
560         self._test.registry.register(self, self._test.logger)
561
562     def remove_vpp_config(self):
563         self._test.vapi.gbp_vxlan_tunnel_del(self.vni)
564
565     def object_id(self):
566         return "gbp-vxlan:%d" % (self.sw_if_index)
567
568     def query_vpp_config(self):
569         return find_gbp_vxlan(self._test, self.vni)
570
571
572 class TestGBP(VppTestCase):
573     """ GBP Test Case """
574
575     @property
576     def config_flags(self):
577         return VppEnum.vl_api_nat_config_flags_t
578
579     @classmethod
580     def setUpClass(cls):
581         super(TestGBP, cls).setUpClass()
582
583     @classmethod
584     def tearDownClass(cls):
585         super(TestGBP, cls).tearDownClass()
586
587     def setUp(self):
588         super(TestGBP, self).setUp()
589
590         self.create_pg_interfaces(range(9))
591         self.create_loopback_interfaces(8)
592
593         self.router_mac = MACAddress("00:11:22:33:44:55")
594
595         for i in self.pg_interfaces:
596             i.admin_up()
597         for i in self.lo_interfaces:
598             i.admin_up()
599
600         self.vlan_100 = VppDot1QSubint(self, self.pg0, 100)
601         self.vlan_100.admin_up()
602         self.vlan_101 = VppDot1QSubint(self, self.pg0, 101)
603         self.vlan_101.admin_up()
604         self.vlan_102 = VppDot1QSubint(self, self.pg0, 102)
605         self.vlan_102.admin_up()
606
607     def tearDown(self):
608         for i in self.pg_interfaces:
609             i.admin_down()
610         super(TestGBP, self).tearDown()
611         for i in self.lo_interfaces:
612             i.remove_vpp_config()
613         self.lo_interfaces = []
614         self.vlan_102.remove_vpp_config()
615         self.vlan_101.remove_vpp_config()
616         self.vlan_100.remove_vpp_config()
617
618     def send_and_expect_bridged(self, src, tx, dst):
619         rx = self.send_and_expect(src, tx, dst)
620
621         for r in rx:
622             self.assertEqual(r[Ether].src, tx[0][Ether].src)
623             self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
624             self.assertEqual(r[IP].src, tx[0][IP].src)
625             self.assertEqual(r[IP].dst, tx[0][IP].dst)
626         return rx
627
628     def send_and_expect_bridged6(self, src, tx, dst):
629         rx = self.send_and_expect(src, tx, dst)
630
631         for r in rx:
632             self.assertEqual(r[Ether].src, tx[0][Ether].src)
633             self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
634             self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
635             self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
636         return rx
637
638     def send_and_expect_routed(self, src, tx, dst, src_mac):
639         rx = self.send_and_expect(src, tx, dst)
640
641         for r in rx:
642             self.assertEqual(r[Ether].src, src_mac)
643             self.assertEqual(r[Ether].dst, dst.remote_mac)
644             self.assertEqual(r[IP].src, tx[0][IP].src)
645             self.assertEqual(r[IP].dst, tx[0][IP].dst)
646         return rx
647
648     def send_and_expect_routed6(self, src, tx, dst, src_mac):
649         rx = self.send_and_expect(src, tx, dst)
650
651         for r in rx:
652             self.assertEqual(r[Ether].src, src_mac)
653             self.assertEqual(r[Ether].dst, dst.remote_mac)
654             self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
655             self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
656         return rx
657
658     def send_and_expect_natted(self, src, tx, dst, src_ip):
659         rx = self.send_and_expect(src, tx, dst)
660
661         for r in rx:
662             self.assertEqual(r[Ether].src, tx[0][Ether].src)
663             self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
664             self.assertEqual(r[IP].src, src_ip)
665             self.assertEqual(r[IP].dst, tx[0][IP].dst)
666         return rx
667
668     def send_and_expect_natted6(self, src, tx, dst, src_ip):
669         rx = self.send_and_expect(src, tx, dst)
670
671         for r in rx:
672             self.assertEqual(r[Ether].src, tx[0][Ether].src)
673             self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
674             self.assertEqual(r[IPv6].src, src_ip)
675             self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
676         return rx
677
678     def send_and_expect_unnatted(self, src, tx, dst, dst_ip):
679         rx = self.send_and_expect(src, tx, dst)
680
681         for r in rx:
682             self.assertEqual(r[Ether].src, tx[0][Ether].src)
683             self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
684             self.assertEqual(r[IP].dst, dst_ip)
685             self.assertEqual(r[IP].src, tx[0][IP].src)
686         return rx
687
688     def send_and_expect_unnatted6(self, src, tx, dst, dst_ip):
689         rx = self.send_and_expect(src, tx, dst)
690
691         for r in rx:
692             self.assertEqual(r[Ether].src, tx[0][Ether].src)
693             self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
694             self.assertEqual(r[IPv6].dst, dst_ip)
695             self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
696         return rx
697
698     def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip):
699         rx = self.send_and_expect(src, tx, dst)
700
701         for r in rx:
702             self.assertEqual(r[Ether].src, str(self.router_mac))
703             self.assertEqual(r[Ether].dst, dst.remote_mac)
704             self.assertEqual(r[IP].dst, dst_ip)
705             self.assertEqual(r[IP].src, src_ip)
706         return rx
707
708     def send_and_expect_double_natted6(self, src, tx, dst, src_ip, dst_ip):
709         rx = self.send_and_expect(src, tx, dst)
710
711         for r in rx:
712             self.assertEqual(r[Ether].src, str(self.router_mac))
713             self.assertEqual(r[Ether].dst, dst.remote_mac)
714             self.assertEqual(r[IPv6].dst, dst_ip)
715             self.assertEqual(r[IPv6].src, src_ip)
716         return rx
717
718     def send_and_expect_no_arp(self, src, tx, dst):
719         self.pg_send(src, tx)
720         dst.get_capture(0, timeout=1)
721         dst.assert_nothing_captured(remark="")
722         timeout = 0.1
723
724     def send_and_expect_arp(self, src, tx, dst):
725         rx = self.send_and_expect(src, tx, dst)
726
727         for r in rx:
728             self.assertEqual(r[Ether].src, tx[0][Ether].src)
729             self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
730             self.assertEqual(r[ARP].psrc, tx[0][ARP].psrc)
731             self.assertEqual(r[ARP].pdst, tx[0][ARP].pdst)
732             self.assertEqual(r[ARP].hwsrc, tx[0][ARP].hwsrc)
733             self.assertEqual(r[ARP].hwdst, tx[0][ARP].hwdst)
734         return rx
735
736     def test_gbp(self):
737         """ Group Based Policy """
738
739         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
740
741         #
742         # Route Domains
743         #
744         gt4 = VppIpTable(self, 0)
745         gt4.add_vpp_config()
746         gt6 = VppIpTable(self, 0, is_ip6=True)
747         gt6.add_vpp_config()
748         nt4 = VppIpTable(self, 20)
749         nt4.add_vpp_config()
750         nt6 = VppIpTable(self, 20, is_ip6=True)
751         nt6.add_vpp_config()
752
753         rd0 = VppGbpRouteDomain(self, 0, 400, gt4, gt6, None, None)
754         rd20 = VppGbpRouteDomain(self, 20, 420, nt4, nt6, None, None)
755
756         rd0.add_vpp_config()
757         rd20.add_vpp_config()
758
759         #
760         # Bridge Domains
761         #
762         bd1 = VppBridgeDomain(self, 1)
763         bd2 = VppBridgeDomain(self, 2)
764         bd20 = VppBridgeDomain(self, 20)
765
766         bd1.add_vpp_config()
767         bd2.add_vpp_config()
768         bd20.add_vpp_config()
769
770         gbd1 = VppGbpBridgeDomain(self, bd1, rd0, self.loop0)
771         gbd2 = VppGbpBridgeDomain(self, bd2, rd0, self.loop1)
772         gbd20 = VppGbpBridgeDomain(self, bd20, rd20, self.loop2)
773
774         gbd1.add_vpp_config()
775         gbd2.add_vpp_config()
776         gbd20.add_vpp_config()
777
778         #
779         # 3 EPGs, 2 of which share a BD.
780         # 2 NAT EPGs, one for floating-IP subnets, the other for internet
781         #
782         epgs = [VppGbpEndpointGroup(self, 220, 1220, rd0, gbd1,
783                                     self.pg4, self.loop0,
784                                     "10.0.0.128", "2001:10::128"),
785                 VppGbpEndpointGroup(self, 221, 1221, rd0, gbd1,
786                                     self.pg5, self.loop0,
787                                     "10.0.1.128", "2001:10:1::128"),
788                 VppGbpEndpointGroup(self, 222, 1222, rd0, gbd2,
789                                     self.pg6, self.loop1,
790                                     "10.0.2.128", "2001:10:2::128"),
791                 VppGbpEndpointGroup(self, 333, 1333, rd20, gbd20,
792                                     self.pg7, self.loop2,
793                                     "11.0.0.128", "3001::128"),
794                 VppGbpEndpointGroup(self, 444, 1444, rd20, gbd20,
795                                     self.pg8, self.loop2,
796                                     "11.0.0.129", "3001::129")]
797         recircs = [VppGbpRecirc(self, epgs[0], self.loop3),
798                    VppGbpRecirc(self, epgs[1], self.loop4),
799                    VppGbpRecirc(self, epgs[2], self.loop5),
800                    VppGbpRecirc(self, epgs[3], self.loop6, is_ext=True),
801                    VppGbpRecirc(self, epgs[4], self.loop7, is_ext=True)]
802
803         epg_nat = epgs[3]
804         recirc_nat = recircs[3]
805
806         #
807         # 4 end-points, 2 in the same subnet, 3 in the same BD
808         #
809         eps = [VppGbpEndpoint(self, self.pg0,
810                               epgs[0], recircs[0],
811                               "10.0.0.1", "11.0.0.1",
812                               "2001:10::1", "3001::1"),
813                VppGbpEndpoint(self, self.pg1,
814                               epgs[0], recircs[0],
815                               "10.0.0.2", "11.0.0.2",
816                               "2001:10::2", "3001::2"),
817                VppGbpEndpoint(self, self.pg2,
818                               epgs[1], recircs[1],
819                               "10.0.1.1", "11.0.0.3",
820                               "2001:10:1::1", "3001::3"),
821                VppGbpEndpoint(self, self.pg3,
822                               epgs[2], recircs[2],
823                               "10.0.2.1", "11.0.0.4",
824                               "2001:10:2::1", "3001::4")]
825
826         #
827         # Config related to each of the EPGs
828         #
829         for epg in epgs:
830             # IP config on the BVI interfaces
831             if epg != epgs[1] and epg != epgs[4]:
832                 VppIpInterfaceBind(self, epg.bvi, epg.rd.t4).add_vpp_config()
833                 VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config()
834                 epg.bvi.set_mac(self.router_mac)
835
836                 # The BVIs are NAT inside interfaces
837                 flags = self.config_flags.NAT_IS_INSIDE
838                 self.vapi.nat44_interface_add_del_feature(
839                     sw_if_index=epg.bvi.sw_if_index,
840                     flags=flags, is_add=1)
841                 self.vapi.nat66_add_del_interface(
842                     is_add=1, flags=flags,
843                     sw_if_index=epg.bvi.sw_if_index)
844
845             if_ip4 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip4, 32)
846             if_ip6 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip6, 128)
847             if_ip4.add_vpp_config()
848             if_ip6.add_vpp_config()
849
850             # EPG uplink interfaces in the RD
851             VppIpInterfaceBind(self, epg.uplink, epg.rd.t4).add_vpp_config()
852             VppIpInterfaceBind(self, epg.uplink, epg.rd.t6).add_vpp_config()
853
854             # add the BD ARP termination entry for BVI IP
855             epg.bd_arp_ip4 = VppBridgeDomainArpEntry(self, epg.bd.bd,
856                                                      str(self.router_mac),
857                                                      epg.bvi_ip4)
858             epg.bd_arp_ip6 = VppBridgeDomainArpEntry(self, epg.bd.bd,
859                                                      str(self.router_mac),
860                                                      epg.bvi_ip6)
861             epg.bd_arp_ip4.add_vpp_config()
862             epg.bd_arp_ip6.add_vpp_config()
863
864             # EPG in VPP
865             epg.add_vpp_config()
866
867         for recirc in recircs:
868             # EPG's ingress recirculation interface maps to its RD
869             VppIpInterfaceBind(self, recirc.recirc,
870                                recirc.epg.rd.t4).add_vpp_config()
871             VppIpInterfaceBind(self, recirc.recirc,
872                                recirc.epg.rd.t6).add_vpp_config()
873
874             self.vapi.nat44_interface_add_del_feature(
875                 sw_if_index=recirc.recirc.sw_if_index, is_add=1)
876             self.vapi.nat66_add_del_interface(
877                 is_add=1,
878                 sw_if_index=recirc.recirc.sw_if_index)
879
880             recirc.add_vpp_config()
881
882         for recirc in recircs:
883             self.assertTrue(find_bridge_domain_port(self,
884                                                     recirc.epg.bd.bd.bd_id,
885                                                     recirc.recirc.sw_if_index))
886
887         for ep in eps:
888             self.pg_enable_capture(self.pg_interfaces)
889             self.pg_start()
890             #
891             # routes to the endpoints. We need these since there are no
892             # adj-fibs due to the fact the the BVI address has /32 and
893             # the subnet is not attached.
894             #
895             for (ip, fip) in zip(ep.ips, ep.fips):
896                 # Add static mappings for each EP from the 10/8 to 11/8 network
897                 if ip_address(ip).version == 4:
898                     flags = self.config_flags.NAT_IS_ADDR_ONLY
899                     self.vapi.nat44_add_del_static_mapping(
900                         is_add=1,
901                         local_ip_address=ip,
902                         external_ip_address=fip,
903                         external_sw_if_index=0xFFFFFFFF,
904                         vrf_id=0,
905                         flags=flags)
906                 else:
907                     self.vapi.nat66_add_del_static_mapping(
908                         local_ip_address=ip,
909                         external_ip_address=fip,
910                         vrf_id=0, is_add=1)
911
912             # VPP EP create ...
913             ep.add_vpp_config()
914
915             self.logger.info(self.vapi.cli("sh gbp endpoint"))
916
917             # ... results in a Gratuitous ARP/ND on the EPG's uplink
918             rx = ep.epg.uplink.get_capture(len(ep.ips), timeout=0.2)
919
920             for ii, ip in enumerate(ep.ips):
921                 p = rx[ii]
922
923                 if ip_address(ip).version == 6:
924                     self.assertTrue(p.haslayer(ICMPv6ND_NA))
925                     self.assertEqual(p[ICMPv6ND_NA].tgt, ip)
926                 else:
927                     self.assertTrue(p.haslayer(ARP))
928                     self.assertEqual(p[ARP].psrc, ip)
929                     self.assertEqual(p[ARP].pdst, ip)
930
931             # add the BD ARP termination entry for floating IP
932             for fip in ep.fips:
933                 ba = VppBridgeDomainArpEntry(self, epg_nat.bd.bd, ep.mac,
934                                              fip)
935                 ba.add_vpp_config()
936
937                 # floating IPs route via EPG recirc
938                 r = VppIpRoute(
939                     self, fip, ip_address(fip).max_prefixlen,
940                     [VppRoutePath(fip,
941                                   ep.recirc.recirc.sw_if_index,
942                                   type=FibPathType.FIB_PATH_TYPE_DVR,
943                                   proto=get_dpo_proto(fip))],
944                     table_id=20)
945                 r.add_vpp_config()
946
947             # L2 FIB entries in the NAT EPG BD to bridge the packets from
948             # the outside direct to the internal EPG
949             lf = VppL2FibEntry(self, epg_nat.bd.bd, ep.mac,
950                                ep.recirc.recirc, bvi_mac=0)
951             lf.add_vpp_config()
952
953         #
954         # ARP packets for unknown IP are sent to the EPG uplink
955         #
956         pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
957                          src=self.pg0.remote_mac) /
958                    ARP(op="who-has",
959                        hwdst="ff:ff:ff:ff:ff:ff",
960                        hwsrc=self.pg0.remote_mac,
961                        pdst="10.0.0.88",
962                        psrc="10.0.0.99"))
963
964         self.vapi.cli("clear trace")
965         self.pg0.add_stream(pkt_arp)
966
967         self.pg_enable_capture(self.pg_interfaces)
968         self.pg_start()
969
970         rxd = epgs[0].uplink.get_capture(1)
971
972         #
973         # ARP/ND packets get a response
974         #
975         pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
976                          src=self.pg0.remote_mac) /
977                    ARP(op="who-has",
978                        hwdst="ff:ff:ff:ff:ff:ff",
979                        hwsrc=self.pg0.remote_mac,
980                        pdst=epgs[0].bvi_ip4,
981                        psrc=eps[0].ip4))
982
983         self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
984
985         nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6))
986         d = inet_ntop(AF_INET6, nsma)
987         pkt_nd = (Ether(dst=in6_getnsmac(nsma),
988                         src=self.pg0.remote_mac) /
989                   IPv6(dst=d, src=eps[0].ip6) /
990                   ICMPv6ND_NS(tgt=epgs[0].bvi_ip6) /
991                   ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
992         self.send_and_expect(self.pg0, [pkt_nd], self.pg0)
993
994         #
995         # broadcast packets are flooded
996         #
997         pkt_bcast = (Ether(dst="ff:ff:ff:ff:ff:ff",
998                            src=self.pg0.remote_mac) /
999                      IP(src=eps[0].ip4, dst="232.1.1.1") /
1000                      UDP(sport=1234, dport=1234) /
1001                      Raw(b'\xa5' * 100))
1002
1003         self.vapi.cli("clear trace")
1004         self.pg0.add_stream(pkt_bcast)
1005
1006         self.pg_enable_capture(self.pg_interfaces)
1007         self.pg_start()
1008
1009         rxd = eps[1].itf.get_capture(1)
1010         self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
1011         rxd = epgs[0].uplink.get_capture(1)
1012         self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
1013
1014         #
1015         # packets to non-local L3 destinations dropped
1016         #
1017         pkt_intra_epg_220_ip4 = (Ether(src=self.pg0.remote_mac,
1018                                        dst=str(self.router_mac)) /
1019                                  IP(src=eps[0].ip4,
1020                                     dst="10.0.0.99") /
1021                                  UDP(sport=1234, dport=1234) /
1022                                  Raw(b'\xa5' * 100))
1023         pkt_inter_epg_222_ip4 = (Ether(src=self.pg0.remote_mac,
1024                                        dst=str(self.router_mac)) /
1025                                  IP(src=eps[0].ip4,
1026                                     dst="10.0.1.99") /
1027                                  UDP(sport=1234, dport=1234) /
1028                                  Raw(b'\xa5' * 100))
1029
1030         self.send_and_assert_no_replies(self.pg0,
1031                                         pkt_intra_epg_220_ip4 * NUM_PKTS)
1032
1033         pkt_inter_epg_222_ip6 = (Ether(src=self.pg0.remote_mac,
1034                                        dst=str(self.router_mac)) /
1035                                  IPv6(src=eps[0].ip6,
1036                                       dst="2001:10::99") /
1037                                  UDP(sport=1234, dport=1234) /
1038                                  Raw(b'\xa5' * 100))
1039         self.send_and_assert_no_replies(self.pg0,
1040                                         pkt_inter_epg_222_ip6 * NUM_PKTS)
1041
1042         #
1043         # Add the subnet routes
1044         #
1045         s41 = VppGbpSubnet(
1046             self, rd0, "10.0.0.0", 24,
1047             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1048         s42 = VppGbpSubnet(
1049             self, rd0, "10.0.1.0", 24,
1050             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1051         s43 = VppGbpSubnet(
1052             self, rd0, "10.0.2.0", 24,
1053             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1054         s61 = VppGbpSubnet(
1055             self, rd0, "2001:10::1", 64,
1056             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1057         s62 = VppGbpSubnet(
1058             self, rd0, "2001:10:1::1", 64,
1059             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1060         s63 = VppGbpSubnet(
1061             self, rd0, "2001:10:2::1", 64,
1062             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1063         s41.add_vpp_config()
1064         s42.add_vpp_config()
1065         s43.add_vpp_config()
1066         s61.add_vpp_config()
1067         s62.add_vpp_config()
1068         s63.add_vpp_config()
1069
1070         self.send_and_expect_bridged(eps[0].itf,
1071                                      pkt_intra_epg_220_ip4 * NUM_PKTS,
1072                                      eps[0].epg.uplink)
1073         self.send_and_expect_bridged(eps[0].itf,
1074                                      pkt_inter_epg_222_ip4 * NUM_PKTS,
1075                                      eps[0].epg.uplink)
1076         self.send_and_expect_bridged6(eps[0].itf,
1077                                       pkt_inter_epg_222_ip6 * NUM_PKTS,
1078                                       eps[0].epg.uplink)
1079
1080         self.logger.info(self.vapi.cli("sh ip fib 11.0.0.2"))
1081         self.logger.info(self.vapi.cli("sh gbp endpoint-group"))
1082         self.logger.info(self.vapi.cli("sh gbp endpoint"))
1083         self.logger.info(self.vapi.cli("sh gbp recirc"))
1084         self.logger.info(self.vapi.cli("sh int"))
1085         self.logger.info(self.vapi.cli("sh int addr"))
1086         self.logger.info(self.vapi.cli("sh int feat loop6"))
1087         self.logger.info(self.vapi.cli("sh vlib graph ip4-gbp-src-classify"))
1088         self.logger.info(self.vapi.cli("sh int feat loop3"))
1089         self.logger.info(self.vapi.cli("sh int feat pg0"))
1090
1091         #
1092         # Packet destined to unknown unicast is sent on the epg uplink ...
1093         #
1094         pkt_intra_epg_220_to_uplink = (Ether(src=self.pg0.remote_mac,
1095                                              dst="00:00:00:33:44:55") /
1096                                        IP(src=eps[0].ip4,
1097                                           dst="10.0.0.99") /
1098                                        UDP(sport=1234, dport=1234) /
1099                                        Raw(b'\xa5' * 100))
1100
1101         self.send_and_expect_bridged(eps[0].itf,
1102                                      pkt_intra_epg_220_to_uplink * NUM_PKTS,
1103                                      eps[0].epg.uplink)
1104         # ... and nowhere else
1105         self.pg1.get_capture(0, timeout=0.1)
1106         self.pg1.assert_nothing_captured(remark="Flood onto other VMS")
1107
1108         pkt_intra_epg_221_to_uplink = (Ether(src=self.pg2.remote_mac,
1109                                              dst="00:00:00:33:44:66") /
1110                                        IP(src=eps[0].ip4,
1111                                           dst="10.0.0.99") /
1112                                        UDP(sport=1234, dport=1234) /
1113                                        Raw(b'\xa5' * 100))
1114
1115         self.send_and_expect_bridged(eps[2].itf,
1116                                      pkt_intra_epg_221_to_uplink * NUM_PKTS,
1117                                      eps[2].epg.uplink)
1118
1119         #
1120         # Packets from the uplink are forwarded in the absence of a contract
1121         #
1122         pkt_intra_epg_220_from_uplink = (Ether(src="00:00:00:33:44:55",
1123                                                dst=self.pg0.remote_mac) /
1124                                          IP(src=eps[0].ip4,
1125                                             dst="10.0.0.99") /
1126                                          UDP(sport=1234, dport=1234) /
1127                                          Raw(b'\xa5' * 100))
1128
1129         self.send_and_expect_bridged(self.pg4,
1130                                      pkt_intra_epg_220_from_uplink * NUM_PKTS,
1131                                      self.pg0)
1132
1133         #
1134         # in the absence of policy, endpoints in the same EPG
1135         # can communicate
1136         #
1137         pkt_intra_epg = (Ether(src=self.pg0.remote_mac,
1138                                dst=self.pg1.remote_mac) /
1139                          IP(src=eps[0].ip4,
1140                             dst=eps[1].ip4) /
1141                          UDP(sport=1234, dport=1234) /
1142                          Raw(b'\xa5' * 100))
1143
1144         self.send_and_expect_bridged(self.pg0,
1145                                      pkt_intra_epg * NUM_PKTS,
1146                                      self.pg1)
1147
1148         #
1149         # in the absence of policy, endpoints in the different EPG
1150         # cannot communicate
1151         #
1152         pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
1153                                           dst=self.pg2.remote_mac) /
1154                                     IP(src=eps[0].ip4,
1155                                        dst=eps[2].ip4) /
1156                                     UDP(sport=1234, dport=1234) /
1157                                     Raw(b'\xa5' * 100))
1158         pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
1159                                           dst=self.pg0.remote_mac) /
1160                                     IP(src=eps[2].ip4,
1161                                        dst=eps[0].ip4) /
1162                                     UDP(sport=1234, dport=1234) /
1163                                     Raw(b'\xa5' * 100))
1164         pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
1165                                           dst=str(self.router_mac)) /
1166                                     IP(src=eps[0].ip4,
1167                                        dst=eps[3].ip4) /
1168                                     UDP(sport=1234, dport=1234) /
1169                                     Raw(b'\xa5' * 100))
1170
1171         self.send_and_assert_no_replies(eps[0].itf,
1172                                         pkt_inter_epg_220_to_221 * NUM_PKTS)
1173         self.send_and_assert_no_replies(eps[0].itf,
1174                                         pkt_inter_epg_220_to_222 * NUM_PKTS)
1175
1176         #
1177         # A uni-directional contract from EPG 220 -> 221
1178         #
1179         rule = AclRule(is_permit=1, proto=17)
1180         rule2 = AclRule(src_prefix=IPv6Network((0, 0)),
1181                         dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17)
1182         acl = VppAcl(self, rules=[rule, rule2])
1183         acl.add_vpp_config()
1184
1185         c1 = VppGbpContract(
1186             self, 400, epgs[0].sclass, epgs[1].sclass, acl.acl_index,
1187             [VppGbpContractRule(
1188                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1189                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1190                 []),
1191                 VppGbpContractRule(
1192                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1193                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1194                     [])],
1195             [ETH_P_IP, ETH_P_IPV6])
1196         c1.add_vpp_config()
1197
1198         self.send_and_expect_bridged(eps[0].itf,
1199                                      pkt_inter_epg_220_to_221 * NUM_PKTS,
1200                                      eps[2].itf)
1201         self.send_and_assert_no_replies(eps[0].itf,
1202                                         pkt_inter_epg_220_to_222 * NUM_PKTS)
1203
1204         #
1205         # contract for the return direction
1206         #
1207         c2 = VppGbpContract(
1208             self, 400, epgs[1].sclass, epgs[0].sclass, acl.acl_index,
1209             [VppGbpContractRule(
1210                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1211                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1212                 []),
1213                 VppGbpContractRule(
1214                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1215                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1216                     [])],
1217             [ETH_P_IP, ETH_P_IPV6])
1218         c2.add_vpp_config()
1219
1220         self.send_and_expect_bridged(eps[0].itf,
1221                                      pkt_inter_epg_220_to_221 * NUM_PKTS,
1222                                      eps[2].itf)
1223         self.send_and_expect_bridged(eps[2].itf,
1224                                      pkt_inter_epg_221_to_220 * NUM_PKTS,
1225                                      eps[0].itf)
1226
1227         ds = c2.get_drop_stats()
1228         self.assertEqual(ds['packets'], 0)
1229         ps = c2.get_permit_stats()
1230         self.assertEqual(ps['packets'], NUM_PKTS)
1231
1232         #
1233         # the contract does not allow non-IP
1234         #
1235         pkt_non_ip_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
1236                                                  dst=self.pg2.remote_mac) /
1237                                            ARP())
1238         self.send_and_assert_no_replies(eps[0].itf,
1239                                         pkt_non_ip_inter_epg_220_to_221 * 17)
1240
1241         #
1242         # check that inter group is still disabled for the groups
1243         # not in the contract.
1244         #
1245         self.send_and_assert_no_replies(eps[0].itf,
1246                                         pkt_inter_epg_220_to_222 * NUM_PKTS)
1247
1248         #
1249         # A uni-directional contract from EPG 220 -> 222 'L3 routed'
1250         #
1251         c3 = VppGbpContract(
1252             self, 400, epgs[0].sclass, epgs[2].sclass, acl.acl_index,
1253             [VppGbpContractRule(
1254                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1255                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1256                 []),
1257                 VppGbpContractRule(
1258                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1259                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1260                     [])],
1261             [ETH_P_IP, ETH_P_IPV6])
1262         c3.add_vpp_config()
1263
1264         self.logger.info(self.vapi.cli("sh gbp contract"))
1265
1266         self.send_and_expect_routed(eps[0].itf,
1267                                     pkt_inter_epg_220_to_222 * NUM_PKTS,
1268                                     eps[3].itf,
1269                                     str(self.router_mac))
1270
1271         #
1272         # remove both contracts, traffic stops in both directions
1273         #
1274         c2.remove_vpp_config()
1275         c1.remove_vpp_config()
1276         c3.remove_vpp_config()
1277         acl.remove_vpp_config()
1278
1279         self.send_and_assert_no_replies(eps[2].itf,
1280                                         pkt_inter_epg_221_to_220 * NUM_PKTS)
1281         self.send_and_assert_no_replies(eps[0].itf,
1282                                         pkt_inter_epg_220_to_221 * NUM_PKTS)
1283         self.send_and_expect_bridged(eps[0].itf,
1284                                      pkt_intra_epg * NUM_PKTS,
1285                                      eps[1].itf)
1286
1287         #
1288         # EPs to the outside world
1289         #
1290
1291         # in the EP's RD an external subnet via the NAT EPG's recirc
1292         se1 = VppGbpSubnet(
1293             self, rd0, "0.0.0.0", 0,
1294             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1295             sw_if_index=recirc_nat.recirc.sw_if_index,
1296             sclass=epg_nat.sclass)
1297         se2 = VppGbpSubnet(
1298             self, rd0, "11.0.0.0", 8,
1299             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1300             sw_if_index=recirc_nat.recirc.sw_if_index,
1301             sclass=epg_nat.sclass)
1302         se16 = VppGbpSubnet(
1303             self, rd0, "::", 0,
1304             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1305             sw_if_index=recirc_nat.recirc.sw_if_index,
1306             sclass=epg_nat.sclass)
1307         # in the NAT RD an external subnet via the NAT EPG's uplink
1308         se3 = VppGbpSubnet(
1309             self, rd20, "0.0.0.0", 0,
1310             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1311             sw_if_index=epg_nat.uplink.sw_if_index,
1312             sclass=epg_nat.sclass)
1313         se36 = VppGbpSubnet(
1314             self, rd20, "::", 0,
1315             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1316             sw_if_index=epg_nat.uplink.sw_if_index,
1317             sclass=epg_nat.sclass)
1318         se4 = VppGbpSubnet(
1319             self, rd20, "11.0.0.0", 8,
1320             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1321             sw_if_index=epg_nat.uplink.sw_if_index,
1322             sclass=epg_nat.sclass)
1323         se1.add_vpp_config()
1324         se2.add_vpp_config()
1325         se16.add_vpp_config()
1326         se3.add_vpp_config()
1327         se36.add_vpp_config()
1328         se4.add_vpp_config()
1329
1330         self.logger.info(self.vapi.cli("sh ip fib 0.0.0.0/0"))
1331         self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1"))
1332         self.logger.info(self.vapi.cli("sh ip6 fib ::/0"))
1333         self.logger.info(self.vapi.cli("sh ip6 fib %s" %
1334                                        eps[0].fip6))
1335
1336         #
1337         # From an EP to an outside address: IN2OUT
1338         #
1339         pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
1340                                              dst=str(self.router_mac)) /
1341                                        IP(src=eps[0].ip4,
1342                                           dst="1.1.1.1") /
1343                                        UDP(sport=1234, dport=1234) /
1344                                        Raw(b'\xa5' * 100))
1345
1346         # no policy yet
1347         self.send_and_assert_no_replies(eps[0].itf,
1348                                         pkt_inter_epg_220_to_global * NUM_PKTS)
1349         rule = AclRule(is_permit=1, proto=17, ports=1234)
1350         rule2 = AclRule(is_permit=1, proto=17, ports=1234,
1351                         src_prefix=IPv6Network((0, 0)),
1352                         dst_prefix=IPv6Network((0, 0)))
1353         acl2 = VppAcl(self, rules=[rule, rule2])
1354         acl2.add_vpp_config()
1355
1356         c4 = VppGbpContract(
1357             self, 400, epgs[0].sclass, epgs[3].sclass, acl2.acl_index,
1358             [VppGbpContractRule(
1359                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1360                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1361                 []),
1362                 VppGbpContractRule(
1363                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1364                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1365                     [])],
1366             [ETH_P_IP, ETH_P_IPV6])
1367         c4.add_vpp_config()
1368
1369         self.send_and_expect_natted(eps[0].itf,
1370                                     pkt_inter_epg_220_to_global * NUM_PKTS,
1371                                     self.pg7,
1372                                     eps[0].fip4)
1373
1374         pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
1375                                              dst=str(self.router_mac)) /
1376                                        IPv6(src=eps[0].ip6,
1377                                             dst="6001::1") /
1378                                        UDP(sport=1234, dport=1234) /
1379                                        Raw(b'\xa5' * 100))
1380
1381         self.send_and_expect_natted6(self.pg0,
1382                                      pkt_inter_epg_220_to_global * NUM_PKTS,
1383                                      self.pg7,
1384                                      eps[0].fip6)
1385
1386         #
1387         # From a global address to an EP: OUT2IN
1388         #
1389         pkt_inter_epg_220_from_global = (Ether(src=str(self.router_mac),
1390                                                dst=self.pg0.remote_mac) /
1391                                          IP(dst=eps[0].fip4,
1392                                             src="1.1.1.1") /
1393                                          UDP(sport=1234, dport=1234) /
1394                                          Raw(b'\xa5' * 100))
1395
1396         self.send_and_assert_no_replies(
1397             self.pg7, pkt_inter_epg_220_from_global * NUM_PKTS)
1398
1399         c5 = VppGbpContract(
1400             self, 400, epgs[3].sclass, epgs[0].sclass, acl2.acl_index,
1401             [VppGbpContractRule(
1402                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1403                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1404                 []),
1405                 VppGbpContractRule(
1406                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1407                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1408                     [])],
1409             [ETH_P_IP, ETH_P_IPV6])
1410         c5.add_vpp_config()
1411
1412         self.send_and_expect_unnatted(self.pg7,
1413                                       pkt_inter_epg_220_from_global * NUM_PKTS,
1414                                       eps[0].itf,
1415                                       eps[0].ip4)
1416
1417         pkt_inter_epg_220_from_global = (Ether(src=str(self.router_mac),
1418                                                dst=self.pg0.remote_mac) /
1419                                          IPv6(dst=eps[0].fip6,
1420                                               src="6001::1") /
1421                                          UDP(sport=1234, dport=1234) /
1422                                          Raw(b'\xa5' * 100))
1423
1424         self.send_and_expect_unnatted6(
1425             self.pg7,
1426             pkt_inter_epg_220_from_global * NUM_PKTS,
1427             eps[0].itf,
1428             eps[0].ip6)
1429
1430         #
1431         # From a local VM to another local VM using resp. public addresses:
1432         #  IN2OUT2IN
1433         #
1434         pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
1435                                           dst=str(self.router_mac)) /
1436                                     IP(src=eps[0].ip4,
1437                                        dst=eps[1].fip4) /
1438                                     UDP(sport=1234, dport=1234) /
1439                                     Raw(b'\xa5' * 100))
1440
1441         self.send_and_expect_double_natted(eps[0].itf,
1442                                            pkt_intra_epg_220_global * NUM_PKTS,
1443                                            eps[1].itf,
1444                                            eps[0].fip4,
1445                                            eps[1].ip4)
1446
1447         pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
1448                                           dst=str(self.router_mac)) /
1449                                     IPv6(src=eps[0].ip6,
1450                                          dst=eps[1].fip6) /
1451                                     UDP(sport=1234, dport=1234) /
1452                                     Raw(b'\xa5' * 100))
1453
1454         self.send_and_expect_double_natted6(
1455             eps[0].itf,
1456             pkt_intra_epg_220_global * NUM_PKTS,
1457             eps[1].itf,
1458             eps[0].fip6,
1459             eps[1].ip6)
1460
1461         #
1462         # cleanup
1463         #
1464         for ep in eps:
1465             # del static mappings for each EP from the 10/8 to 11/8 network
1466             flags = self.config_flags.NAT_IS_ADDR_ONLY
1467             self.vapi.nat44_add_del_static_mapping(
1468                 is_add=0,
1469                 local_ip_address=ep.ip4,
1470                 external_ip_address=ep.fip4,
1471                 external_sw_if_index=0xFFFFFFFF,
1472                 vrf_id=0,
1473                 flags=flags)
1474             self.vapi.nat66_add_del_static_mapping(
1475                 local_ip_address=ep.ip6,
1476                 external_ip_address=ep.fip6,
1477                 vrf_id=0, is_add=0)
1478
1479         for epg in epgs:
1480             # IP config on the BVI interfaces
1481             if epg != epgs[0] and epg != epgs[3]:
1482                 flags = self.config_flags.NAT_IS_INSIDE
1483                 self.vapi.nat44_interface_add_del_feature(
1484                     sw_if_index=epg.bvi.sw_if_index,
1485                     flags=flags,
1486                     is_add=0)
1487                 self.vapi.nat66_add_del_interface(
1488                     is_add=0, flags=flags,
1489                     sw_if_index=epg.bvi.sw_if_index)
1490
1491         for recirc in recircs:
1492             self.vapi.nat44_interface_add_del_feature(
1493                 sw_if_index=recirc.recirc.sw_if_index,
1494                 is_add=0)
1495             self.vapi.nat66_add_del_interface(
1496                 is_add=0,
1497                 sw_if_index=recirc.recirc.sw_if_index)
1498
1499     def wait_for_ep_timeout(self, sw_if_index=None, ip=None, mac=None,
1500                             tep=None, n_tries=100, s_time=1):
1501         while (n_tries):
1502             if not find_gbp_endpoint(self, sw_if_index, ip, mac, tep=tep):
1503                 return True
1504             n_tries = n_tries - 1
1505             self.sleep(s_time)
1506         self.assertFalse(find_gbp_endpoint(self, sw_if_index, ip, mac))
1507         return False
1508
1509     def test_gbp_learn_l2(self):
1510         """ GBP L2 Endpoint Learning """
1511
1512         drop_no_contract = self.statistics.get_err_counter(
1513             '/err/gbp-policy-port/drop-no-contract')
1514         allow_intra_class = self.statistics.get_err_counter(
1515             '/err/gbp-policy-port/allow-intra-sclass')
1516
1517         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
1518         learnt = [{'mac': '00:00:11:11:11:01',
1519                    'ip': '10.0.0.1',
1520                    'ip6': '2001:10::2'},
1521                   {'mac': '00:00:11:11:11:02',
1522                    'ip': '10.0.0.2',
1523                    'ip6': '2001:10::3'}]
1524
1525         #
1526         # IP tables
1527         #
1528         gt4 = VppIpTable(self, 1)
1529         gt4.add_vpp_config()
1530         gt6 = VppIpTable(self, 1, is_ip6=True)
1531         gt6.add_vpp_config()
1532
1533         rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
1534         rd1.add_vpp_config()
1535
1536         #
1537         # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs
1538         # Pg3 hosts the IP4 UU-flood VXLAN tunnel
1539         # Pg4 hosts the IP6 UU-flood VXLAN tunnel
1540         #
1541         self.pg2.config_ip4()
1542         self.pg2.resolve_arp()
1543         self.pg2.generate_remote_hosts(4)
1544         self.pg2.configure_ipv4_neighbors()
1545         self.pg3.config_ip4()
1546         self.pg3.resolve_arp()
1547         self.pg4.config_ip4()
1548         self.pg4.resolve_arp()
1549
1550         #
1551         # Add a mcast destination VXLAN-GBP tunnel for B&M traffic
1552         #
1553         tun_bm = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
1554                                    "239.1.1.1", 88,
1555                                    mcast_itf=self.pg4)
1556         tun_bm.add_vpp_config()
1557
1558         #
1559         # a GBP bridge domain with a BVI and a UU-flood interface
1560         #
1561         bd1 = VppBridgeDomain(self, 1)
1562         bd1.add_vpp_config()
1563         gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0,
1564                                   self.pg3, tun_bm)
1565         gbd1.add_vpp_config()
1566
1567         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
1568         self.logger.info(self.vapi.cli("sh gbp bridge"))
1569
1570         # ... and has a /32 applied
1571         ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
1572         ip_addr.add_vpp_config()
1573
1574         #
1575         # The Endpoint-group in which we are learning endpoints
1576         #
1577         epg_220 = VppGbpEndpointGroup(self, 220, 112, rd1, gbd1,
1578                                       None, self.loop0,
1579                                       "10.0.0.128",
1580                                       "2001:10::128",
1581                                       VppGbpEndpointRetention(2))
1582         epg_220.add_vpp_config()
1583         epg_330 = VppGbpEndpointGroup(self, 330, 113, rd1, gbd1,
1584                                       None, self.loop1,
1585                                       "10.0.1.128",
1586                                       "2001:11::128",
1587                                       VppGbpEndpointRetention(2))
1588         epg_330.add_vpp_config()
1589
1590         #
1591         # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
1592         # learning enabled
1593         #
1594         vx_tun_l2_1 = VppGbpVxlanTunnel(
1595             self, 99, bd1.bd_id,
1596             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2,
1597             self.pg2.local_ip4)
1598         vx_tun_l2_1.add_vpp_config()
1599
1600         #
1601         # A static endpoint that the learnt endpoints are trying to
1602         # talk to
1603         #
1604         ep = VppGbpEndpoint(self, self.pg0,
1605                             epg_220, None,
1606                             "10.0.0.127", "11.0.0.127",
1607                             "2001:10::1", "3001::1")
1608         ep.add_vpp_config()
1609
1610         self.assertTrue(find_route(self, ep.ip4, 32, table_id=1))
1611
1612         # a packet with an sclass from an unknown EPG
1613         p = (Ether(src=self.pg2.remote_mac,
1614                    dst=self.pg2.local_mac) /
1615              IP(src=self.pg2.remote_hosts[0].ip4,
1616                 dst=self.pg2.local_ip4) /
1617              UDP(sport=1234, dport=48879) /
1618              VXLAN(vni=99, gpid=88, flags=0x88) /
1619              Ether(src=learnt[0]["mac"], dst=ep.mac) /
1620              IP(src=learnt[0]["ip"], dst=ep.ip4) /
1621              UDP(sport=1234, dport=1234) /
1622              Raw(b'\xa5' * 100))
1623
1624         self.send_and_assert_no_replies(self.pg2, p)
1625
1626         self.logger.info(self.vapi.cli("sh error"))
1627         self.assert_error_counter_equal(
1628             '/err/gbp-policy-port/drop-no-contract',
1629             drop_no_contract + 1)
1630
1631         #
1632         # we should not have learnt a new tunnel endpoint, since
1633         # the EPG was not learnt.
1634         #
1635         self.assertEqual(INDEX_INVALID,
1636                          find_vxlan_gbp_tunnel(self,
1637                                                self.pg2.local_ip4,
1638                                                self.pg2.remote_hosts[0].ip4,
1639                                                99))
1640
1641         # ep is not learnt, because the EPG is unknown
1642         self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1)
1643
1644         #
1645         # Learn new EPs from IP packets
1646         #
1647         for ii, l in enumerate(learnt):
1648             # a packet with an sclass from a known EPG
1649             # arriving on an unknown TEP
1650             p = (Ether(src=self.pg2.remote_mac,
1651                        dst=self.pg2.local_mac) /
1652                  IP(src=self.pg2.remote_hosts[1].ip4,
1653                     dst=self.pg2.local_ip4) /
1654                  UDP(sport=1234, dport=48879) /
1655                  VXLAN(vni=99, gpid=112, flags=0x88) /
1656                  Ether(src=l['mac'], dst=ep.mac) /
1657                  IP(src=l['ip'], dst=ep.ip4) /
1658                  UDP(sport=1234, dport=1234) /
1659                  Raw(b'\xa5' * 100))
1660
1661             rx = self.send_and_expect(self.pg2, [p], self.pg0)
1662
1663             # the new TEP
1664             tep1_sw_if_index = find_vxlan_gbp_tunnel(
1665                 self,
1666                 self.pg2.local_ip4,
1667                 self.pg2.remote_hosts[1].ip4,
1668                 99)
1669             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
1670
1671             #
1672             # the EP is learnt via the learnt TEP
1673             # both from its MAC and its IP
1674             #
1675             self.assertTrue(find_gbp_endpoint(self,
1676                                               vx_tun_l2_1.sw_if_index,
1677                                               mac=l['mac']))
1678             self.assertTrue(find_gbp_endpoint(self,
1679                                               vx_tun_l2_1.sw_if_index,
1680                                               ip=l['ip']))
1681
1682         self.assert_error_counter_equal(
1683             '/err/gbp-policy-port/allow-intra-sclass',
1684             allow_intra_class + 2)
1685
1686         self.logger.info(self.vapi.cli("show gbp endpoint"))
1687         self.logger.info(self.vapi.cli("show gbp vxlan"))
1688         self.logger.info(self.vapi.cli("show ip mfib"))
1689
1690         #
1691         # If we sleep for the threshold time, the learnt endpoints should
1692         # age out
1693         #
1694         for l in learnt:
1695             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
1696                                      mac=l['mac'])
1697
1698         #
1699         # Learn new EPs from GARP packets received on the BD's mcast tunnel
1700         #
1701         for ii, l in enumerate(learnt):
1702             # add some junk in the reserved field of the vxlan-header
1703             # next to the VNI. we should accept since reserved bits are
1704             # ignored on rx.
1705             p = (Ether(src=self.pg2.remote_mac,
1706                        dst=self.pg2.local_mac) /
1707                  IP(src=self.pg2.remote_hosts[1].ip4,
1708                     dst="239.1.1.1") /
1709                  UDP(sport=1234, dport=48879) /
1710                  VXLAN(vni=88, reserved2=0x80, gpid=112, flags=0x88) /
1711                  Ether(src=l['mac'], dst="ff:ff:ff:ff:ff:ff") /
1712                  ARP(op="who-has",
1713                      psrc=l['ip'], pdst=l['ip'],
1714                      hwsrc=l['mac'], hwdst="ff:ff:ff:ff:ff:ff"))
1715
1716             rx = self.send_and_expect(self.pg4, [p], self.pg0)
1717
1718             # the new TEP
1719             tep1_sw_if_index = find_vxlan_gbp_tunnel(
1720                 self,
1721                 self.pg2.local_ip4,
1722                 self.pg2.remote_hosts[1].ip4,
1723                 99)
1724             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
1725
1726             #
1727             # the EP is learnt via the learnt TEP
1728             # both from its MAC and its IP
1729             #
1730             self.assertTrue(find_gbp_endpoint(self,
1731                                               vx_tun_l2_1.sw_if_index,
1732                                               mac=l['mac']))
1733             self.assertTrue(find_gbp_endpoint(self,
1734                                               vx_tun_l2_1.sw_if_index,
1735                                               ip=l['ip']))
1736
1737         #
1738         # wait for the learnt endpoints to age out
1739         #
1740         for l in learnt:
1741             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
1742                                      mac=l['mac'])
1743
1744         #
1745         # Learn new EPs from L2 packets
1746         #
1747         for ii, l in enumerate(learnt):
1748             # a packet with an sclass from a known EPG
1749             # arriving on an unknown TEP
1750             p = (Ether(src=self.pg2.remote_mac,
1751                        dst=self.pg2.local_mac) /
1752                  IP(src=self.pg2.remote_hosts[1].ip4,
1753                     dst=self.pg2.local_ip4) /
1754                  UDP(sport=1234, dport=48879) /
1755                  VXLAN(vni=99, gpid=112, flags=0x88) /
1756                  Ether(src=l['mac'], dst=ep.mac) /
1757                  Raw(b'\xa5' * 100))
1758
1759             rx = self.send_and_expect(self.pg2, [p], self.pg0)
1760
1761             # the new TEP
1762             tep1_sw_if_index = find_vxlan_gbp_tunnel(
1763                 self,
1764                 self.pg2.local_ip4,
1765                 self.pg2.remote_hosts[1].ip4,
1766                 99)
1767             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
1768
1769             #
1770             # the EP is learnt via the learnt TEP
1771             # both from its MAC and its IP
1772             #
1773             self.assertTrue(find_gbp_endpoint(self,
1774                                               vx_tun_l2_1.sw_if_index,
1775                                               mac=l['mac']))
1776
1777         self.logger.info(self.vapi.cli("show gbp endpoint"))
1778         self.logger.info(self.vapi.cli("show gbp vxlan"))
1779         self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
1780
1781         #
1782         # wait for the learnt endpoints to age out
1783         #
1784         for l in learnt:
1785             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
1786                                      mac=l['mac'])
1787
1788         #
1789         # repeat. the do not learn bit is set so the EPs are not learnt
1790         #
1791         for l in learnt:
1792             # a packet with an sclass from a known EPG
1793             p = (Ether(src=self.pg2.remote_mac,
1794                        dst=self.pg2.local_mac) /
1795                  IP(src=self.pg2.remote_hosts[1].ip4,
1796                     dst=self.pg2.local_ip4) /
1797                  UDP(sport=1234, dport=48879) /
1798                  VXLAN(vni=99, gpid=112, flags=0x88, gpflags="D") /
1799                  Ether(src=l['mac'], dst=ep.mac) /
1800                  IP(src=l['ip'], dst=ep.ip4) /
1801                  UDP(sport=1234, dport=1234) /
1802                  Raw(b'\xa5' * 100))
1803
1804             rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
1805
1806         for l in learnt:
1807             self.assertFalse(find_gbp_endpoint(self,
1808                                                vx_tun_l2_1.sw_if_index,
1809                                                mac=l['mac']))
1810
1811         #
1812         # repeat
1813         #
1814         for l in learnt:
1815             # a packet with an sclass from a known EPG
1816             # set a reserved bit in addition to the G and I
1817             # reserved bits should not be checked on rx.
1818             p = (Ether(src=self.pg2.remote_mac,
1819                        dst=self.pg2.local_mac) /
1820                  IP(src=self.pg2.remote_hosts[1].ip4,
1821                     dst=self.pg2.local_ip4) /
1822                  UDP(sport=1234, dport=48879) /
1823                  VXLAN(vni=99, gpid=112, flags=0xc8) /
1824                  Ether(src=l['mac'], dst=ep.mac) /
1825                  IP(src=l['ip'], dst=ep.ip4) /
1826                  UDP(sport=1234, dport=1234) /
1827                  Raw(b'\xa5' * 100))
1828
1829             rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
1830
1831             self.assertTrue(find_gbp_endpoint(self,
1832                                               vx_tun_l2_1.sw_if_index,
1833                                               mac=l['mac']))
1834
1835         #
1836         # Static EP replies to dynamics
1837         #
1838         self.logger.info(self.vapi.cli("sh l2fib bd_id 1"))
1839         for l in learnt:
1840             p = (Ether(src=ep.mac, dst=l['mac']) /
1841                  IP(dst=l['ip'], src=ep.ip4) /
1842                  UDP(sport=1234, dport=1234) /
1843                  Raw(b'\xa5' * 100))
1844
1845             rxs = self.send_and_expect(self.pg0, p * 17, self.pg2)
1846
1847             for rx in rxs:
1848                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
1849                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
1850                 self.assertEqual(rx[UDP].dport, 48879)
1851                 # the UDP source port is a random value for hashing
1852                 self.assertEqual(rx[VXLAN].gpid, 112)
1853                 self.assertEqual(rx[VXLAN].vni, 99)
1854                 self.assertTrue(rx[VXLAN].flags.G)
1855                 self.assertTrue(rx[VXLAN].flags.Instance)
1856                 self.assertTrue(rx[VXLAN].gpflags.A)
1857                 self.assertFalse(rx[VXLAN].gpflags.D)
1858
1859         for l in learnt:
1860             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
1861                                      mac=l['mac'])
1862
1863         #
1864         # repeat in the other EPG
1865         # there's no contract between 220 and 330, but the A-bit is set
1866         # so the packet is cleared for delivery
1867         #
1868         for l in learnt:
1869             # a packet with an sclass from a known EPG
1870             p = (Ether(src=self.pg2.remote_mac,
1871                        dst=self.pg2.local_mac) /
1872                  IP(src=self.pg2.remote_hosts[1].ip4,
1873                     dst=self.pg2.local_ip4) /
1874                  UDP(sport=1234, dport=48879) /
1875                  VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') /
1876                  Ether(src=l['mac'], dst=ep.mac) /
1877                  IP(src=l['ip'], dst=ep.ip4) /
1878                  UDP(sport=1234, dport=1234) /
1879                  Raw(b'\xa5' * 100))
1880
1881             rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
1882
1883             self.assertTrue(find_gbp_endpoint(self,
1884                                               vx_tun_l2_1.sw_if_index,
1885                                               mac=l['mac']))
1886
1887         #
1888         # static EP cannot reach the learnt EPs since there is no contract
1889         # only test 1 EP as the others could timeout
1890         #
1891         p = (Ether(src=ep.mac, dst=l['mac']) /
1892              IP(dst=learnt[0]['ip'], src=ep.ip4) /
1893              UDP(sport=1234, dport=1234) /
1894              Raw(b'\xa5' * 100))
1895
1896         self.send_and_assert_no_replies(self.pg0, [p])
1897
1898         #
1899         # refresh the entries after the check for no replies above
1900         #
1901         for l in learnt:
1902             # a packet with an sclass from a known EPG
1903             p = (Ether(src=self.pg2.remote_mac,
1904                        dst=self.pg2.local_mac) /
1905                  IP(src=self.pg2.remote_hosts[1].ip4,
1906                     dst=self.pg2.local_ip4) /
1907                  UDP(sport=1234, dport=48879) /
1908                  VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') /
1909                  Ether(src=l['mac'], dst=ep.mac) /
1910                  IP(src=l['ip'], dst=ep.ip4) /
1911                  UDP(sport=1234, dport=1234) /
1912                  Raw(b'\xa5' * 100))
1913
1914             rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
1915
1916             self.assertTrue(find_gbp_endpoint(self,
1917                                               vx_tun_l2_1.sw_if_index,
1918                                               mac=l['mac']))
1919
1920         #
1921         # Add the contract so they can talk
1922         #
1923         rule = AclRule(is_permit=1, proto=17)
1924         rule2 = AclRule(src_prefix=IPv6Network((0, 0)),
1925                         dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17)
1926         acl = VppAcl(self, rules=[rule, rule2])
1927         acl.add_vpp_config()
1928
1929         c1 = VppGbpContract(
1930             self, 401, epg_220.sclass, epg_330.sclass, acl.acl_index,
1931             [VppGbpContractRule(
1932                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1933                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1934                 []),
1935              VppGbpContractRule(
1936                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1937                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1938                  [])],
1939             [ETH_P_IP, ETH_P_IPV6])
1940         c1.add_vpp_config()
1941
1942         for l in learnt:
1943             p = (Ether(src=ep.mac, dst=l['mac']) /
1944                  IP(dst=l['ip'], src=ep.ip4) /
1945                  UDP(sport=1234, dport=1234) /
1946                  Raw(b'\xa5' * 100))
1947
1948             self.send_and_expect(self.pg0, [p], self.pg2)
1949
1950         #
1951         # send UU packets from the local EP
1952         #
1953         self.logger.info(self.vapi.cli("sh gbp bridge"))
1954         self.logger.info(self.vapi.cli("sh bridge-domain 1 detail"))
1955         p_uu = (Ether(src=ep.mac, dst="00:11:11:11:11:11") /
1956                 IP(dst="10.0.0.133", src=ep.ip4) /
1957                 UDP(sport=1234, dport=1234) /
1958                 Raw(b'\xa5' * 100))
1959         rxs = self.send_and_expect(ep.itf, [p_uu], gbd1.uu_fwd)
1960
1961         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
1962
1963         p_bm = (Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") /
1964                 IP(dst="10.0.0.133", src=ep.ip4) /
1965                 UDP(sport=1234, dport=1234) /
1966                 Raw(b'\xa5' * 100))
1967         rxs = self.send_and_expect_only(ep.itf, [p_bm], tun_bm.mcast_itf)
1968
1969         for rx in rxs:
1970             self.assertEqual(rx[IP].src, self.pg4.local_ip4)
1971             self.assertEqual(rx[IP].dst, "239.1.1.1")
1972             self.assertEqual(rx[UDP].dport, 48879)
1973             # the UDP source port is a random value for hashing
1974             self.assertEqual(rx[VXLAN].gpid, 112)
1975             self.assertEqual(rx[VXLAN].vni, 88)
1976             self.assertTrue(rx[VXLAN].flags.G)
1977             self.assertTrue(rx[VXLAN].flags.Instance)
1978             self.assertFalse(rx[VXLAN].gpflags.A)
1979             self.assertFalse(rx[VXLAN].gpflags.D)
1980
1981         rule = AclRule(is_permit=1, proto=17)
1982         rule2 = AclRule(src_prefix=IPv6Network((0, 0)),
1983                         dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17)
1984         acl = VppAcl(self, rules=[rule, rule2])
1985         acl.add_vpp_config()
1986
1987         c2 = VppGbpContract(
1988             self, 401, epg_330.sclass, epg_220.sclass, acl.acl_index,
1989             [VppGbpContractRule(
1990                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1991                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1992                 []),
1993                 VppGbpContractRule(
1994                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1995                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
1996                     [])],
1997             [ETH_P_IP, ETH_P_IPV6])
1998         c2.add_vpp_config()
1999
2000         for l in learnt:
2001             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
2002                                      mac=l['mac'])
2003         #
2004         # Check v6 Endpoints learning
2005         #
2006         for l in learnt:
2007             # a packet with an sclass from a known EPG
2008             p = (Ether(src=self.pg2.remote_mac,
2009                        dst=self.pg2.local_mac) /
2010                  IP(src=self.pg2.remote_hosts[1].ip4,
2011                     dst=self.pg2.local_ip4) /
2012                  UDP(sport=1234, dport=48879) /
2013                  VXLAN(vni=99, gpid=113, flags=0x88) /
2014                  Ether(src=l['mac'], dst=ep.mac) /
2015                  IPv6(src=l['ip6'], dst=ep.ip6) /
2016                  UDP(sport=1234, dport=1234) /
2017                  Raw(b'\xa5' * 100))
2018
2019             rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
2020             rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
2021
2022             self.assertTrue(find_gbp_endpoint(
2023                 self,
2024                 vx_tun_l2_1.sw_if_index,
2025                 ip=l['ip6'],
2026                 tep=[self.pg2.local_ip4,
2027                      self.pg2.remote_hosts[1].ip4]))
2028
2029         self.logger.info(self.vapi.cli("sh int"))
2030         self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
2031         self.logger.info(self.vapi.cli("sh gbp vxlan"))
2032         self.logger.info(self.vapi.cli("sh gbp endpoint"))
2033         self.logger.info(self.vapi.cli("sh gbp interface"))
2034
2035         #
2036         # EP moves to a different TEP
2037         #
2038         for l in learnt:
2039             # a packet with an sclass from a known EPG
2040             p = (Ether(src=self.pg2.remote_mac,
2041                        dst=self.pg2.local_mac) /
2042                  IP(src=self.pg2.remote_hosts[2].ip4,
2043                     dst=self.pg2.local_ip4) /
2044                  UDP(sport=1234, dport=48879) /
2045                  VXLAN(vni=99, gpid=113, flags=0x88) /
2046                  Ether(src=l['mac'], dst=ep.mac) /
2047                  IPv6(src=l['ip6'], dst=ep.ip6) /
2048                  UDP(sport=1234, dport=1234) /
2049                  Raw(b'\xa5' * 100))
2050
2051             rx = self.send_and_expect(self.pg2, p * 1, self.pg0)
2052             rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
2053
2054             self.assertTrue(find_gbp_endpoint(
2055                 self,
2056                 vx_tun_l2_1.sw_if_index,
2057                 sclass=113,
2058                 mac=l['mac'],
2059                 tep=[self.pg2.local_ip4,
2060                      self.pg2.remote_hosts[2].ip4]))
2061
2062         #
2063         # v6 remote EP reachability
2064         #
2065         for l in learnt:
2066             p = (Ether(src=ep.mac, dst=l['mac']) /
2067                  IPv6(dst=l['ip6'], src=ep.ip6) /
2068                  UDP(sport=1234, dport=1234) /
2069                  Raw(b'\xa5' * 100))
2070
2071             rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
2072
2073             for rx in rxs:
2074                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
2075                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
2076                 self.assertEqual(rx[UDP].dport, 48879)
2077                 # the UDP source port is a random value for hashing
2078                 self.assertEqual(rx[VXLAN].gpid, 112)
2079                 self.assertEqual(rx[VXLAN].vni, 99)
2080                 self.assertTrue(rx[VXLAN].flags.G)
2081                 self.assertTrue(rx[VXLAN].flags.Instance)
2082                 self.assertTrue(rx[VXLAN].gpflags.A)
2083                 self.assertFalse(rx[VXLAN].gpflags.D)
2084                 self.assertEqual(rx[IPv6].dst, l['ip6'])
2085
2086         #
2087         # EP changes sclass
2088         #
2089         for l in learnt:
2090             # a packet with an sclass from a known EPG
2091             p = (Ether(src=self.pg2.remote_mac,
2092                        dst=self.pg2.local_mac) /
2093                  IP(src=self.pg2.remote_hosts[2].ip4,
2094                     dst=self.pg2.local_ip4) /
2095                  UDP(sport=1234, dport=48879) /
2096                  VXLAN(vni=99, gpid=112, flags=0x88) /
2097                  Ether(src=l['mac'], dst=ep.mac) /
2098                  IPv6(src=l['ip6'], dst=ep.ip6) /
2099                  UDP(sport=1234, dport=1234) /
2100                  Raw(b'\xa5' * 100))
2101
2102             rx = self.send_and_expect(self.pg2, p * 1, self.pg0)
2103             rx = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
2104
2105             self.assertTrue(find_gbp_endpoint(
2106                 self,
2107                 vx_tun_l2_1.sw_if_index,
2108                 mac=l['mac'],
2109                 sclass=112,
2110                 tep=[self.pg2.local_ip4,
2111                      self.pg2.remote_hosts[2].ip4]))
2112
2113         #
2114         # check reachability and contract intra-epg
2115         #
2116         allow_intra_class = self.statistics.get_err_counter(
2117             '/err/gbp-policy-mac/allow-intra-sclass')
2118
2119         for l in learnt:
2120             p = (Ether(src=ep.mac, dst=l['mac']) /
2121                  IPv6(dst=l['ip6'], src=ep.ip6) /
2122                  UDP(sport=1234, dport=1234) /
2123                  Raw(b'\xa5' * 100))
2124
2125             rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
2126
2127             for rx in rxs:
2128                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
2129                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
2130                 self.assertEqual(rx[UDP].dport, 48879)
2131                 self.assertEqual(rx[VXLAN].gpid, 112)
2132                 self.assertEqual(rx[VXLAN].vni, 99)
2133                 self.assertTrue(rx[VXLAN].flags.G)
2134                 self.assertTrue(rx[VXLAN].flags.Instance)
2135                 self.assertTrue(rx[VXLAN].gpflags.A)
2136                 self.assertFalse(rx[VXLAN].gpflags.D)
2137                 self.assertEqual(rx[IPv6].dst, l['ip6'])
2138
2139             allow_intra_class += NUM_PKTS
2140
2141         self.assert_error_counter_equal(
2142             '/err/gbp-policy-mac/allow-intra-sclass',
2143             allow_intra_class)
2144
2145         #
2146         # clean up
2147         #
2148         for l in learnt:
2149             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
2150                                      mac=l['mac'])
2151         self.pg2.unconfig_ip4()
2152         self.pg3.unconfig_ip4()
2153         self.pg4.unconfig_ip4()
2154
2155     def test_gbp_contract(self):
2156         """ GBP Contracts """
2157
2158         #
2159         # Route Domains
2160         #
2161         gt4 = VppIpTable(self, 0)
2162         gt4.add_vpp_config()
2163         gt6 = VppIpTable(self, 0, is_ip6=True)
2164         gt6.add_vpp_config()
2165
2166         rd0 = VppGbpRouteDomain(self, 0, 400, gt4, gt6, None, None)
2167
2168         rd0.add_vpp_config()
2169
2170         #
2171         # Bridge Domains
2172         #
2173         bd1 = VppBridgeDomain(self, 1, arp_term=0)
2174         bd2 = VppBridgeDomain(self, 2, arp_term=0)
2175
2176         bd1.add_vpp_config()
2177         bd2.add_vpp_config()
2178
2179         gbd1 = VppGbpBridgeDomain(self, bd1, rd0, self.loop0)
2180         gbd2 = VppGbpBridgeDomain(self, bd2, rd0, self.loop1)
2181
2182         gbd1.add_vpp_config()
2183         gbd2.add_vpp_config()
2184
2185         #
2186         # 3 EPGs, 2 of which share a BD.
2187         #
2188         epgs = [VppGbpEndpointGroup(self, 220, 1220, rd0, gbd1,
2189                                     None, self.loop0,
2190                                     "10.0.0.128", "2001:10::128"),
2191                 VppGbpEndpointGroup(self, 221, 1221, rd0, gbd1,
2192                                     None, self.loop0,
2193                                     "10.0.1.128", "2001:10:1::128"),
2194                 VppGbpEndpointGroup(self, 222, 1222, rd0, gbd2,
2195                                     None, self.loop1,
2196                                     "10.0.2.128", "2001:10:2::128")]
2197         #
2198         # 4 end-points, 2 in the same subnet, 3 in the same BD
2199         #
2200         eps = [VppGbpEndpoint(self, self.pg0,
2201                               epgs[0], None,
2202                               "10.0.0.1", "11.0.0.1",
2203                               "2001:10::1", "3001::1"),
2204                VppGbpEndpoint(self, self.pg1,
2205                               epgs[0], None,
2206                               "10.0.0.2", "11.0.0.2",
2207                               "2001:10::2", "3001::2"),
2208                VppGbpEndpoint(self, self.pg2,
2209                               epgs[1], None,
2210                               "10.0.1.1", "11.0.0.3",
2211                               "2001:10:1::1", "3001::3"),
2212                VppGbpEndpoint(self, self.pg3,
2213                               epgs[2], None,
2214                               "10.0.2.1", "11.0.0.4",
2215                               "2001:10:2::1", "3001::4")]
2216
2217         #
2218         # Config related to each of the EPGs
2219         #
2220         for epg in epgs:
2221             # IP config on the BVI interfaces
2222             if epg != epgs[1]:
2223                 VppIpInterfaceBind(self, epg.bvi, epg.rd.t4).add_vpp_config()
2224                 VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config()
2225                 epg.bvi.set_mac(self.router_mac)
2226
2227             if_ip4 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip4, 32)
2228             if_ip6 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip6, 128)
2229             if_ip4.add_vpp_config()
2230             if_ip6.add_vpp_config()
2231
2232             # add the BD ARP termination entry for BVI IP
2233             epg.bd_arp_ip4 = VppBridgeDomainArpEntry(self, epg.bd.bd,
2234                                                      str(self.router_mac),
2235                                                      epg.bvi_ip4)
2236             epg.bd_arp_ip4.add_vpp_config()
2237
2238             # EPG in VPP
2239             epg.add_vpp_config()
2240
2241         #
2242         # config ep
2243         #
2244         for ep in eps:
2245             ep.add_vpp_config()
2246
2247         self.logger.info(self.vapi.cli("show gbp endpoint"))
2248         self.logger.info(self.vapi.cli("show interface"))
2249         self.logger.info(self.vapi.cli("show br"))
2250
2251         #
2252         # Intra epg allowed without contract
2253         #
2254         pkt_intra_epg_220_to_220 = (Ether(src=self.pg0.remote_mac,
2255                                           dst=self.pg1.remote_mac) /
2256                                     IP(src=eps[0].ip4,
2257                                        dst=eps[1].ip4) /
2258                                     UDP(sport=1234, dport=1234) /
2259                                     Raw(b'\xa5' * 100))
2260
2261         self.send_and_expect_bridged(self.pg0,
2262                                      pkt_intra_epg_220_to_220 * 65,
2263                                      self.pg1)
2264
2265         pkt_intra_epg_220_to_220 = (Ether(src=self.pg0.remote_mac,
2266                                           dst=self.pg1.remote_mac) /
2267                                     IPv6(src=eps[0].ip6,
2268                                          dst=eps[1].ip6) /
2269                                     UDP(sport=1234, dport=1234) /
2270                                     Raw(b'\xa5' * 100))
2271
2272         self.send_and_expect_bridged6(self.pg0,
2273                                       pkt_intra_epg_220_to_220 * 65,
2274                                       self.pg1)
2275
2276         #
2277         # Inter epg denied without contract
2278         #
2279         pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
2280                                           dst=self.pg2.remote_mac) /
2281                                     IP(src=eps[0].ip4,
2282                                        dst=eps[2].ip4) /
2283                                     UDP(sport=1234, dport=1234) /
2284                                     Raw(b'\xa5' * 100))
2285
2286         self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_220_to_221)
2287
2288         #
2289         # A uni-directional contract from EPG 220 -> 221
2290         #
2291         rule = AclRule(is_permit=1, proto=17)
2292         rule2 = AclRule(src_prefix=IPv6Network((0, 0)),
2293                         dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17)
2294         rule3 = AclRule(is_permit=1, proto=1)
2295         acl = VppAcl(self, rules=[rule, rule2, rule3])
2296         acl.add_vpp_config()
2297
2298         c1 = VppGbpContract(
2299             self, 400, epgs[0].sclass, epgs[1].sclass, acl.acl_index,
2300             [VppGbpContractRule(
2301                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
2302                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2303                 []),
2304              VppGbpContractRule(
2305                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
2306                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2307                  []),
2308              VppGbpContractRule(
2309                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
2310                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2311                  [])],
2312             [ETH_P_IP, ETH_P_IPV6])
2313         c1.add_vpp_config()
2314
2315         self.send_and_expect_bridged(eps[0].itf,
2316                                      pkt_inter_epg_220_to_221 * 65,
2317                                      eps[2].itf)
2318
2319         pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
2320                                           dst=str(self.router_mac)) /
2321                                     IP(src=eps[0].ip4,
2322                                        dst=eps[3].ip4) /
2323                                     UDP(sport=1234, dport=1234) /
2324                                     Raw(b'\xa5' * 100))
2325         self.send_and_assert_no_replies(eps[0].itf,
2326                                         pkt_inter_epg_220_to_222 * 65)
2327
2328         #
2329         # ping router IP in different BD
2330         #
2331         pkt_router_ping_220_to_221 = (Ether(src=self.pg0.remote_mac,
2332                                             dst=str(self.router_mac)) /
2333                                       IP(src=eps[0].ip4,
2334                                          dst=epgs[1].bvi_ip4) /
2335                                       ICMP(type='echo-request'))
2336
2337         self.send_and_expect(self.pg0, [pkt_router_ping_220_to_221], self.pg0)
2338
2339         pkt_router_ping_220_to_221 = (Ether(src=self.pg0.remote_mac,
2340                                             dst=str(self.router_mac)) /
2341                                       IPv6(src=eps[0].ip6,
2342                                            dst=epgs[1].bvi_ip6) /
2343                                       ICMPv6EchoRequest())
2344
2345         self.send_and_expect(self.pg0, [pkt_router_ping_220_to_221], self.pg0)
2346
2347         #
2348         # contract for the return direction
2349         #
2350         c2 = VppGbpContract(
2351             self, 400, epgs[1].sclass, epgs[0].sclass, acl.acl_index,
2352             [VppGbpContractRule(
2353                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
2354                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2355                 []),
2356              VppGbpContractRule(
2357                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
2358                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2359                  [])],
2360             [ETH_P_IP, ETH_P_IPV6])
2361         c2.add_vpp_config()
2362
2363         self.send_and_expect_bridged(eps[0].itf,
2364                                      pkt_inter_epg_220_to_221 * 65,
2365                                      eps[2].itf)
2366         pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
2367                                           dst=self.pg0.remote_mac) /
2368                                     IP(src=eps[2].ip4,
2369                                        dst=eps[0].ip4) /
2370                                     UDP(sport=1234, dport=1234) /
2371                                     Raw(b'\xa5' * 100))
2372         self.send_and_expect_bridged(eps[2].itf,
2373                                      pkt_inter_epg_221_to_220 * 65,
2374                                      eps[0].itf)
2375         pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
2376                                           dst=str(self.router_mac)) /
2377                                     IP(src=eps[2].ip4,
2378                                        dst=eps[0].ip4) /
2379                                     UDP(sport=1234, dport=1234) /
2380                                     Raw(b'\xa5' * 100))
2381         self.send_and_expect_routed(eps[2].itf,
2382                                     pkt_inter_epg_221_to_220 * 65,
2383                                     eps[0].itf,
2384                                     str(self.router_mac))
2385         pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
2386                                           dst=str(self.router_mac)) /
2387                                     IPv6(src=eps[2].ip6,
2388                                          dst=eps[0].ip6) /
2389                                     UDP(sport=1234, dport=1234) /
2390                                     Raw(b'\xa5' * 100))
2391         self.send_and_expect_routed6(eps[2].itf,
2392                                      pkt_inter_epg_221_to_220 * 65,
2393                                      eps[0].itf,
2394                                      str(self.router_mac))
2395
2396         #
2397         # contract between 220 and 222 uni-direction
2398         #
2399         c3 = VppGbpContract(
2400             self, 400, epgs[0].sclass, epgs[2].sclass, acl.acl_index,
2401             [VppGbpContractRule(
2402                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
2403                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2404                 []),
2405              VppGbpContractRule(
2406                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
2407                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2408                  [])],
2409             [ETH_P_IP, ETH_P_IPV6])
2410         c3.add_vpp_config()
2411
2412         self.send_and_expect(eps[0].itf,
2413                              pkt_inter_epg_220_to_222 * 65,
2414                              eps[3].itf)
2415
2416         c3.remove_vpp_config()
2417         c1.remove_vpp_config()
2418         c2.remove_vpp_config()
2419         acl.remove_vpp_config()
2420
2421     def test_gbp_bd_drop_flags(self):
2422         """ GBP BD drop flags """
2423
2424         #
2425         # IP tables
2426         #
2427         gt4 = VppIpTable(self, 1)
2428         gt4.add_vpp_config()
2429         gt6 = VppIpTable(self, 1, is_ip6=True)
2430         gt6.add_vpp_config()
2431
2432         rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
2433         rd1.add_vpp_config()
2434
2435         #
2436         # a GBP bridge domain with a BVI only
2437         #
2438         bd1 = VppBridgeDomain(self, 1)
2439         bd1.add_vpp_config()
2440
2441         gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0,
2442                                   None, None,
2443                                   uu_drop=True, bm_drop=True)
2444         gbd1.add_vpp_config()
2445
2446         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
2447         self.logger.info(self.vapi.cli("sh gbp bridge"))
2448
2449         # ... and has a /32 applied
2450         ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
2451         ip_addr.add_vpp_config()
2452
2453         #
2454         # The Endpoint-group
2455         #
2456         epg_220 = VppGbpEndpointGroup(self, 220, 112, rd1, gbd1,
2457                                       None, self.loop0,
2458                                       "10.0.0.128",
2459                                       "2001:10::128",
2460                                       VppGbpEndpointRetention(2))
2461         epg_220.add_vpp_config()
2462
2463         ep = VppGbpEndpoint(self, self.pg0,
2464                             epg_220, None,
2465                             "10.0.0.127", "11.0.0.127",
2466                             "2001:10::1", "3001::1")
2467         ep.add_vpp_config()
2468
2469         #
2470         # send UU/BM packet from the local EP with UU drop and BM drop enabled
2471         # in bd
2472         #
2473         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
2474         self.logger.info(self.vapi.cli("sh gbp bridge"))
2475         p_uu = (Ether(src=ep.mac, dst="00:11:11:11:11:11") /
2476                 IP(dst="10.0.0.133", src=ep.ip4) /
2477                 UDP(sport=1234, dport=1234) /
2478                 Raw(b'\xa5' * 100))
2479         self.send_and_assert_no_replies(ep.itf, [p_uu])
2480
2481         p_bm = (Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") /
2482                 IP(dst="10.0.0.133", src=ep.ip4) /
2483                 UDP(sport=1234, dport=1234) /
2484                 Raw(b'\xa5' * 100))
2485         self.send_and_assert_no_replies(ep.itf, [p_bm])
2486
2487         self.pg3.unconfig_ip4()
2488
2489         self.logger.info(self.vapi.cli("sh int"))
2490
2491     def test_gbp_bd_arp_flags(self):
2492         """ GBP BD arp flags """
2493
2494         #
2495         # IP tables
2496         #
2497         gt4 = VppIpTable(self, 1)
2498         gt4.add_vpp_config()
2499         gt6 = VppIpTable(self, 1, is_ip6=True)
2500         gt6.add_vpp_config()
2501
2502         rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
2503         rd1.add_vpp_config()
2504
2505         #
2506         # Pg4 hosts the IP6 UU-flood VXLAN tunnel
2507         #
2508         self.pg4.config_ip4()
2509         self.pg4.resolve_arp()
2510
2511         #
2512         # Add a mcast destination VXLAN-GBP tunnel for B&M traffic
2513         #
2514         tun_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
2515                                    "239.1.1.1", 88,
2516                                    mcast_itf=self.pg4)
2517         tun_uu.add_vpp_config()
2518
2519         #
2520         # a GBP bridge domain with a BVI and a UU-flood interface
2521         #
2522         bd1 = VppBridgeDomain(self, 1)
2523         bd1.add_vpp_config()
2524
2525         gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0,
2526                                   tun_uu, None,
2527                                   ucast_arp=True)
2528         gbd1.add_vpp_config()
2529
2530         # ... and has a /32 applied
2531         ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
2532         ip_addr.add_vpp_config()
2533
2534         #
2535         # The Endpoint-group
2536         #
2537         epg_220 = VppGbpEndpointGroup(self, 220, 112, rd1, gbd1,
2538                                       None, self.loop0,
2539                                       "10.0.0.128",
2540                                       "2001:10::128",
2541                                       VppGbpEndpointRetention(2))
2542         epg_220.add_vpp_config()
2543
2544         ep = VppGbpEndpoint(self, self.pg0,
2545                             epg_220, None,
2546                             "10.0.0.127", "11.0.0.127",
2547                             "2001:10::1", "3001::1")
2548         ep.add_vpp_config()
2549
2550         #
2551         # send ARP packet from the local EP expect it on the uu interface
2552         #
2553         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
2554         self.logger.info(self.vapi.cli("sh gbp bridge"))
2555         p_arp = (Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") /
2556                  ARP(op="who-has",
2557                      psrc=ep.ip4, pdst="10.0.0.99",
2558                      hwsrc=ep.mac,
2559                      hwdst="ff:ff:ff:ff:ff:ff"))
2560         self.send_and_expect(ep.itf, [p_arp], self.pg4)
2561
2562         self.pg4.unconfig_ip4()
2563
2564     def test_gbp_learn_vlan_l2(self):
2565         """ GBP L2 Endpoint w/ VLANs"""
2566
2567         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
2568         learnt = [{'mac': '00:00:11:11:11:01',
2569                    'ip': '10.0.0.1',
2570                    'ip6': '2001:10::2'},
2571                   {'mac': '00:00:11:11:11:02',
2572                    'ip': '10.0.0.2',
2573                    'ip6': '2001:10::3'}]
2574
2575         #
2576         # IP tables
2577         #
2578         gt4 = VppIpTable(self, 1)
2579         gt4.add_vpp_config()
2580         gt6 = VppIpTable(self, 1, is_ip6=True)
2581         gt6.add_vpp_config()
2582
2583         rd1 = VppGbpRouteDomain(self, 1, 401, gt4, gt6)
2584         rd1.add_vpp_config()
2585
2586         #
2587         # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs
2588         #
2589         self.pg2.config_ip4()
2590         self.pg2.resolve_arp()
2591         self.pg2.generate_remote_hosts(4)
2592         self.pg2.configure_ipv4_neighbors()
2593         self.pg3.config_ip4()
2594         self.pg3.resolve_arp()
2595
2596         #
2597         # The EP will be on a vlan sub-interface
2598         #
2599         vlan_11 = VppDot1QSubint(self, self.pg0, 11)
2600         vlan_11.admin_up()
2601         self.vapi.l2_interface_vlan_tag_rewrite(
2602             sw_if_index=vlan_11.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1,
2603             push_dot1q=11)
2604
2605         bd_uu_fwd = VppVxlanGbpTunnel(self, self.pg3.local_ip4,
2606                                       self.pg3.remote_ip4, 116)
2607         bd_uu_fwd.add_vpp_config()
2608
2609         #
2610         # a GBP bridge domain with a BVI and a UU-flood interface
2611         # The BD is marked as do not learn, so no endpoints are ever
2612         # learnt in this BD.
2613         #
2614         bd1 = VppBridgeDomain(self, 1)
2615         bd1.add_vpp_config()
2616         gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, bd_uu_fwd,
2617                                   learn=False)
2618         gbd1.add_vpp_config()
2619
2620         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
2621         self.logger.info(self.vapi.cli("sh gbp bridge"))
2622
2623         # ... and has a /32 applied
2624         ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
2625         ip_addr.add_vpp_config()
2626
2627         #
2628         # The Endpoint-group in which we are learning endpoints
2629         #
2630         epg_220 = VppGbpEndpointGroup(self, 220, 441, rd1, gbd1,
2631                                       None, self.loop0,
2632                                       "10.0.0.128",
2633                                       "2001:10::128",
2634                                       VppGbpEndpointRetention(2))
2635         epg_220.add_vpp_config()
2636
2637         #
2638         # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
2639         # learning enabled
2640         #
2641         vx_tun_l2_1 = VppGbpVxlanTunnel(
2642             self, 99, bd1.bd_id,
2643             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2,
2644             self.pg2.local_ip4)
2645         vx_tun_l2_1.add_vpp_config()
2646
2647         #
2648         # A static endpoint that the learnt endpoints are trying to
2649         # talk to
2650         #
2651         ep = VppGbpEndpoint(self, vlan_11,
2652                             epg_220, None,
2653                             "10.0.0.127", "11.0.0.127",
2654                             "2001:10::1", "3001::1")
2655         ep.add_vpp_config()
2656
2657         self.assertTrue(find_route(self, ep.ip4, 32, table_id=1))
2658
2659         #
2660         # Send to the static EP
2661         #
2662         for ii, l in enumerate(learnt):
2663             # a packet with an sclass from a known EPG
2664             # arriving on an unknown TEP
2665             p = (Ether(src=self.pg2.remote_mac,
2666                        dst=self.pg2.local_mac) /
2667                  IP(src=self.pg2.remote_hosts[1].ip4,
2668                     dst=self.pg2.local_ip4) /
2669                  UDP(sport=1234, dport=48879) /
2670                  VXLAN(vni=99, gpid=441, flags=0x88) /
2671                  Ether(src=l['mac'], dst=ep.mac) /
2672                  IP(src=l['ip'], dst=ep.ip4) /
2673                  UDP(sport=1234, dport=1234) /
2674                  Raw(b'\xa5' * 100))
2675
2676             rxs = self.send_and_expect(self.pg2, [p], self.pg0)
2677
2678             #
2679             # packet to EP has the EP's vlan tag
2680             #
2681             for rx in rxs:
2682                 self.assertEqual(rx[Dot1Q].vlan, 11)
2683
2684             #
2685             # the EP is not learnt since the BD setting prevents it
2686             # also no TEP too
2687             #
2688             self.assertFalse(find_gbp_endpoint(self,
2689                                                vx_tun_l2_1.sw_if_index,
2690                                                mac=l['mac']))
2691             self.assertEqual(INDEX_INVALID,
2692                              find_vxlan_gbp_tunnel(
2693                                  self,
2694                                  self.pg2.local_ip4,
2695                                  self.pg2.remote_hosts[1].ip4,
2696                                  99))
2697
2698         self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1)
2699
2700         #
2701         # static to remotes
2702         # we didn't learn the remotes so they are sent to the UU-fwd
2703         #
2704         for l in learnt:
2705             p = (Ether(src=ep.mac, dst=l['mac']) /
2706                  Dot1Q(vlan=11) /
2707                  IP(dst=l['ip'], src=ep.ip4) /
2708                  UDP(sport=1234, dport=1234) /
2709                  Raw(b'\xa5' * 100))
2710
2711             rxs = self.send_and_expect(self.pg0, p * 17, self.pg3)
2712
2713             for rx in rxs:
2714                 self.assertEqual(rx[IP].src, self.pg3.local_ip4)
2715                 self.assertEqual(rx[IP].dst, self.pg3.remote_ip4)
2716                 self.assertEqual(rx[UDP].dport, 48879)
2717                 # the UDP source port is a random value for hashing
2718                 self.assertEqual(rx[VXLAN].gpid, 441)
2719                 self.assertEqual(rx[VXLAN].vni, 116)
2720                 self.assertTrue(rx[VXLAN].flags.G)
2721                 self.assertTrue(rx[VXLAN].flags.Instance)
2722                 self.assertFalse(rx[VXLAN].gpflags.A)
2723                 self.assertFalse(rx[VXLAN].gpflags.D)
2724
2725         self.pg2.unconfig_ip4()
2726         self.pg3.unconfig_ip4()
2727
2728     def test_gbp_learn_l3(self):
2729         """ GBP L3 Endpoint Learning """
2730
2731         self.vapi.cli("set logging class gbp level debug")
2732
2733         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
2734         routed_dst_mac = "00:0c:0c:0c:0c:0c"
2735         routed_src_mac = "00:22:bd:f8:19:ff"
2736
2737         learnt = [{'mac': '00:00:11:11:11:02',
2738                    'ip': '10.0.1.2',
2739                    'ip6': '2001:10::2'},
2740                   {'mac': '00:00:11:11:11:03',
2741                    'ip': '10.0.1.3',
2742                    'ip6': '2001:10::3'}]
2743
2744         #
2745         # IP tables
2746         #
2747         t4 = VppIpTable(self, 1)
2748         t4.add_vpp_config()
2749         t6 = VppIpTable(self, 1, True)
2750         t6.add_vpp_config()
2751
2752         tun_ip4_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
2753                                        self.pg4.remote_ip4, 114)
2754         tun_ip6_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
2755                                        self.pg4.remote_ip4, 116)
2756         tun_ip4_uu.add_vpp_config()
2757         tun_ip6_uu.add_vpp_config()
2758
2759         rd1 = VppGbpRouteDomain(self, 2, 401, t4, t6, tun_ip4_uu, tun_ip6_uu)
2760         rd1.add_vpp_config()
2761
2762         self.loop0.set_mac(self.router_mac)
2763
2764         #
2765         # Bind the BVI to the RD
2766         #
2767         VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
2768         VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
2769
2770         #
2771         # Pg2 hosts the vxlan tunnel
2772         # hosts on pg2 to act as TEPs
2773         # pg3 is BD uu-fwd
2774         # pg4 is RD uu-fwd
2775         #
2776         self.pg2.config_ip4()
2777         self.pg2.resolve_arp()
2778         self.pg2.generate_remote_hosts(4)
2779         self.pg2.configure_ipv4_neighbors()
2780         self.pg3.config_ip4()
2781         self.pg3.resolve_arp()
2782         self.pg4.config_ip4()
2783         self.pg4.resolve_arp()
2784
2785         #
2786         # a GBP bridge domain with a BVI and a UU-flood interface
2787         #
2788         bd1 = VppBridgeDomain(self, 1)
2789         bd1.add_vpp_config()
2790         gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, self.pg3)
2791         gbd1.add_vpp_config()
2792
2793         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
2794         self.logger.info(self.vapi.cli("sh gbp bridge"))
2795         self.logger.info(self.vapi.cli("sh gbp route"))
2796
2797         # ... and has a /32 and /128 applied
2798         ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
2799         ip4_addr.add_vpp_config()
2800         ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 128)
2801         ip6_addr.add_vpp_config()
2802
2803         #
2804         # The Endpoint-group in which we are learning endpoints
2805         #
2806         epg_220 = VppGbpEndpointGroup(self, 220, 441, rd1, gbd1,
2807                                       None, self.loop0,
2808                                       "10.0.0.128",
2809                                       "2001:10::128",
2810                                       VppGbpEndpointRetention(2))
2811         epg_220.add_vpp_config()
2812
2813         #
2814         # The VXLAN GBP tunnel is in L3 mode with learning enabled
2815         #
2816         vx_tun_l3 = VppGbpVxlanTunnel(
2817             self, 101, rd1.rd_id,
2818             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
2819             self.pg2.local_ip4)
2820         vx_tun_l3.add_vpp_config()
2821
2822         #
2823         # A static endpoint that the learnt endpoints are trying to
2824         # talk to
2825         #
2826         ep = VppGbpEndpoint(self, self.pg0,
2827                             epg_220, None,
2828                             "10.0.0.127", "11.0.0.127",
2829                             "2001:10::1", "3001::1")
2830         ep.add_vpp_config()
2831
2832         #
2833         # learn some remote IPv4 EPs
2834         #
2835         for ii, l in enumerate(learnt):
2836             # a packet with an sclass from a known EPG
2837             # arriving on an unknown TEP
2838             p = (Ether(src=self.pg2.remote_mac,
2839                        dst=self.pg2.local_mac) /
2840                  IP(src=self.pg2.remote_hosts[1].ip4,
2841                     dst=self.pg2.local_ip4) /
2842                  UDP(sport=1234, dport=48879) /
2843                  VXLAN(vni=101, gpid=441, flags=0x88) /
2844                  Ether(src=l['mac'], dst="00:00:00:11:11:11") /
2845                  IP(src=l['ip'], dst=ep.ip4) /
2846                  UDP(sport=1234, dport=1234) /
2847                  Raw(b'\xa5' * 100))
2848
2849             rx = self.send_and_expect(self.pg2, [p], self.pg0)
2850
2851             # the new TEP
2852             tep1_sw_if_index = find_vxlan_gbp_tunnel(
2853                 self,
2854                 self.pg2.local_ip4,
2855                 self.pg2.remote_hosts[1].ip4,
2856                 vx_tun_l3.vni)
2857             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
2858
2859             # endpoint learnt via the parent GBP-vxlan interface
2860             self.assertTrue(find_gbp_endpoint(self,
2861                                               vx_tun_l3._sw_if_index,
2862                                               ip=l['ip']))
2863
2864         #
2865         # Static IPv4 EP replies to learnt
2866         #
2867         for l in learnt:
2868             p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2869                  IP(dst=l['ip'], src=ep.ip4) /
2870                  UDP(sport=1234, dport=1234) /
2871                  Raw(b'\xa5' * 100))
2872
2873             rxs = self.send_and_expect(self.pg0, p * 1, self.pg2)
2874
2875             for rx in rxs:
2876                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
2877                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
2878                 self.assertEqual(rx[UDP].dport, 48879)
2879                 # the UDP source port is a random value for hashing
2880                 self.assertEqual(rx[VXLAN].gpid, 441)
2881                 self.assertEqual(rx[VXLAN].vni, 101)
2882                 self.assertTrue(rx[VXLAN].flags.G)
2883                 self.assertTrue(rx[VXLAN].flags.Instance)
2884                 self.assertTrue(rx[VXLAN].gpflags.A)
2885                 self.assertFalse(rx[VXLAN].gpflags.D)
2886
2887                 inner = rx[VXLAN].payload
2888
2889                 self.assertEqual(inner[Ether].src, routed_src_mac)
2890                 self.assertEqual(inner[Ether].dst, routed_dst_mac)
2891                 self.assertEqual(inner[IP].src, ep.ip4)
2892                 self.assertEqual(inner[IP].dst, l['ip'])
2893
2894         for l in learnt:
2895             self.assertFalse(find_gbp_endpoint(self,
2896                                                tep1_sw_if_index,
2897                                                ip=l['ip']))
2898
2899         #
2900         # learn some remote IPv6 EPs
2901         #
2902         for ii, l in enumerate(learnt):
2903             # a packet with an sclass from a known EPG
2904             # arriving on an unknown TEP
2905             p = (Ether(src=self.pg2.remote_mac,
2906                        dst=self.pg2.local_mac) /
2907                  IP(src=self.pg2.remote_hosts[1].ip4,
2908                     dst=self.pg2.local_ip4) /
2909                  UDP(sport=1234, dport=48879) /
2910                  VXLAN(vni=101, gpid=441, flags=0x88) /
2911                  Ether(src=l['mac'], dst="00:00:00:11:11:11") /
2912                  IPv6(src=l['ip6'], dst=ep.ip6) /
2913                  UDP(sport=1234, dport=1234) /
2914                  Raw(b'\xa5' * 100))
2915
2916             rx = self.send_and_expect(self.pg2, [p], self.pg0)
2917
2918             # the new TEP
2919             tep1_sw_if_index = find_vxlan_gbp_tunnel(
2920                 self,
2921                 self.pg2.local_ip4,
2922                 self.pg2.remote_hosts[1].ip4,
2923                 vx_tun_l3.vni)
2924             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
2925
2926             self.logger.info(self.vapi.cli("show gbp bridge"))
2927             self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
2928             self.logger.info(self.vapi.cli("show gbp vxlan"))
2929             self.logger.info(self.vapi.cli("show int addr"))
2930
2931             # endpoint learnt via the TEP
2932             self.assertTrue(find_gbp_endpoint(self, ip=l['ip6']))
2933
2934         self.logger.info(self.vapi.cli("show gbp endpoint"))
2935         self.logger.info(self.vapi.cli("show ip fib index 1 %s" % l['ip']))
2936
2937         #
2938         # Static EP replies to learnt
2939         #
2940         for l in learnt:
2941             p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2942                  IPv6(dst=l['ip6'], src=ep.ip6) /
2943                  UDP(sport=1234, dport=1234) /
2944                  Raw(b'\xa5' * 100))
2945
2946             rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
2947
2948             for rx in rxs:
2949                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
2950                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
2951                 self.assertEqual(rx[UDP].dport, 48879)
2952                 # the UDP source port is a random value for hashing
2953                 self.assertEqual(rx[VXLAN].gpid, 441)
2954                 self.assertEqual(rx[VXLAN].vni, 101)
2955                 self.assertTrue(rx[VXLAN].flags.G)
2956                 self.assertTrue(rx[VXLAN].flags.Instance)
2957                 self.assertTrue(rx[VXLAN].gpflags.A)
2958                 self.assertFalse(rx[VXLAN].gpflags.D)
2959
2960                 inner = rx[VXLAN].payload
2961
2962                 self.assertEqual(inner[Ether].src, routed_src_mac)
2963                 self.assertEqual(inner[Ether].dst, routed_dst_mac)
2964                 self.assertEqual(inner[IPv6].src, ep.ip6)
2965                 self.assertEqual(inner[IPv6].dst, l['ip6'])
2966
2967         self.logger.info(self.vapi.cli("sh gbp endpoint"))
2968         for l in learnt:
2969             self.wait_for_ep_timeout(ip=l['ip'])
2970
2971         #
2972         # Static sends to unknown EP with no route
2973         #
2974         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2975              IP(dst="10.0.0.99", src=ep.ip4) /
2976              UDP(sport=1234, dport=1234) /
2977              Raw(b'\xa5' * 100))
2978
2979         self.send_and_assert_no_replies(self.pg0, [p])
2980
2981         #
2982         # Add a route to static EP's v4 and v6 subnet
2983         #
2984         se_10_24 = VppGbpSubnet(
2985             self, rd1, "10.0.0.0", 24,
2986             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT)
2987         se_10_24.add_vpp_config()
2988
2989         #
2990         # static pings router
2991         #
2992         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2993              IP(dst=epg_220.bvi_ip4, src=ep.ip4) /
2994              UDP(sport=1234, dport=1234) /
2995              Raw(b'\xa5' * 100))
2996
2997         self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg0)
2998
2999         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
3000              IPv6(dst=epg_220.bvi_ip6, src=ep.ip6) /
3001              UDP(sport=1234, dport=1234) /
3002              Raw(b'\xa5' * 100))
3003
3004         self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg0)
3005
3006         #
3007         # packets to address in the subnet are sent on the uu-fwd
3008         #
3009         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
3010              IP(dst="10.0.0.99", src=ep.ip4) /
3011              UDP(sport=1234, dport=1234) /
3012              Raw(b'\xa5' * 100))
3013
3014         rxs = self.send_and_expect(self.pg0, [p], self.pg4)
3015         for rx in rxs:
3016             self.assertEqual(rx[IP].src, self.pg4.local_ip4)
3017             self.assertEqual(rx[IP].dst, self.pg4.remote_ip4)
3018             self.assertEqual(rx[UDP].dport, 48879)
3019             # the UDP source port is a random value for hashing
3020             self.assertEqual(rx[VXLAN].gpid, 441)
3021             self.assertEqual(rx[VXLAN].vni, 114)
3022             self.assertTrue(rx[VXLAN].flags.G)
3023             self.assertTrue(rx[VXLAN].flags.Instance)
3024             # policy is not applied to packets sent to the uu-fwd interfaces
3025             self.assertFalse(rx[VXLAN].gpflags.A)
3026             self.assertFalse(rx[VXLAN].gpflags.D)
3027
3028         #
3029         # learn some remote IPv4 EPs
3030         #
3031         for ii, l in enumerate(learnt):
3032             # a packet with an sclass from a known EPG
3033             # arriving on an unknown TEP
3034             p = (Ether(src=self.pg2.remote_mac,
3035                        dst=self.pg2.local_mac) /
3036                  IP(src=self.pg2.remote_hosts[2].ip4,
3037                     dst=self.pg2.local_ip4) /
3038                  UDP(sport=1234, dport=48879) /
3039                  VXLAN(vni=101, gpid=441, flags=0x88) /
3040                  Ether(src=l['mac'], dst="00:00:00:11:11:11") /
3041                  IP(src=l['ip'], dst=ep.ip4) /
3042                  UDP(sport=1234, dport=1234) /
3043                  Raw(b'\xa5' * 100))
3044
3045             rx = self.send_and_expect(self.pg2, [p], self.pg0)
3046
3047             # the new TEP
3048             tep1_sw_if_index = find_vxlan_gbp_tunnel(
3049                 self,
3050                 self.pg2.local_ip4,
3051                 self.pg2.remote_hosts[2].ip4,
3052                 vx_tun_l3.vni)
3053             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
3054
3055             # endpoint learnt via the parent GBP-vxlan interface
3056             self.assertTrue(find_gbp_endpoint(self,
3057                                               vx_tun_l3._sw_if_index,
3058                                               ip=l['ip']))
3059
3060         #
3061         # Add a remote endpoint from the API
3062         #
3063         rep_88 = VppGbpEndpoint(self, vx_tun_l3,
3064                                 epg_220, None,
3065                                 "10.0.0.88", "11.0.0.88",
3066                                 "2001:10::88", "3001::88",
3067                                 ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
3068                                 self.pg2.local_ip4,
3069                                 self.pg2.remote_hosts[2].ip4,
3070                                 mac=None)
3071         rep_88.add_vpp_config()
3072
3073         #
3074         # Add a remote endpoint from the API that matches an existing one
3075         # this is a lower priority, hence the packet is sent to the DP leanrt
3076         # TEP
3077         #
3078         rep_2 = VppGbpEndpoint(self, vx_tun_l3,
3079                                epg_220, None,
3080                                learnt[0]['ip'], "11.0.0.101",
3081                                learnt[0]['ip6'], "3001::101",
3082                                ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
3083                                self.pg2.local_ip4,
3084                                self.pg2.remote_hosts[1].ip4,
3085                                mac=None)
3086         rep_2.add_vpp_config()
3087
3088         #
3089         # Add a route to the learned EP's v4 subnet
3090         #  packets should be send on the v4/v6 uu=fwd interface resp.
3091         #
3092         se_10_1_24 = VppGbpSubnet(
3093             self, rd1, "10.0.1.0", 24,
3094             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT)
3095         se_10_1_24.add_vpp_config()
3096
3097         self.logger.info(self.vapi.cli("show gbp endpoint"))
3098
3099         ips = ["10.0.0.88", learnt[0]['ip']]
3100         for ip in ips:
3101             p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
3102                  IP(dst=ip, src=ep.ip4) /
3103                  UDP(sport=1234, dport=1234) /
3104                  Raw(b'\xa5' * 100))
3105
3106             rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
3107
3108             for rx in rxs:
3109                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
3110                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
3111                 self.assertEqual(rx[UDP].dport, 48879)
3112                 # the UDP source port is a random value for hashing
3113                 self.assertEqual(rx[VXLAN].gpid, 441)
3114                 self.assertEqual(rx[VXLAN].vni, 101)
3115                 self.assertTrue(rx[VXLAN].flags.G)
3116                 self.assertTrue(rx[VXLAN].flags.Instance)
3117                 self.assertTrue(rx[VXLAN].gpflags.A)
3118                 self.assertFalse(rx[VXLAN].gpflags.D)
3119
3120                 inner = rx[VXLAN].payload
3121
3122                 self.assertEqual(inner[Ether].src, routed_src_mac)
3123                 self.assertEqual(inner[Ether].dst, routed_dst_mac)
3124                 self.assertEqual(inner[IP].src, ep.ip4)
3125                 self.assertEqual(inner[IP].dst, ip)
3126
3127         #
3128         # remove the API remote EPs, only API sourced is gone, the DP
3129         # learnt one remains
3130         #
3131         rep_88.remove_vpp_config()
3132         rep_2.remove_vpp_config()
3133
3134         self.assertTrue(find_gbp_endpoint(self, ip=rep_2.ip4))
3135
3136         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
3137              IP(src=ep.ip4, dst=rep_2.ip4) /
3138              UDP(sport=1234, dport=1234) /
3139              Raw(b'\xa5' * 100))
3140         rxs = self.send_and_expect(self.pg0, [p], self.pg2)
3141
3142         self.assertFalse(find_gbp_endpoint(self, ip=rep_88.ip4))
3143
3144         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
3145              IP(src=ep.ip4, dst=rep_88.ip4) /
3146              UDP(sport=1234, dport=1234) /
3147              Raw(b'\xa5' * 100))
3148         rxs = self.send_and_expect(self.pg0, [p], self.pg4)
3149
3150         #
3151         # to appease the testcase we cannot have the registered EP still
3152         # present (because it's DP learnt) when the TC ends so wait until
3153         # it is removed
3154         #
3155         self.wait_for_ep_timeout(ip=rep_88.ip4)
3156         self.wait_for_ep_timeout(ip=rep_2.ip4)
3157
3158         #
3159         # Same as above, learn a remote EP via CP and DP
3160         # this time remove the DP one first. expect the CP data to remain
3161         #
3162         rep_3 = VppGbpEndpoint(self, vx_tun_l3,
3163                                epg_220, None,
3164                                "10.0.1.4", "11.0.0.103",
3165                                "2001::10:3", "3001::103",
3166                                ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
3167                                self.pg2.local_ip4,
3168                                self.pg2.remote_hosts[1].ip4,
3169                                mac=None)
3170         rep_3.add_vpp_config()
3171
3172         p = (Ether(src=self.pg2.remote_mac,
3173                    dst=self.pg2.local_mac) /
3174              IP(src=self.pg2.remote_hosts[2].ip4,
3175                 dst=self.pg2.local_ip4) /
3176              UDP(sport=1234, dport=48879) /
3177              VXLAN(vni=101, gpid=441, flags=0x88) /
3178              Ether(src=l['mac'], dst="00:00:00:11:11:11") /
3179              IP(src="10.0.1.4", dst=ep.ip4) /
3180              UDP(sport=1234, dport=1234) /
3181              Raw(b'\xa5' * 100))
3182         rxs = self.send_and_expect(self.pg2, p * NUM_PKTS, self.pg0)
3183
3184         self.assertTrue(find_gbp_endpoint(self,
3185                                           vx_tun_l3._sw_if_index,
3186                                           ip=rep_3.ip4,
3187                                           tep=[self.pg2.local_ip4,
3188                                                self.pg2.remote_hosts[2].ip4]))
3189
3190         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
3191              IP(dst="10.0.1.4", src=ep.ip4) /
3192              UDP(sport=1234, dport=1234) /
3193              Raw(b'\xa5' * 100))
3194         rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
3195
3196         # host 2 is the DP learned TEP
3197         for rx in rxs:
3198             self.assertEqual(rx[IP].src, self.pg2.local_ip4)
3199             self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[2].ip4)
3200
3201         self.wait_for_ep_timeout(ip=rep_3.ip4,
3202                                  tep=[self.pg2.local_ip4,
3203                                       self.pg2.remote_hosts[2].ip4])
3204
3205         rxs = self.send_and_expect(self.pg0, p * NUM_PKTS, self.pg2)
3206
3207         # host 1 is the CP learned TEP
3208         for rx in rxs:
3209             self.assertEqual(rx[IP].src, self.pg2.local_ip4)
3210             self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
3211
3212         #
3213         # shutdown with learnt endpoint present
3214         #
3215         p = (Ether(src=self.pg2.remote_mac,
3216                    dst=self.pg2.local_mac) /
3217              IP(src=self.pg2.remote_hosts[1].ip4,
3218                 dst=self.pg2.local_ip4) /
3219              UDP(sport=1234, dport=48879) /
3220              VXLAN(vni=101, gpid=441, flags=0x88) /
3221              Ether(src=l['mac'], dst="00:00:00:11:11:11") /
3222              IP(src=learnt[1]['ip'], dst=ep.ip4) /
3223              UDP(sport=1234, dport=1234) /
3224              Raw(b'\xa5' * 100))
3225
3226         rx = self.send_and_expect(self.pg2, [p], self.pg0)
3227
3228         # endpoint learnt via the parent GBP-vxlan interface
3229         self.assertTrue(find_gbp_endpoint(self,
3230                                           vx_tun_l3._sw_if_index,
3231                                           ip=l['ip']))
3232
3233         #
3234         # TODO
3235         # remote endpoint becomes local
3236         #
3237         self.pg2.unconfig_ip4()
3238         self.pg3.unconfig_ip4()
3239         self.pg4.unconfig_ip4()
3240
3241     def test_gbp_redirect(self):
3242         """ GBP Endpoint Redirect """
3243
3244         self.vapi.cli("set logging class gbp level debug")
3245
3246         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
3247         routed_dst_mac = "00:0c:0c:0c:0c:0c"
3248         routed_src_mac = "00:22:bd:f8:19:ff"
3249
3250         learnt = [{'mac': '00:00:11:11:11:02',
3251                    'ip': '10.0.1.2',
3252                    'ip6': '2001:10::2'},
3253                   {'mac': '00:00:11:11:11:03',
3254                    'ip': '10.0.1.3',
3255                    'ip6': '2001:10::3'}]
3256
3257         #
3258         # IP tables
3259         #
3260         t4 = VppIpTable(self, 1)
3261         t4.add_vpp_config()
3262         t6 = VppIpTable(self, 1, True)
3263         t6.add_vpp_config()
3264
3265         rd1 = VppGbpRouteDomain(self, 2, 402, t4, t6)
3266         rd1.add_vpp_config()
3267
3268         self.loop0.set_mac(self.router_mac)
3269
3270         #
3271         # Bind the BVI to the RD
3272         #
3273         VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
3274         VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
3275
3276         #
3277         # Pg7 hosts a BD's UU-fwd
3278         #
3279         self.pg7.config_ip4()
3280         self.pg7.resolve_arp()
3281
3282         #
3283         # a GBP bridge domains for the EPs
3284         #
3285         bd1 = VppBridgeDomain(self, 1)
3286         bd1.add_vpp_config()
3287         gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0)
3288         gbd1.add_vpp_config()
3289
3290         bd2 = VppBridgeDomain(self, 2)
3291         bd2.add_vpp_config()
3292         gbd2 = VppGbpBridgeDomain(self, bd2, rd1, self.loop1)
3293         gbd2.add_vpp_config()
3294
3295         # ... and has a /32 and /128 applied
3296         ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
3297         ip4_addr.add_vpp_config()
3298         ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 128)
3299         ip6_addr.add_vpp_config()
3300         ip4_addr = VppIpInterfaceAddress(self, gbd2.bvi, "10.0.1.128", 32)
3301         ip4_addr.add_vpp_config()
3302         ip6_addr = VppIpInterfaceAddress(self, gbd2.bvi, "2001:11::128", 128)
3303         ip6_addr.add_vpp_config()
3304
3305         #
3306         # The Endpoint-groups in which we are learning endpoints
3307         #
3308         epg_220 = VppGbpEndpointGroup(self, 220, 440, rd1, gbd1,
3309                                       None, gbd1.bvi,
3310                                       "10.0.0.128",
3311                                       "2001:10::128",
3312                                       VppGbpEndpointRetention(2))
3313         epg_220.add_vpp_config()
3314         epg_221 = VppGbpEndpointGroup(self, 221, 441, rd1, gbd2,
3315                                       None, gbd2.bvi,
3316                                       "10.0.1.128",
3317                                       "2001:11::128",
3318                                       VppGbpEndpointRetention(2))
3319         epg_221.add_vpp_config()
3320         epg_222 = VppGbpEndpointGroup(self, 222, 442, rd1, gbd1,
3321                                       None, gbd1.bvi,
3322                                       "10.0.2.128",
3323                                       "2001:12::128",
3324                                       VppGbpEndpointRetention(2))
3325         epg_222.add_vpp_config()
3326
3327         #
3328         # a GBP bridge domains for the SEPs
3329         #
3330         bd_uu1 = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
3331                                    self.pg7.remote_ip4, 116)
3332         bd_uu1.add_vpp_config()
3333         bd_uu2 = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
3334                                    self.pg7.remote_ip4, 117)
3335         bd_uu2.add_vpp_config()
3336
3337         bd3 = VppBridgeDomain(self, 3)
3338         bd3.add_vpp_config()
3339         gbd3 = VppGbpBridgeDomain(self, bd3, rd1, self.loop2,
3340                                   bd_uu1, learn=False)
3341         gbd3.add_vpp_config()
3342         bd4 = VppBridgeDomain(self, 4)
3343         bd4.add_vpp_config()
3344         gbd4 = VppGbpBridgeDomain(self, bd4, rd1, self.loop3,
3345                                   bd_uu2, learn=False)
3346         gbd4.add_vpp_config()
3347
3348         #
3349         # EPGs in which the service endpoints exist
3350         #
3351         epg_320 = VppGbpEndpointGroup(self, 320, 550, rd1, gbd3,
3352                                       None, gbd1.bvi,
3353                                       "12.0.0.128",
3354                                       "4001:10::128",
3355                                       VppGbpEndpointRetention(2))
3356         epg_320.add_vpp_config()
3357         epg_321 = VppGbpEndpointGroup(self, 321, 551, rd1, gbd4,
3358                                       None, gbd2.bvi,
3359                                       "12.0.1.128",
3360                                       "4001:11::128",
3361                                       VppGbpEndpointRetention(2))
3362         epg_321.add_vpp_config()
3363
3364         #
3365         # three local endpoints
3366         #
3367         ep1 = VppGbpEndpoint(self, self.pg0,
3368                              epg_220, None,
3369                              "10.0.0.1", "11.0.0.1",
3370                              "2001:10::1", "3001:10::1")
3371         ep1.add_vpp_config()
3372         ep2 = VppGbpEndpoint(self, self.pg1,
3373                              epg_221, None,
3374                              "10.0.1.1", "11.0.1.1",
3375                              "2001:11::1", "3001:11::1")
3376         ep2.add_vpp_config()
3377         ep3 = VppGbpEndpoint(self, self.pg2,
3378                              epg_222, None,
3379                              "10.0.2.2", "11.0.2.2",
3380                              "2001:12::1", "3001:12::1")
3381         ep3.add_vpp_config()
3382
3383         #
3384         # service endpoints
3385         #
3386         sep1 = VppGbpEndpoint(self, self.pg3,
3387                               epg_320, None,
3388                               "12.0.0.1", "13.0.0.1",
3389                               "4001:10::1", "5001:10::1")
3390         sep1.add_vpp_config()
3391         sep2 = VppGbpEndpoint(self, self.pg4,
3392                               epg_320, None,
3393                               "12.0.0.2", "13.0.0.2",
3394                               "4001:10::2", "5001:10::2")
3395         sep2.add_vpp_config()
3396         sep3 = VppGbpEndpoint(self, self.pg5,
3397                               epg_321, None,
3398                               "12.0.1.1", "13.0.1.1",
3399                               "4001:11::1", "5001:11::1")
3400         sep3.add_vpp_config()
3401         # this EP is not installed immediately
3402         sep4 = VppGbpEndpoint(self, self.pg6,
3403                               epg_321, None,
3404                               "12.0.1.2", "13.0.1.2",
3405                               "4001:11::2", "5001:11::2")
3406
3407         #
3408         # an L2 switch packet between local EPs in different EPGs
3409         #  different dest ports on each so the are LB hashed differently
3410         #
3411         p4 = [(Ether(src=ep1.mac, dst=ep3.mac) /
3412                IP(src=ep1.ip4, dst=ep3.ip4) /
3413                UDP(sport=1234, dport=1234) /
3414                Raw(b'\xa5' * 100)),
3415               (Ether(src=ep3.mac, dst=ep1.mac) /
3416                IP(src=ep3.ip4, dst=ep1.ip4) /
3417                UDP(sport=1234, dport=1234) /
3418                Raw(b'\xa5' * 100))]
3419         p6 = [(Ether(src=ep1.mac, dst=ep3.mac) /
3420                IPv6(src=ep1.ip6, dst=ep3.ip6) /
3421                UDP(sport=1234, dport=1234) /
3422                Raw(b'\xa5' * 100)),
3423               (Ether(src=ep3.mac, dst=ep1.mac) /
3424                IPv6(src=ep3.ip6, dst=ep1.ip6) /
3425                UDP(sport=1234, dport=1230) /
3426                Raw(b'\xa5' * 100))]
3427
3428         # should be dropped since no contract yet
3429         self.send_and_assert_no_replies(self.pg0, [p4[0]])
3430         self.send_and_assert_no_replies(self.pg0, [p6[0]])
3431
3432         #
3433         # Add a contract with a rule to load-balance redirect via SEP1 and SEP2
3434         # one of the next-hops is via an EP that is not known
3435         #
3436         rule4 = AclRule(is_permit=1, proto=17)
3437         rule6 = AclRule(src_prefix=IPv6Network((0, 0)),
3438                         dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17)
3439         acl = VppAcl(self, rules=[rule4, rule6])
3440         acl.add_vpp_config()
3441
3442         #
3443         # test the src-ip hash mode
3444         #
3445         c1 = VppGbpContract(
3446             self, 402, epg_220.sclass, epg_222.sclass, acl.acl_index,
3447             [VppGbpContractRule(
3448                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3449                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
3450                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
3451                                        sep1.ip4, sep1.epg.rd),
3452                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
3453                                        sep2.ip4, sep2.epg.rd)]),
3454                 VppGbpContractRule(
3455                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3456                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
3457                     [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
3458                                            sep3.ip6, sep3.epg.rd),
3459                      VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
3460                                            sep4.ip6, sep4.epg.rd)])],
3461             [ETH_P_IP, ETH_P_IPV6])
3462         c1.add_vpp_config()
3463
3464         c2 = VppGbpContract(
3465             self, 402, epg_222.sclass, epg_220.sclass, acl.acl_index,
3466             [VppGbpContractRule(
3467                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3468                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
3469                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
3470                                        sep1.ip4, sep1.epg.rd),
3471                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
3472                                        sep2.ip4, sep2.epg.rd)]),
3473                 VppGbpContractRule(
3474                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3475                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
3476                     [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
3477                                            sep3.ip6, sep3.epg.rd),
3478                      VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
3479                                            sep4.ip6, sep4.epg.rd)])],
3480             [ETH_P_IP, ETH_P_IPV6])
3481         c2.add_vpp_config()
3482
3483         #
3484         # send again with the contract preset, now packets arrive
3485         # at SEP1 or SEP2 depending on the hashing
3486         #
3487         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
3488
3489         for rx in rxs:
3490             self.assertEqual(rx[Ether].src, routed_src_mac)
3491             self.assertEqual(rx[Ether].dst, sep1.mac)
3492             self.assertEqual(rx[IP].src, ep1.ip4)
3493             self.assertEqual(rx[IP].dst, ep3.ip4)
3494
3495         rxs = self.send_and_expect(self.pg2, p4[1] * 17, sep2.itf)
3496
3497         for rx in rxs:
3498             self.assertEqual(rx[Ether].src, routed_src_mac)
3499             self.assertEqual(rx[Ether].dst, sep2.mac)
3500             self.assertEqual(rx[IP].src, ep3.ip4)
3501             self.assertEqual(rx[IP].dst, ep1.ip4)
3502
3503         rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7)
3504
3505         for rx in rxs:
3506             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
3507             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
3508             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
3509             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
3510             self.assertEqual(rx[VXLAN].vni, 117)
3511             self.assertTrue(rx[VXLAN].flags.G)
3512             self.assertTrue(rx[VXLAN].flags.Instance)
3513             # redirect policy has been applied
3514             self.assertTrue(rx[VXLAN].gpflags.A)
3515             self.assertFalse(rx[VXLAN].gpflags.D)
3516
3517             inner = rx[VXLAN].payload
3518
3519             self.assertEqual(inner[Ether].src, routed_src_mac)
3520             self.assertEqual(inner[Ether].dst, sep4.mac)
3521             self.assertEqual(inner[IPv6].src, ep1.ip6)
3522             self.assertEqual(inner[IPv6].dst, ep3.ip6)
3523
3524         rxs = self.send_and_expect(self.pg2, p6[1] * 17, sep3.itf)
3525
3526         for rx in rxs:
3527             self.assertEqual(rx[Ether].src, routed_src_mac)
3528             self.assertEqual(rx[Ether].dst, sep3.mac)
3529             self.assertEqual(rx[IPv6].src, ep3.ip6)
3530             self.assertEqual(rx[IPv6].dst, ep1.ip6)
3531
3532         #
3533         # programme the unknown EP
3534         #
3535         sep4.add_vpp_config()
3536
3537         rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf)
3538
3539         for rx in rxs:
3540             self.assertEqual(rx[Ether].src, routed_src_mac)
3541             self.assertEqual(rx[Ether].dst, sep4.mac)
3542             self.assertEqual(rx[IPv6].src, ep1.ip6)
3543             self.assertEqual(rx[IPv6].dst, ep3.ip6)
3544
3545         #
3546         # and revert back to unprogrammed
3547         #
3548         sep4.remove_vpp_config()
3549
3550         rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7)
3551
3552         for rx in rxs:
3553             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
3554             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
3555             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
3556             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
3557             self.assertEqual(rx[VXLAN].vni, 117)
3558             self.assertTrue(rx[VXLAN].flags.G)
3559             self.assertTrue(rx[VXLAN].flags.Instance)
3560             # redirect policy has been applied
3561             self.assertTrue(rx[VXLAN].gpflags.A)
3562             self.assertFalse(rx[VXLAN].gpflags.D)
3563
3564             inner = rx[VXLAN].payload
3565
3566             self.assertEqual(inner[Ether].src, routed_src_mac)
3567             self.assertEqual(inner[Ether].dst, sep4.mac)
3568             self.assertEqual(inner[IPv6].src, ep1.ip6)
3569             self.assertEqual(inner[IPv6].dst, ep3.ip6)
3570
3571         c1.remove_vpp_config()
3572         c2.remove_vpp_config()
3573
3574         #
3575         # test the symmetric hash mode
3576         #
3577         c1 = VppGbpContract(
3578             self, 402, epg_220.sclass, epg_222.sclass, acl.acl_index,
3579             [VppGbpContractRule(
3580                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3581                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
3582                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
3583                                        sep1.ip4, sep1.epg.rd),
3584                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
3585                                        sep2.ip4, sep2.epg.rd)]),
3586                 VppGbpContractRule(
3587                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3588                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
3589                     [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
3590                                            sep3.ip6, sep3.epg.rd),
3591                      VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
3592                                            sep4.ip6, sep4.epg.rd)])],
3593             [ETH_P_IP, ETH_P_IPV6])
3594         c1.add_vpp_config()
3595
3596         c2 = VppGbpContract(
3597             self, 402, epg_222.sclass, epg_220.sclass, acl.acl_index,
3598             [VppGbpContractRule(
3599                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3600                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
3601                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
3602                                        sep1.ip4, sep1.epg.rd),
3603                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
3604                                        sep2.ip4, sep2.epg.rd)]),
3605                 VppGbpContractRule(
3606                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3607                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
3608                     [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
3609                                            sep3.ip6, sep3.epg.rd),
3610                      VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
3611                                            sep4.ip6, sep4.epg.rd)])],
3612             [ETH_P_IP, ETH_P_IPV6])
3613         c2.add_vpp_config()
3614
3615         #
3616         # send again with the contract preset, now packets arrive
3617         # at SEP1 for both directions
3618         #
3619         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
3620
3621         for rx in rxs:
3622             self.assertEqual(rx[Ether].src, routed_src_mac)
3623             self.assertEqual(rx[Ether].dst, sep1.mac)
3624             self.assertEqual(rx[IP].src, ep1.ip4)
3625             self.assertEqual(rx[IP].dst, ep3.ip4)
3626
3627         rxs = self.send_and_expect(self.pg2, p4[1] * 17, sep1.itf)
3628
3629         for rx in rxs:
3630             self.assertEqual(rx[Ether].src, routed_src_mac)
3631             self.assertEqual(rx[Ether].dst, sep1.mac)
3632             self.assertEqual(rx[IP].src, ep3.ip4)
3633             self.assertEqual(rx[IP].dst, ep1.ip4)
3634
3635         #
3636         # programme the unknown EP for the L3 tests
3637         #
3638         sep4.add_vpp_config()
3639
3640         #
3641         # an L3 switch packet between local EPs in different EPGs
3642         #  different dest ports on each so the are LB hashed differently
3643         #
3644         p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
3645                IP(src=ep1.ip4, dst=ep2.ip4) /
3646                UDP(sport=1234, dport=1234) /
3647                Raw(b'\xa5' * 100)),
3648               (Ether(src=ep2.mac, dst=str(self.router_mac)) /
3649                IP(src=ep2.ip4, dst=ep1.ip4) /
3650                UDP(sport=1234, dport=1234) /
3651                Raw(b'\xa5' * 100))]
3652         p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
3653                IPv6(src=ep1.ip6, dst=ep2.ip6) /
3654                UDP(sport=1234, dport=1234) /
3655                Raw(b'\xa5' * 100)),
3656               (Ether(src=ep2.mac, dst=str(self.router_mac)) /
3657                IPv6(src=ep2.ip6, dst=ep1.ip6) /
3658                UDP(sport=1234, dport=1234) /
3659                Raw(b'\xa5' * 100))]
3660
3661         c3 = VppGbpContract(
3662             self, 402, epg_220.sclass, epg_221.sclass, acl.acl_index,
3663             [VppGbpContractRule(
3664                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3665                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
3666                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
3667                                        sep1.ip4, sep1.epg.rd),
3668                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
3669                                        sep2.ip4, sep2.epg.rd)]),
3670                 VppGbpContractRule(
3671                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3672                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
3673                     [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
3674                                            sep3.ip6, sep3.epg.rd),
3675                      VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
3676                                            sep4.ip6, sep4.epg.rd)])],
3677             [ETH_P_IP, ETH_P_IPV6])
3678         c3.add_vpp_config()
3679
3680         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
3681
3682         for rx in rxs:
3683             self.assertEqual(rx[Ether].src, routed_src_mac)
3684             self.assertEqual(rx[Ether].dst, sep1.mac)
3685             self.assertEqual(rx[IP].src, ep1.ip4)
3686             self.assertEqual(rx[IP].dst, ep2.ip4)
3687
3688         #
3689         # learn a remote EP in EPG 221
3690         #   packets coming from unknown remote EPs will be leant & redirected
3691         #
3692         vx_tun_l3 = VppGbpVxlanTunnel(
3693             self, 444, rd1.rd_id,
3694             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
3695             self.pg2.local_ip4)
3696         vx_tun_l3.add_vpp_config()
3697
3698         c4 = VppGbpContract(
3699             self, 402, epg_221.sclass, epg_220.sclass, acl.acl_index,
3700             [VppGbpContractRule(
3701                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3702                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
3703                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
3704                                        sep1.ip4, sep1.epg.rd),
3705                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
3706                                        sep2.ip4, sep2.epg.rd)]),
3707                 VppGbpContractRule(
3708                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3709                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
3710                     [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
3711                                            sep3.ip6, sep3.epg.rd),
3712                      VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
3713                                            sep4.ip6, sep4.epg.rd)])],
3714             [ETH_P_IP, ETH_P_IPV6])
3715         c4.add_vpp_config()
3716
3717         p = (Ether(src=self.pg7.remote_mac,
3718                    dst=self.pg7.local_mac) /
3719              IP(src=self.pg7.remote_ip4,
3720                 dst=self.pg7.local_ip4) /
3721              UDP(sport=1234, dport=48879) /
3722              VXLAN(vni=444, gpid=441, flags=0x88) /
3723              Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) /
3724              IP(src="10.0.0.88", dst=ep1.ip4) /
3725              UDP(sport=1234, dport=1234) /
3726              Raw(b'\xa5' * 100))
3727
3728         # unknown remote EP to local EP redirected
3729         rxs = self.send_and_expect(self.pg7, [p], sep1.itf)
3730
3731         for rx in rxs:
3732             self.assertEqual(rx[Ether].src, routed_src_mac)
3733             self.assertEqual(rx[Ether].dst, sep1.mac)
3734             self.assertEqual(rx[IP].src, "10.0.0.88")
3735             self.assertEqual(rx[IP].dst, ep1.ip4)
3736
3737         # endpoint learnt via the parent GBP-vxlan interface
3738         self.assertTrue(find_gbp_endpoint(self,
3739                                           vx_tun_l3._sw_if_index,
3740                                           ip="10.0.0.88"))
3741
3742         p = (Ether(src=self.pg7.remote_mac,
3743                    dst=self.pg7.local_mac) /
3744              IP(src=self.pg7.remote_ip4,
3745                 dst=self.pg7.local_ip4) /
3746              UDP(sport=1234, dport=48879) /
3747              VXLAN(vni=444, gpid=441, flags=0x88) /
3748              Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) /
3749              IPv6(src="2001:10::88", dst=ep1.ip6) /
3750              UDP(sport=1234, dport=1234) /
3751              Raw(b'\xa5' * 100))
3752
3753         # unknown remote EP to local EP redirected (ipv6)
3754         rxs = self.send_and_expect(self.pg7, [p], sep3.itf)
3755
3756         for rx in rxs:
3757             self.assertEqual(rx[Ether].src, routed_src_mac)
3758             self.assertEqual(rx[Ether].dst, sep3.mac)
3759             self.assertEqual(rx[IPv6].src, "2001:10::88")
3760             self.assertEqual(rx[IPv6].dst, ep1.ip6)
3761
3762         # endpoint learnt via the parent GBP-vxlan interface
3763         self.assertTrue(find_gbp_endpoint(self,
3764                                           vx_tun_l3._sw_if_index,
3765                                           ip="2001:10::88"))
3766
3767         #
3768         # L3 switch from local to remote EP
3769         #
3770         p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
3771                IP(src=ep1.ip4, dst="10.0.0.88") /
3772                UDP(sport=1234, dport=1234) /
3773                Raw(b'\xa5' * 100))]
3774         p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
3775                IPv6(src=ep1.ip6, dst="2001:10::88") /
3776                UDP(sport=1234, dport=1234) /
3777                Raw(b'\xa5' * 100))]
3778
3779         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
3780
3781         for rx in rxs:
3782             self.assertEqual(rx[Ether].src, routed_src_mac)
3783             self.assertEqual(rx[Ether].dst, sep1.mac)
3784             self.assertEqual(rx[IP].src, ep1.ip4)
3785             self.assertEqual(rx[IP].dst, "10.0.0.88")
3786
3787         rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf)
3788
3789         for rx in rxs:
3790             self.assertEqual(rx[Ether].src, routed_src_mac)
3791             self.assertEqual(rx[Ether].dst, sep4.mac)
3792             self.assertEqual(rx[IPv6].src, ep1.ip6)
3793             self.assertEqual(rx[IPv6].dst, "2001:10::88")
3794
3795         #
3796         # test the dst-ip hash mode
3797         #
3798         c5 = VppGbpContract(
3799             self, 402, epg_220.sclass, epg_221.sclass, acl.acl_index,
3800             [VppGbpContractRule(
3801                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3802                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
3803                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
3804                                        sep1.ip4, sep1.epg.rd),
3805                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
3806                                        sep2.ip4, sep2.epg.rd)]),
3807                 VppGbpContractRule(
3808                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3809                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
3810                     [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
3811                                            sep3.ip6, sep3.epg.rd),
3812                      VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
3813                                            sep4.ip6, sep4.epg.rd)])],
3814             [ETH_P_IP, ETH_P_IPV6])
3815         c5.add_vpp_config()
3816
3817         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
3818
3819         for rx in rxs:
3820             self.assertEqual(rx[Ether].src, routed_src_mac)
3821             self.assertEqual(rx[Ether].dst, sep1.mac)
3822             self.assertEqual(rx[IP].src, ep1.ip4)
3823             self.assertEqual(rx[IP].dst, "10.0.0.88")
3824
3825         rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep3.itf)
3826
3827         for rx in rxs:
3828             self.assertEqual(rx[Ether].src, routed_src_mac)
3829             self.assertEqual(rx[Ether].dst, sep3.mac)
3830             self.assertEqual(rx[IPv6].src, ep1.ip6)
3831             self.assertEqual(rx[IPv6].dst, "2001:10::88")
3832
3833         #
3834         # a programmed remote SEP in EPG 320
3835         #
3836
3837         # gbp vxlan tunnel for the remote SEP
3838         vx_tun_l3_sep = VppGbpVxlanTunnel(
3839             self, 555, rd1.rd_id,
3840             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
3841             self.pg2.local_ip4)
3842         vx_tun_l3_sep.add_vpp_config()
3843
3844         # remote SEP
3845         sep5 = VppGbpEndpoint(self, vx_tun_l3_sep,
3846                               epg_320, None,
3847                               "12.0.0.10", "13.0.0.10",
3848                               "4001:10::10", "5001:10::10",
3849                               ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
3850                               self.pg7.local_ip4,
3851                               self.pg7.remote_ip4,
3852                               mac=None)
3853         sep5.add_vpp_config()
3854
3855         #
3856         # local l3out redirect tests
3857         #
3858
3859         # add local l3out
3860         # the external bd
3861         self.loop4.set_mac(self.router_mac)
3862         VppIpInterfaceBind(self, self.loop4, t4).add_vpp_config()
3863         VppIpInterfaceBind(self, self.loop4, t6).add_vpp_config()
3864         ebd = VppBridgeDomain(self, 100)
3865         ebd.add_vpp_config()
3866         gebd = VppGbpBridgeDomain(self, ebd, rd1, self.loop4, None, None)
3867         gebd.add_vpp_config()
3868         # the external epg
3869         eepg = VppGbpEndpointGroup(self, 888, 765, rd1, gebd,
3870                                    None, gebd.bvi,
3871                                    "10.1.0.128",
3872                                    "2001:10:1::128",
3873                                    VppGbpEndpointRetention(2))
3874         eepg.add_vpp_config()
3875         # add subnets to BVI
3876         VppIpInterfaceAddress(
3877             self,
3878             gebd.bvi,
3879             "10.1.0.128",
3880             24).add_vpp_config()
3881         VppIpInterfaceAddress(
3882             self,
3883             gebd.bvi,
3884             "2001:10:1::128",
3885             64).add_vpp_config()
3886         # ... which are L3-out subnets
3887         VppGbpSubnet(self, rd1, "10.1.0.0", 24,
3888                      VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
3889                      sclass=765).add_vpp_config()
3890         VppGbpSubnet(self, rd1, "2001:10:1::128", 64,
3891                      VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
3892                      sclass=765).add_vpp_config()
3893         # external endpoints
3894         VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
3895         eep1 = VppGbpEndpoint(self, self.vlan_100, eepg, None, "10.1.0.1",
3896                               "11.1.0.1", "2001:10:1::1", "3001:10:1::1",
3897                               ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
3898         eep1.add_vpp_config()
3899         VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
3900         eep2 = VppGbpEndpoint(self, self.vlan_101, eepg, None, "10.1.0.2",
3901                               "11.1.0.2", "2001:10:1::2", "3001:10:1::2",
3902                               ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
3903         eep2.add_vpp_config()
3904
3905         # external subnets reachable though eep1 and eep2 respectively
3906         VppIpRoute(self, "10.220.0.0", 24,
3907                    [VppRoutePath(eep1.ip4, eep1.epg.bvi.sw_if_index)],
3908                    table_id=t4.table_id).add_vpp_config()
3909         VppGbpSubnet(self, rd1, "10.220.0.0", 24,
3910                      VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
3911                      sclass=4220).add_vpp_config()
3912         VppIpRoute(self, "10:220::", 64,
3913                    [VppRoutePath(eep1.ip6, eep1.epg.bvi.sw_if_index)],
3914                    table_id=t6.table_id).add_vpp_config()
3915         VppGbpSubnet(self, rd1, "10:220::", 64,
3916                      VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
3917                      sclass=4220).add_vpp_config()
3918         VppIpRoute(self, "10.221.0.0", 24,
3919                    [VppRoutePath(eep2.ip4, eep2.epg.bvi.sw_if_index)],
3920                    table_id=t4.table_id).add_vpp_config()
3921         VppGbpSubnet(self, rd1, "10.221.0.0", 24,
3922                      VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
3923                      sclass=4221).add_vpp_config()
3924         VppIpRoute(self, "10:221::", 64,
3925                    [VppRoutePath(eep2.ip6, eep2.epg.bvi.sw_if_index)],
3926                    table_id=t6.table_id).add_vpp_config()
3927         VppGbpSubnet(self, rd1, "10:221::", 64,
3928                      VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
3929                      sclass=4221).add_vpp_config()
3930
3931         #
3932         # l3out redirect to remote (known, then unknown) SEP
3933         #
3934
3935         # packets from 1 external subnet to the other
3936         p = [(Ether(src=eep1.mac, dst=self.router_mac) /
3937               Dot1Q(vlan=100) /
3938               IP(src="10.220.0.17", dst="10.221.0.65") /
3939               UDP(sport=1234, dport=1234) /
3940               Raw(b'\xa5' * 100)),
3941              (Ether(src=eep1.mac, dst=self.router_mac) /
3942               Dot1Q(vlan=100) /
3943               IPv6(src="10:220::17", dst="10:221::65") /
3944               UDP(sport=1234, dport=1234) /
3945               Raw(b'\xa5' * 100))]
3946
3947         # packets should be dropped in absence of contract
3948         self.send_and_assert_no_replies(self.pg0, p)
3949
3950         # contract redirecting to sep5
3951         VppGbpContract(
3952             self, 402, 4220, 4221, acl.acl_index,
3953             [VppGbpContractRule(
3954                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3955                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
3956                 [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd,
3957                                        sep5.ip4, sep5.epg.rd)]),
3958                 VppGbpContractRule(
3959                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3960                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
3961                     [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd,
3962                                            sep5.ip6, sep5.epg.rd)])],
3963             [ETH_P_IP, ETH_P_IPV6]).add_vpp_config()
3964
3965         rxs = self.send_and_expect(self.pg0, p, self.pg7)
3966
3967         for rx, tx in zip(rxs, p):
3968             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
3969             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
3970             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
3971             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
3972             # this should use the programmed remote leaf TEP
3973             self.assertEqual(rx[VXLAN].vni, 555)
3974             self.assertEqual(rx[VXLAN].gpid, 4220)
3975             self.assertTrue(rx[VXLAN].flags.G)
3976             self.assertTrue(rx[VXLAN].flags.Instance)
3977             # redirect policy has been applied
3978             self.assertTrue(rx[VXLAN].gpflags.A)
3979             self.assertTrue(rx[VXLAN].gpflags.D)
3980             rxip = rx[VXLAN][Ether].payload
3981             txip = tx[Dot1Q].payload
3982             self.assertEqual(rxip.src, txip.src)
3983             self.assertEqual(rxip.dst, txip.dst)
3984
3985         # remote SEP: it is now an unknown remote SEP and should go
3986         # to spine proxy
3987         sep5.remove_vpp_config()
3988
3989         rxs = self.send_and_expect(self.pg0, p, self.pg7)
3990
3991         for rx, tx in zip(rxs, p):
3992             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
3993             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
3994             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
3995             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
3996             # this should use the spine proxy TEP
3997             self.assertEqual(rx[VXLAN].vni, epg_320.bd.uu_fwd.vni)
3998             self.assertEqual(rx[VXLAN].gpid, 4220)
3999             self.assertTrue(rx[VXLAN].flags.G)
4000             self.assertTrue(rx[VXLAN].flags.Instance)
4001             # redirect policy has been applied
4002             self.assertTrue(rx[VXLAN].gpflags.A)
4003             self.assertTrue(rx[VXLAN].gpflags.D)
4004             rxip = rx[VXLAN][Ether].payload
4005             txip = tx[Dot1Q].payload
4006             self.assertEqual(rxip.src, txip.src)
4007             self.assertEqual(rxip.dst, txip.dst)
4008
4009         #
4010         # l3out redirect to local SEP
4011         #
4012
4013         # change the contract between l3out to redirect to local SEPs
4014         # instead of remote SEP
4015         VppGbpContract(
4016             self, 402, 4220, 4221, acl.acl_index,
4017             [VppGbpContractRule(
4018                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
4019                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
4020                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
4021                                        sep1.ip4, sep1.epg.rd)]),
4022                 VppGbpContractRule(
4023                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
4024                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
4025                     [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
4026                                            sep1.ip6, sep1.epg.rd)])],
4027             [ETH_P_IP, ETH_P_IPV6]).add_vpp_config()
4028
4029         rxs = self.send_and_expect(self.pg0, p, sep1.itf)
4030         for rx, tx in zip(rxs, p):
4031             self.assertEqual(rx[Ether].src, routed_src_mac)
4032             self.assertEqual(rx[Ether].dst, sep1.mac)
4033             rxip = rx[Ether].payload
4034             txip = tx[Ether].payload
4035             self.assertEqual(rxip.src, txip.src)
4036             self.assertEqual(rxip.dst, txip.dst)
4037
4038         #
4039         # redirect remote EP to remote (known then unknown) SEP
4040         #
4041
4042         # remote SEP known again
4043         sep5.add_vpp_config()
4044
4045         # contract to redirect to learnt SEP
4046         VppGbpContract(
4047             self, 402, epg_221.sclass, epg_222.sclass, acl.acl_index,
4048             [VppGbpContractRule(
4049                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
4050                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
4051                 [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd,
4052                                        sep5.ip4, sep5.epg.rd)]),
4053                 VppGbpContractRule(
4054                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
4055                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
4056                     [VppGbpContractNextHop(sep5.vmac, sep5.epg.bd,
4057                                            sep5.ip6, sep5.epg.rd)])],
4058             [ETH_P_IP, ETH_P_IPV6]).add_vpp_config()
4059
4060         # packets from unknown EP 221 to known EP in EPG 222
4061         # should be redirected to known remote SEP
4062         base = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) /
4063                 IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) /
4064                 UDP(sport=1234, dport=48879) /
4065                 VXLAN(vni=444, gpid=441, flags=0x88) /
4066                 Ether(src="00:22:22:22:22:44", dst=str(self.router_mac)))
4067         p = [(base /
4068               IP(src="10.0.1.100", dst=ep3.ip4) /
4069               UDP(sport=1234, dport=1234) /
4070               Raw(b'\xa5' * 100)),
4071              (base /
4072               IPv6(src="2001:10::100", dst=ep3.ip6) /
4073               UDP(sport=1234, dport=1234) /
4074               Raw(b'\xa5' * 100))]
4075
4076         # unknown remote EP to local EP redirected to known remote SEP
4077         rxs = self.send_and_expect(self.pg7, p, self.pg7)
4078
4079         for rx, tx in zip(rxs, p):
4080             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
4081             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
4082             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
4083             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
4084             # this should use the programmed remote leaf TEP
4085             self.assertEqual(rx[VXLAN].vni, 555)
4086             self.assertEqual(rx[VXLAN].gpid, epg_221.sclass)
4087             self.assertTrue(rx[VXLAN].flags.G)
4088             self.assertTrue(rx[VXLAN].flags.Instance)
4089             # redirect policy has been applied
4090             self.assertTrue(rx[VXLAN].gpflags.A)
4091             self.assertFalse(rx[VXLAN].gpflags.D)
4092             rxip = rx[VXLAN][Ether].payload
4093             txip = tx[VXLAN][Ether].payload
4094             self.assertEqual(rxip.src, txip.src)
4095             self.assertEqual(rxip.dst, txip.dst)
4096
4097         # endpoint learnt via the parent GBP-vxlan interface
4098         self.assertTrue(find_gbp_endpoint(self,
4099                                           vx_tun_l3._sw_if_index,
4100                                           ip="10.0.1.100"))
4101         self.assertTrue(find_gbp_endpoint(self,
4102                                           vx_tun_l3._sw_if_index,
4103                                           ip="2001:10::100"))
4104
4105         # remote SEP: it is now an unknown remote SEP and should go
4106         # to spine proxy
4107         sep5.remove_vpp_config()
4108
4109         # remote EP (coming from spine proxy) to local EP redirected to
4110         # known remote SEP
4111         rxs = self.send_and_expect(self.pg7, p, self.pg7)
4112
4113         for rx, tx in zip(rxs, p):
4114             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
4115             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
4116             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
4117             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
4118             # this should use the spine proxy TEP
4119             self.assertEqual(rx[VXLAN].vni, epg_320.bd.uu_fwd.vni)
4120             self.assertEqual(rx[VXLAN].gpid, epg_221.sclass)
4121             self.assertTrue(rx[VXLAN].flags.G)
4122             self.assertTrue(rx[VXLAN].flags.Instance)
4123             # redirect policy has been applied
4124             self.assertTrue(rx[VXLAN].gpflags.A)
4125             self.assertFalse(rx[VXLAN].gpflags.D)
4126             rxip = rx[VXLAN][Ether].payload
4127             txip = tx[VXLAN][Ether].payload
4128             self.assertEqual(rxip.src, txip.src)
4129             self.assertEqual(rxip.dst, txip.dst)
4130
4131         #
4132         # cleanup
4133         #
4134         self.pg7.unconfig_ip4()
4135
4136     def test_gbp_redirect_extended(self):
4137         """ GBP Endpoint Redirect Extended """
4138
4139         self.vapi.cli("set logging class gbp level debug")
4140
4141         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
4142         routed_dst_mac = "00:0c:0c:0c:0c:0c"
4143         routed_src_mac = "00:22:bd:f8:19:ff"
4144
4145         learnt = [{'mac': '00:00:11:11:11:02',
4146                    'ip': '10.0.1.2',
4147                    'ip6': '2001:10::2'},
4148                   {'mac': '00:00:11:11:11:03',
4149                    'ip': '10.0.1.3',
4150                    'ip6': '2001:10::3'}]
4151
4152         #
4153         # IP tables
4154         #
4155         t4 = VppIpTable(self, 1)
4156         t4.add_vpp_config()
4157         t6 = VppIpTable(self, 1, True)
4158         t6.add_vpp_config()
4159
4160         # create IPv4 and IPv6 RD UU VxLAN-GBP TEP and bind them to the right
4161         # VRF
4162         rd_uu4 = VppVxlanGbpTunnel(
4163             self,
4164             self.pg7.local_ip4,
4165             self.pg7.remote_ip4,
4166             114,
4167             mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.
4168                   VXLAN_GBP_API_TUNNEL_MODE_L3))
4169         rd_uu4.add_vpp_config()
4170         VppIpInterfaceBind(self, rd_uu4, t4).add_vpp_config()
4171
4172         rd_uu6 = VppVxlanGbpTunnel(
4173             self,
4174             self.pg7.local_ip4,
4175             self.pg7.remote_ip4,
4176             115,
4177             mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.
4178                   VXLAN_GBP_API_TUNNEL_MODE_L3))
4179         rd_uu6.add_vpp_config()
4180         VppIpInterfaceBind(self, rd_uu6, t4).add_vpp_config()
4181
4182         rd1 = VppGbpRouteDomain(self, 2, 402, t4, t6, rd_uu4, rd_uu6)
4183         rd1.add_vpp_config()
4184
4185         self.loop0.set_mac(self.router_mac)
4186         self.loop1.set_mac(self.router_mac)
4187         self.loop2.set_mac(self.router_mac)
4188
4189         #
4190         # Bind the BVI to the RD
4191         #
4192         VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
4193         VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
4194         VppIpInterfaceBind(self, self.loop1, t4).add_vpp_config()
4195         VppIpInterfaceBind(self, self.loop1, t6).add_vpp_config()
4196         VppIpInterfaceBind(self, self.loop2, t4).add_vpp_config()
4197         VppIpInterfaceBind(self, self.loop2, t6).add_vpp_config()
4198
4199         #
4200         # Pg7 hosts a BD's UU-fwd
4201         #
4202         self.pg7.config_ip4()
4203         self.pg7.resolve_arp()
4204
4205         #
4206         # a GBP bridge domains for the EPs
4207         #
4208         bd1 = VppBridgeDomain(self, 1)
4209         bd1.add_vpp_config()
4210         gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0)
4211         gbd1.add_vpp_config()
4212
4213         bd2 = VppBridgeDomain(self, 2)
4214         bd2.add_vpp_config()
4215         gbd2 = VppGbpBridgeDomain(self, bd2, rd1, self.loop1)
4216         gbd2.add_vpp_config()
4217
4218         # ... and has a /32 and /128 applied
4219         ip4_addr1 = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
4220         ip4_addr1.add_vpp_config()
4221         ip6_addr1 = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 128)
4222         ip6_addr1.add_vpp_config()
4223         ip4_addr2 = VppIpInterfaceAddress(self, gbd2.bvi, "10.0.1.128", 32)
4224         ip4_addr2.add_vpp_config()
4225         ip6_addr2 = VppIpInterfaceAddress(self, gbd2.bvi, "2001:11::128", 128)
4226         ip6_addr2.add_vpp_config()
4227
4228         #
4229         # The Endpoint-groups
4230         #
4231         epg_220 = VppGbpEndpointGroup(self, 220, 440, rd1, gbd1,
4232                                       None, gbd1.bvi,
4233                                       "10.0.0.128",
4234                                       "2001:10::128",
4235                                       VppGbpEndpointRetention(2))
4236         epg_220.add_vpp_config()
4237         epg_221 = VppGbpEndpointGroup(self, 221, 441, rd1, gbd2,
4238                                       None, gbd2.bvi,
4239                                       "10.0.1.128",
4240                                       "2001:11::128",
4241                                       VppGbpEndpointRetention(2))
4242         epg_221.add_vpp_config()
4243
4244         #
4245         # a GBP bridge domains for the SEPs
4246         #
4247         bd_uu3 = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
4248                                    self.pg7.remote_ip4, 116)
4249         bd_uu3.add_vpp_config()
4250
4251         bd3 = VppBridgeDomain(self, 3)
4252         bd3.add_vpp_config()
4253         gbd3 = VppGbpBridgeDomain(self, bd3, rd1, self.loop2,
4254                                   bd_uu3, learn=False)
4255         gbd3.add_vpp_config()
4256
4257         ip4_addr3 = VppIpInterfaceAddress(self, gbd3.bvi, "12.0.0.128", 32)
4258         ip4_addr3.add_vpp_config()
4259         ip6_addr3 = VppIpInterfaceAddress(self, gbd3.bvi, "4001:10::128", 128)
4260         ip6_addr3.add_vpp_config()
4261
4262         #
4263         # self.logger.info(self.vapi.cli("show gbp bridge"))
4264         # self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
4265         # self.logger.info(self.vapi.cli("show gbp vxlan"))
4266         # self.logger.info(self.vapi.cli("show int addr"))
4267         #
4268
4269         #
4270         # EPGs in which the service endpoints exist
4271         #
4272         epg_320 = VppGbpEndpointGroup(self, 320, 550, rd1, gbd3,
4273                                       None, gbd3.bvi,
4274                                       "12.0.0.128",
4275                                       "4001:10::128",
4276                                       VppGbpEndpointRetention(2))
4277         epg_320.add_vpp_config()
4278
4279         #
4280         # endpoints
4281         #
4282         ep1 = VppGbpEndpoint(self, self.pg0,
4283                              epg_220, None,
4284                              "10.0.0.1", "11.0.0.1",
4285                              "2001:10::1", "3001:10::1")
4286         ep1.add_vpp_config()
4287         ep2 = VppGbpEndpoint(self, self.pg1,
4288                              epg_221, None,
4289                              "10.0.1.1", "11.0.1.1",
4290                              "2001:11::1", "3001:11::1")
4291         ep2.add_vpp_config()
4292
4293         #
4294         # service endpoints
4295         #
4296         sep1 = VppGbpEndpoint(self, self.pg3,
4297                               epg_320, None,
4298                               "12.0.0.1", "13.0.0.1",
4299                               "4001:10::1", "5001:10::1")
4300         sep2 = VppGbpEndpoint(self, self.pg4,
4301                               epg_320, None,
4302                               "12.0.0.2", "13.0.0.2",
4303                               "4001:10::2", "5001:10::2")
4304
4305         # sep1 and sep2 are not added to config yet
4306         # they are unknown for now
4307
4308         #
4309         # add routes to EPG subnets
4310         #
4311         VppGbpSubnet(self, rd1, "10.0.0.0", 24,
4312                      VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT
4313                      ).add_vpp_config()
4314         VppGbpSubnet(self, rd1, "10.0.1.0", 24,
4315                      VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT
4316                      ).add_vpp_config()
4317
4318         #
4319         # Local host to known local host in different BD
4320         # with SFC contract (source and destination are in
4321         # one node and service endpoint in another node)
4322         #
4323         p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
4324                IP(src=ep1.ip4, dst=ep2.ip4) /
4325                UDP(sport=1234, dport=1234) /
4326                Raw(b'\xa5' * 100)),
4327               (Ether(src=ep2.mac, dst=str(self.router_mac)) /
4328                IP(src=ep2.ip4, dst=ep1.ip4) /
4329                UDP(sport=1234, dport=1234) /
4330                Raw(b'\xa5' * 100))]
4331         p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
4332                IPv6(src=ep1.ip6, dst=ep2.ip6) /
4333                UDP(sport=1234, dport=1234) /
4334                Raw(b'\xa5' * 100)),
4335               (Ether(src=ep2.mac, dst=str(self.router_mac)) /
4336                IPv6(src=ep2.ip6, dst=ep1.ip6) /
4337                UDP(sport=1234, dport=1230) /
4338                Raw(b'\xa5' * 100))]
4339
4340         # should be dropped since no contract yet
4341         self.send_and_assert_no_replies(self.pg0, [p4[0]])
4342         self.send_and_assert_no_replies(self.pg0, [p6[0]])
4343
4344         #
4345         # Add a contract with a rule to load-balance redirect via SEP1 and SEP2
4346         # one of the next-hops is via an EP that is not known
4347         #
4348         rule4 = AclRule(is_permit=1, proto=17)
4349         rule6 = AclRule(src_prefix=IPv6Network((0, 0)),
4350                         dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17)
4351         acl = VppAcl(self, rules=[rule4, rule6])
4352         acl.add_vpp_config()
4353
4354         #
4355         # test the src-ip hash mode
4356         #
4357         c1 = VppGbpContract(
4358             self, 402, epg_220.sclass, epg_221.sclass, acl.acl_index,
4359             [VppGbpContractRule(
4360                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
4361                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
4362                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
4363                                        sep1.ip4, sep1.epg.rd)]),
4364              VppGbpContractRule(
4365                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
4366                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
4367                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
4368                                        sep1.ip6, sep1.epg.rd)])],
4369             [ETH_P_IP, ETH_P_IPV6])
4370         c1.add_vpp_config()
4371
4372         c2 = VppGbpContract(
4373             self, 402, epg_221.sclass, epg_220.sclass, acl.acl_index,
4374             [VppGbpContractRule(
4375                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
4376                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
4377                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
4378                                        sep1.ip4, sep1.epg.rd)]),
4379              VppGbpContractRule(
4380                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
4381                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
4382                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
4383                                        sep1.ip6, sep1.epg.rd)])],
4384             [ETH_P_IP, ETH_P_IPV6])
4385         c2.add_vpp_config()
4386
4387         # ep1 <--> ep2 redirected through sep1
4388         # sep1 is unknown
4389         # packet is redirected to sep bd and then go through sep bd UU
4390
4391         rxs = self.send_and_expect(self.pg0, p4[0] * 17, self.pg7)
4392
4393         for rx in rxs:
4394             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
4395             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
4396             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
4397             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
4398             self.assertEqual(rx[VXLAN].vni, 116)
4399             self.assertTrue(rx[VXLAN].flags.G)
4400             self.assertTrue(rx[VXLAN].flags.Instance)
4401             # redirect policy has been applied
4402             self.assertTrue(rx[VXLAN].gpflags.A)
4403             self.assertFalse(rx[VXLAN].gpflags.D)
4404
4405             inner = rx[VXLAN].payload
4406
4407             self.assertEqual(inner[Ether].src, routed_src_mac)
4408             self.assertEqual(inner[Ether].dst, sep1.mac)
4409             self.assertEqual(inner[IP].src, ep1.ip4)
4410             self.assertEqual(inner[IP].dst, ep2.ip4)
4411
4412         rxs = self.send_and_expect(self.pg1, p4[1] * 17, self.pg7)
4413
4414         for rx in rxs:
4415             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
4416             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
4417             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
4418             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
4419             self.assertEqual(rx[VXLAN].vni, 116)
4420             self.assertTrue(rx[VXLAN].flags.G)
4421             self.assertTrue(rx[VXLAN].flags.Instance)
4422             # redirect policy has been applied
4423             self.assertTrue(rx[VXLAN].gpflags.A)
4424             self.assertFalse(rx[VXLAN].gpflags.D)
4425
4426             inner = rx[VXLAN].payload
4427
4428             self.assertEqual(inner[Ether].src, routed_src_mac)
4429             self.assertEqual(inner[Ether].dst, sep1.mac)
4430             self.assertEqual(inner[IP].src, ep2.ip4)
4431             self.assertEqual(inner[IP].dst, ep1.ip4)
4432
4433         rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7)
4434
4435         for rx in rxs:
4436             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
4437             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
4438             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
4439             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
4440             self.assertEqual(rx[VXLAN].vni, 116)
4441             self.assertTrue(rx[VXLAN].flags.G)
4442             self.assertTrue(rx[VXLAN].flags.Instance)
4443             # redirect policy has been applied
4444             inner = rx[VXLAN].payload
4445
4446             self.assertEqual(inner[Ether].src, routed_src_mac)
4447             self.assertEqual(inner[Ether].dst, sep1.mac)
4448             self.assertEqual(inner[IPv6].src, ep1.ip6)
4449             self.assertEqual(inner[IPv6].dst, ep2.ip6)
4450
4451         rxs = self.send_and_expect(self.pg1, p6[1] * 17, self.pg7)
4452
4453         for rx in rxs:
4454             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
4455             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
4456             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
4457             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
4458             self.assertEqual(rx[VXLAN].vni, 116)
4459             self.assertTrue(rx[VXLAN].flags.G)
4460             self.assertTrue(rx[VXLAN].flags.Instance)
4461             # redirect policy has been applied
4462             self.assertTrue(rx[VXLAN].gpflags.A)
4463             self.assertFalse(rx[VXLAN].gpflags.D)
4464
4465             inner = rx[VXLAN].payload
4466
4467             self.assertEqual(inner[Ether].src, routed_src_mac)
4468             self.assertEqual(inner[Ether].dst, sep1.mac)
4469             self.assertEqual(inner[IPv6].src, ep2.ip6)
4470             self.assertEqual(inner[IPv6].dst, ep1.ip6)
4471
4472         # configure sep1: it is now local
4473         # packets between ep1 and ep2 are redirected locally
4474         sep1.add_vpp_config()
4475
4476         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
4477
4478         for rx in rxs:
4479             self.assertEqual(rx[Ether].src, routed_src_mac)
4480             self.assertEqual(rx[Ether].dst, sep1.mac)
4481             self.assertEqual(rx[IP].src, ep1.ip4)
4482             self.assertEqual(rx[IP].dst, ep2.ip4)
4483
4484         rxs = self.send_and_expect(self.pg1, p6[1] * 17, sep1.itf)
4485
4486         for rx in rxs:
4487             self.assertEqual(rx[Ether].src, routed_src_mac)
4488             self.assertEqual(rx[Ether].dst, sep1.mac)
4489             self.assertEqual(rx[IPv6].src, ep2.ip6)
4490             self.assertEqual(rx[IPv6].dst, ep1.ip6)
4491
4492         # packet coming from the l2 spine-proxy to sep1
4493         p = (Ether(src=self.pg7.remote_mac,
4494                    dst=self.pg7.local_mac) /
4495              IP(src=self.pg7.remote_ip4,
4496                 dst=self.pg7.local_ip4) /
4497              UDP(sport=1234, dport=48879) /
4498              VXLAN(vni=116, gpid=440, gpflags=0x08, flags=0x88) /
4499              Ether(src=str(self.router_mac), dst=sep1.mac) /
4500              IP(src=ep1.ip4, dst=ep2.ip4) /
4501              UDP(sport=1234, dport=1234) /
4502              Raw(b'\xa5' * 100))
4503
4504         rxs = self.send_and_expect(self.pg7, [p] * 17, sep1.itf)
4505
4506         for rx in rxs:
4507             self.assertEqual(rx[Ether].src, str(self.router_mac))
4508             self.assertEqual(rx[Ether].dst, sep1.mac)
4509             self.assertEqual(rx[IP].src, ep1.ip4)
4510             self.assertEqual(rx[IP].dst, ep2.ip4)
4511
4512         # contract for SEP to communicate with dst EP
4513         c3 = VppGbpContract(
4514             self, 402, epg_320.sclass, epg_221.sclass, acl.acl_index,
4515             [VppGbpContractRule(
4516                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
4517                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC),
4518              VppGbpContractRule(
4519                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
4520                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC)],
4521             [ETH_P_IP, ETH_P_IPV6])
4522         c3.add_vpp_config()
4523
4524         # temporarily remove ep2, so that ep2 is remote & unknown
4525         ep2.remove_vpp_config()
4526
4527         # packet going back from sep1 to its original dest (ep2)
4528         # as ep2 is now unknown (see above), it must go through
4529         # the rd UU (packet is routed)
4530
4531         p1 = (Ether(src=sep1.mac, dst=self.router_mac) /
4532               IP(src=ep1.ip4, dst=ep2.ip4) /
4533               UDP(sport=1234, dport=1234) /
4534               Raw(b'\xa5' * 100))
4535
4536         rxs = self.send_and_expect(self.pg3, [p1] * 17, self.pg7)
4537
4538         for rx in rxs:
4539             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
4540             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
4541             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
4542             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
4543             self.assertEqual(rx[VXLAN].vni, 114)
4544             self.assertTrue(rx[VXLAN].flags.G)
4545             self.assertTrue(rx[VXLAN].flags.Instance)
4546             # redirect policy has been applied
4547             inner = rx[VXLAN].payload
4548             self.assertEqual(inner[Ether].src, routed_src_mac)
4549             self.assertEqual(inner[Ether].dst, routed_dst_mac)
4550             self.assertEqual(inner[IP].src, ep1.ip4)
4551             self.assertEqual(inner[IP].dst, ep2.ip4)
4552
4553         self.logger.info(self.vapi.cli("show bridge 3 detail"))
4554         sep1.remove_vpp_config()
4555
4556         self.logger.info(self.vapi.cli("show bridge 1 detail"))
4557         self.logger.info(self.vapi.cli("show bridge 2 detail"))
4558
4559         # re-add ep2: it is local again :)
4560         ep2.add_vpp_config()
4561
4562         # packet coming back from the remote sep through rd UU
4563         p2 = (Ether(src=self.pg7.remote_mac,
4564                     dst=self.pg7.local_mac) /
4565               IP(src=self.pg7.remote_ip4,
4566                  dst=self.pg7.local_ip4) /
4567               UDP(sport=1234, dport=48879) /
4568               VXLAN(vni=114, gpid=441, gpflags=0x09, flags=0x88) /
4569               Ether(src=str(self.router_mac), dst=self.router_mac) /
4570               IP(src=ep1.ip4, dst=ep2.ip4) /
4571               UDP(sport=1234, dport=1234) /
4572               Raw(b'\xa5' * 100))
4573
4574         rxs = self.send_and_expect(self.pg7, [p2], self.pg1)
4575
4576         for rx in rxs:
4577             self.assertEqual(rx[Ether].src, str(self.router_mac))
4578             self.assertEqual(rx[Ether].dst, self.pg1.remote_mac)
4579             self.assertEqual(rx[IP].src, ep1.ip4)
4580             self.assertEqual(rx[IP].dst, ep2.ip4)
4581
4582         #
4583         # bd_uu2.add_vpp_config()
4584         #
4585
4586         #
4587         # cleanup
4588         #
4589         c1.remove_vpp_config()
4590         c2.remove_vpp_config()
4591         c3.remove_vpp_config()
4592         self.pg7.unconfig_ip4()
4593
4594     def test_gbp_l3_out(self):
4595         """ GBP L3 Out """
4596
4597         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
4598         self.vapi.cli("set logging class gbp level debug")
4599
4600         routed_dst_mac = "00:0c:0c:0c:0c:0c"
4601         routed_src_mac = "00:22:bd:f8:19:ff"
4602
4603         #
4604         # IP tables
4605         #
4606         t4 = VppIpTable(self, 1)
4607         t4.add_vpp_config()
4608         t6 = VppIpTable(self, 1, True)
4609         t6.add_vpp_config()
4610
4611         rd1 = VppGbpRouteDomain(self, 2, 55, t4, t6)
4612         rd1.add_vpp_config()
4613
4614         self.loop0.set_mac(self.router_mac)
4615
4616         #
4617         # Bind the BVI to the RD
4618         #
4619         VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
4620         VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
4621
4622         #
4623         # Pg7 hosts a BD's BUM
4624         # Pg1 some other l3 interface
4625         #
4626         self.pg7.config_ip4()
4627         self.pg7.resolve_arp()
4628
4629         #
4630         # a multicast vxlan-gbp tunnel for broadcast in the BD
4631         #
4632         tun_bm = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
4633                                    "239.1.1.1", 88,
4634                                    mcast_itf=self.pg7)
4635         tun_bm.add_vpp_config()
4636
4637         #
4638         # a GBP external bridge domains for the EPs
4639         #
4640         bd1 = VppBridgeDomain(self, 1)
4641         bd1.add_vpp_config()
4642         gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, None, tun_bm)
4643         gbd1.add_vpp_config()
4644
4645         #
4646         # The Endpoint-groups in which the external endpoints exist
4647         #
4648         epg_220 = VppGbpEndpointGroup(self, 220, 113, rd1, gbd1,
4649                                       None, gbd1.bvi,
4650                                       "10.0.0.128",
4651                                       "2001:10::128",
4652                                       VppGbpEndpointRetention(2))
4653         epg_220.add_vpp_config()
4654
4655         # the BVIs have the subnets applied ...
4656         ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 24)
4657         ip4_addr.add_vpp_config()
4658         ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 64)
4659         ip6_addr.add_vpp_config()
4660
4661         # ... which are L3-out subnets
4662         l3o_1 = VppGbpSubnet(
4663             self, rd1, "10.0.0.0", 24,
4664             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
4665             sclass=113)
4666         l3o_1.add_vpp_config()
4667
4668         #
4669         # an external interface attached to the outside world and the
4670         # external BD
4671         #
4672         VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
4673         VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
4674         vlan_144 = VppDot1QSubint(self, self.pg0, 144)
4675         vlan_144.admin_up()
4676         # vlan_102 is not poped
4677
4678         #
4679         # an unicast vxlan-gbp for inter-RD traffic
4680         #
4681         vx_tun_l3 = VppGbpVxlanTunnel(
4682             self, 444, rd1.rd_id,
4683             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
4684             self.pg2.local_ip4)
4685         vx_tun_l3.add_vpp_config()
4686
4687         #
4688         # External Endpoints
4689         #
4690         eep1 = VppGbpEndpoint(self, self.vlan_100,
4691                               epg_220, None,
4692                               "10.0.0.1", "11.0.0.1",
4693                               "2001:10::1", "3001::1",
4694                               ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
4695         eep1.add_vpp_config()
4696         eep2 = VppGbpEndpoint(self, self.vlan_101,
4697                               epg_220, None,
4698                               "10.0.0.2", "11.0.0.2",
4699                               "2001:10::2", "3001::2",
4700                               ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
4701         eep2.add_vpp_config()
4702         eep3 = VppGbpEndpoint(self, self.vlan_102,
4703                               epg_220, None,
4704                               "10.0.0.3", "11.0.0.3",
4705                               "2001:10::3", "3001::3",
4706                               ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
4707         eep3.add_vpp_config()
4708
4709         #
4710         # A remote external endpoint
4711         #
4712         rep = VppGbpEndpoint(self, vx_tun_l3,
4713                              epg_220, None,
4714                              "10.0.0.101", "11.0.0.101",
4715                              "2001:10::101", "3001::101",
4716                              ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
4717                              self.pg7.local_ip4,
4718                              self.pg7.remote_ip4,
4719                              mac=None)
4720         rep.add_vpp_config()
4721
4722         #
4723         # EP1 impersonating EP3 is dropped
4724         #
4725         p = (Ether(src=eep1.mac, dst="ff:ff:ff:ff:ff:ff") /
4726              Dot1Q(vlan=100) /
4727              ARP(op="who-has",
4728                  psrc="10.0.0.3", pdst="10.0.0.128",
4729                  hwsrc=eep1.mac, hwdst="ff:ff:ff:ff:ff:ff"))
4730         self.send_and_assert_no_replies(self.pg0, p)
4731
4732         #
4733         # ARP packet from External EPs are accepted and replied to
4734         #
4735         p_arp = (Ether(src=eep1.mac, dst="ff:ff:ff:ff:ff:ff") /
4736                  Dot1Q(vlan=100) /
4737                  ARP(op="who-has",
4738                      psrc=eep1.ip4, pdst="10.0.0.128",
4739                      hwsrc=eep1.mac, hwdst="ff:ff:ff:ff:ff:ff"))
4740         rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0)
4741
4742         #
4743         # ARP packet from host in remote subnet are accepted and replied to
4744         #
4745         p_arp = (Ether(src=eep3.mac, dst="ff:ff:ff:ff:ff:ff") /
4746                  Dot1Q(vlan=102) /
4747                  ARP(op="who-has",
4748                      psrc=eep3.ip4, pdst="10.0.0.128",
4749                      hwsrc=eep3.mac, hwdst="ff:ff:ff:ff:ff:ff"))
4750         rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0)
4751
4752         #
4753         # packets destined to unknown addresses in the BVI's subnet
4754         # are ARP'd for
4755         #
4756         p4 = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
4757               Dot1Q(vlan=100) /
4758               IP(src="10.0.0.1", dst="10.0.0.88") /
4759               UDP(sport=1234, dport=1234) /
4760               Raw(b'\xa5' * 100))
4761         p6 = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
4762               Dot1Q(vlan=100) /
4763               IPv6(src="2001:10::1", dst="2001:10::88") /
4764               UDP(sport=1234, dport=1234) /
4765               Raw(b'\xa5' * 100))
4766
4767         rxs = self.send_and_expect(self.pg0, p4 * 1, self.pg7)
4768
4769         for rx in rxs:
4770             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
4771             # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
4772             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
4773             self.assertEqual(rx[IP].dst, "239.1.1.1")
4774             self.assertEqual(rx[VXLAN].vni, 88)
4775             self.assertTrue(rx[VXLAN].flags.G)
4776             self.assertTrue(rx[VXLAN].flags.Instance)
4777             # policy was applied to the original IP packet
4778             self.assertEqual(rx[VXLAN].gpid, 113)
4779             self.assertTrue(rx[VXLAN].gpflags.A)
4780             self.assertFalse(rx[VXLAN].gpflags.D)
4781
4782             inner = rx[VXLAN].payload
4783
4784             self.assertTrue(inner.haslayer(ARP))
4785
4786         #
4787         # remote to external
4788         #
4789         p = (Ether(src=self.pg7.remote_mac,
4790                    dst=self.pg7.local_mac) /
4791              IP(src=self.pg7.remote_ip4,
4792                 dst=self.pg7.local_ip4) /
4793              UDP(sport=1234, dport=48879) /
4794              VXLAN(vni=444, gpid=113, flags=0x88) /
4795              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
4796              IP(src="10.0.0.101", dst="10.0.0.1") /
4797              UDP(sport=1234, dport=1234) /
4798              Raw(b'\xa5' * 100))
4799
4800         rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
4801
4802         #
4803         # local EP pings router
4804         #
4805         p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
4806              Dot1Q(vlan=100) /
4807              IP(src=eep1.ip4, dst="10.0.0.128") /
4808              ICMP(type='echo-request'))
4809
4810         rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
4811
4812         for rx in rxs:
4813             self.assertEqual(rx[Ether].src, str(self.router_mac))
4814             self.assertEqual(rx[Ether].dst, eep1.mac)
4815             self.assertEqual(rx[Dot1Q].vlan, 100)
4816
4817         #
4818         # local EP pings other local EP
4819         #
4820         p = (Ether(src=eep1.mac, dst=eep2.mac) /
4821              Dot1Q(vlan=100) /
4822              IP(src=eep1.ip4, dst=eep2.ip4) /
4823              ICMP(type='echo-request'))
4824
4825         rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
4826
4827         for rx in rxs:
4828             self.assertEqual(rx[Ether].src, eep1.mac)
4829             self.assertEqual(rx[Ether].dst, eep2.mac)
4830             self.assertEqual(rx[Dot1Q].vlan, 101)
4831
4832         #
4833         # local EP pings router w/o vlan tag poped
4834         #
4835         p = (Ether(src=eep3.mac, dst=str(self.router_mac)) /
4836              Dot1Q(vlan=102) /
4837              IP(src=eep3.ip4, dst="10.0.0.128") /
4838              ICMP(type='echo-request'))
4839
4840         rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
4841
4842         for rx in rxs:
4843             self.assertEqual(rx[Ether].src, str(self.router_mac))
4844             self.assertEqual(rx[Ether].dst, self.vlan_102.remote_mac)
4845
4846         #
4847         # A ip4 subnet reachable through the external EP1
4848         #
4849         ip_220 = VppIpRoute(self, "10.220.0.0", 24,
4850                             [VppRoutePath(eep1.ip4,
4851                                           eep1.epg.bvi.sw_if_index)],
4852                             table_id=t4.table_id)
4853         ip_220.add_vpp_config()
4854
4855         l3o_220 = VppGbpSubnet(
4856             self, rd1, "10.220.0.0", 24,
4857             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
4858             sclass=4220)
4859         l3o_220.add_vpp_config()
4860
4861         #
4862         # An ip6 subnet reachable through the external EP1
4863         #
4864         ip6_220 = VppIpRoute(self, "10:220::", 64,
4865                              [VppRoutePath(eep1.ip6,
4866                                            eep1.epg.bvi.sw_if_index)],
4867                              table_id=t6.table_id)
4868         ip6_220.add_vpp_config()
4869
4870         l3o6_220 = VppGbpSubnet(
4871             self, rd1, "10:220::", 64,
4872             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
4873             sclass=4220)
4874         l3o6_220.add_vpp_config()
4875
4876         #
4877         # A subnet reachable through the external EP2
4878         #
4879         ip_221 = VppIpRoute(self, "10.221.0.0", 24,
4880                             [VppRoutePath(eep2.ip4,
4881                                           eep2.epg.bvi.sw_if_index)],
4882                             table_id=t4.table_id)
4883         ip_221.add_vpp_config()
4884
4885         l3o_221 = VppGbpSubnet(
4886             self, rd1, "10.221.0.0", 24,
4887             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
4888             sclass=4221)
4889         l3o_221.add_vpp_config()
4890
4891         #
4892         # ping between hosts in remote subnets
4893         #  dropped without a contract
4894         #
4895         p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
4896              Dot1Q(vlan=100) /
4897              IP(src="10.220.0.1", dst="10.221.0.1") /
4898              ICMP(type='echo-request'))
4899
4900         self.send_and_assert_no_replies(self.pg0, p * 1)
4901
4902         #
4903         # contract for the external nets to communicate
4904         #
4905         rule4 = AclRule(is_permit=1, proto=17)
4906         rule6 = AclRule(src_prefix=IPv6Network((0, 0)),
4907                         dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17)
4908         acl = VppAcl(self, rules=[rule4, rule6])
4909         acl.add_vpp_config()
4910
4911         #
4912         # A contract with the wrong scope is not matched
4913         #
4914         c_44 = VppGbpContract(
4915             self, 44, 4220, 4221, acl.acl_index,
4916             [VppGbpContractRule(
4917                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
4918                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
4919                 []),
4920              VppGbpContractRule(
4921                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
4922                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
4923                  [])],
4924             [ETH_P_IP, ETH_P_IPV6])
4925         c_44.add_vpp_config()
4926         self.send_and_assert_no_replies(self.pg0, p * 1)
4927
4928         c1 = VppGbpContract(
4929             self, 55, 4220, 4221, acl.acl_index,
4930             [VppGbpContractRule(
4931                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
4932                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
4933                 []),
4934                 VppGbpContractRule(
4935                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
4936                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
4937                     [])],
4938             [ETH_P_IP, ETH_P_IPV6])
4939         c1.add_vpp_config()
4940
4941         #
4942         # Contracts allowing ext-net 200 to talk with external EPs
4943         #
4944         c2 = VppGbpContract(
4945             self, 55, 4220, 113, acl.acl_index,
4946             [VppGbpContractRule(
4947                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
4948                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
4949                 []),
4950                 VppGbpContractRule(
4951                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
4952                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
4953                     [])],
4954             [ETH_P_IP, ETH_P_IPV6])
4955         c2.add_vpp_config()
4956         c3 = VppGbpContract(
4957             self, 55, 113, 4220, acl.acl_index,
4958             [VppGbpContractRule(
4959                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
4960                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
4961                 []),
4962                 VppGbpContractRule(
4963                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
4964                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
4965                     [])],
4966             [ETH_P_IP, ETH_P_IPV6])
4967         c3.add_vpp_config()
4968
4969         #
4970         # ping between hosts in remote subnets
4971         #
4972         p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
4973              Dot1Q(vlan=100) /
4974              IP(src="10.220.0.1", dst="10.221.0.1") /
4975              UDP(sport=1234, dport=1234) /
4976              Raw(b'\xa5' * 100))
4977
4978         rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
4979
4980         for rx in rxs:
4981             self.assertEqual(rx[Ether].src, str(self.router_mac))
4982             self.assertEqual(rx[Ether].dst, eep2.mac)
4983             self.assertEqual(rx[Dot1Q].vlan, 101)
4984
4985         # we did not learn these external hosts
4986         self.assertFalse(find_gbp_endpoint(self, ip="10.220.0.1"))
4987         self.assertFalse(find_gbp_endpoint(self, ip="10.221.0.1"))
4988
4989         #
4990         # from remote external EP to local external EP
4991         #
4992         p = (Ether(src=self.pg7.remote_mac,
4993                    dst=self.pg7.local_mac) /
4994              IP(src=self.pg7.remote_ip4,
4995                 dst=self.pg7.local_ip4) /
4996              UDP(sport=1234, dport=48879) /
4997              VXLAN(vni=444, gpid=113, flags=0x88) /
4998              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
4999              IP(src="10.0.0.101", dst="10.220.0.1") /
5000              UDP(sport=1234, dport=1234) /
5001              Raw(b'\xa5' * 100))
5002
5003         rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
5004
5005         #
5006         # ping from an external host to the remote external EP
5007         #
5008         p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
5009              Dot1Q(vlan=100) /
5010              IP(src="10.220.0.1", dst=rep.ip4) /
5011              UDP(sport=1234, dport=1234) /
5012              Raw(b'\xa5' * 100))
5013
5014         rxs = self.send_and_expect(self.pg0, p * 1, self.pg7)
5015
5016         for rx in rxs:
5017             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
5018             # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
5019             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
5020             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
5021             self.assertEqual(rx[VXLAN].vni, 444)
5022             self.assertTrue(rx[VXLAN].flags.G)
5023             self.assertTrue(rx[VXLAN].flags.Instance)
5024             # the sclass of the ext-net the packet came from
5025             self.assertEqual(rx[VXLAN].gpid, 4220)
5026             # policy was applied to the original IP packet
5027             self.assertTrue(rx[VXLAN].gpflags.A)
5028             # since it's an external host the reciever should not learn it
5029             self.assertTrue(rx[VXLAN].gpflags.D)
5030             inner = rx[VXLAN].payload
5031             self.assertEqual(inner[IP].src, "10.220.0.1")
5032             self.assertEqual(inner[IP].dst, rep.ip4)
5033
5034         #
5035         # An external subnet reachable via the remote external EP
5036         #
5037
5038         #
5039         # first the VXLAN-GBP tunnel over which it is reached
5040         #
5041         vx_tun_r1 = VppVxlanGbpTunnel(
5042             self, self.pg7.local_ip4,
5043             self.pg7.remote_ip4, 445,
5044             mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.
5045                   VXLAN_GBP_API_TUNNEL_MODE_L3))
5046         vx_tun_r1.add_vpp_config()
5047         VppIpInterfaceBind(self, vx_tun_r1, t4).add_vpp_config()
5048
5049         self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
5050
5051         #
5052         # then the special adj to resolve through on that tunnel
5053         #
5054         n1 = VppNeighbor(self,
5055                          vx_tun_r1.sw_if_index,
5056                          "00:0c:0c:0c:0c:0c",
5057                          self.pg7.remote_ip4)
5058         n1.add_vpp_config()
5059
5060         #
5061         # the route via the adj above
5062         #
5063         ip_222 = VppIpRoute(self, "10.222.0.0", 24,
5064                             [VppRoutePath(self.pg7.remote_ip4,
5065                                           vx_tun_r1.sw_if_index)],
5066                             table_id=t4.table_id)
5067         ip_222.add_vpp_config()
5068
5069         l3o_222 = VppGbpSubnet(
5070             self, rd1, "10.222.0.0", 24,
5071             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
5072             sclass=4222)
5073         l3o_222.add_vpp_config()
5074
5075         #
5076         # ping between hosts in local and remote external subnets
5077         #  dropped without a contract
5078         #
5079         p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
5080              Dot1Q(vlan=100) /
5081              IP(src="10.220.0.1", dst="10.222.0.1") /
5082              UDP(sport=1234, dport=1234) /
5083              Raw(b'\xa5' * 100))
5084
5085         rxs = self.send_and_assert_no_replies(self.pg0, p * 1)
5086
5087         #
5088         # Add contracts ext-nets for 220 -> 222
5089         #
5090         c4 = VppGbpContract(
5091             self, 55, 4220, 4222, acl.acl_index,
5092             [VppGbpContractRule(
5093                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
5094                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
5095                 []),
5096                 VppGbpContractRule(
5097                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
5098                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
5099                     [])],
5100             [ETH_P_IP, ETH_P_IPV6])
5101         c4.add_vpp_config()
5102
5103         #
5104         # ping from host in local to remote external subnets
5105         #
5106         p = (Ether(src=eep1.mac, dst=str(self.router_mac)) /
5107              Dot1Q(vlan=100) /
5108              IP(src="10.220.0.1", dst="10.222.0.1") /
5109              UDP(sport=1234, dport=1234) /
5110              Raw(b'\xa5' * 100))
5111
5112         rxs = self.send_and_expect(self.pg0, p * 3, self.pg7)
5113
5114         for rx in rxs:
5115             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
5116             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
5117             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
5118             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
5119             self.assertEqual(rx[VXLAN].vni, 445)
5120             self.assertTrue(rx[VXLAN].flags.G)
5121             self.assertTrue(rx[VXLAN].flags.Instance)
5122             # the sclass of the ext-net the packet came from
5123             self.assertEqual(rx[VXLAN].gpid, 4220)
5124             # policy was applied to the original IP packet
5125             self.assertTrue(rx[VXLAN].gpflags.A)
5126             # since it's an external host the reciever should not learn it
5127             self.assertTrue(rx[VXLAN].gpflags.D)
5128             inner = rx[VXLAN].payload
5129             self.assertEqual(inner[Ether].dst, "00:0c:0c:0c:0c:0c")
5130             self.assertEqual(inner[IP].src, "10.220.0.1")
5131             self.assertEqual(inner[IP].dst, "10.222.0.1")
5132
5133         #
5134         # make the external subnet ECMP
5135         #
5136         vx_tun_r2 = VppVxlanGbpTunnel(
5137             self, self.pg7.local_ip4,
5138             self.pg7.remote_ip4, 446,
5139             mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.
5140                   VXLAN_GBP_API_TUNNEL_MODE_L3))
5141         vx_tun_r2.add_vpp_config()
5142         VppIpInterfaceBind(self, vx_tun_r2, t4).add_vpp_config()
5143
5144         self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
5145
5146         n2 = VppNeighbor(self,
5147                          vx_tun_r2.sw_if_index,
5148                          "00:0c:0c:0c:0c:0c",
5149                          self.pg7.remote_ip4)
5150         n2.add_vpp_config()
5151
5152         ip_222.modify([VppRoutePath(self.pg7.remote_ip4,
5153                                     vx_tun_r1.sw_if_index),
5154                        VppRoutePath(self.pg7.remote_ip4,
5155                                     vx_tun_r2.sw_if_index)])
5156
5157         #
5158         # now expect load-balance
5159         #
5160         p = [(Ether(src=eep1.mac, dst=str(self.router_mac)) /
5161               Dot1Q(vlan=100) /
5162               IP(src="10.220.0.1", dst="10.222.0.1") /
5163               UDP(sport=1234, dport=1234) /
5164               Raw(b'\xa5' * 100)),
5165              (Ether(src=eep1.mac, dst=str(self.router_mac)) /
5166               Dot1Q(vlan=100) /
5167               IP(src="10.220.0.1", dst="10.222.0.1") /
5168               UDP(sport=1222, dport=1235) /
5169               Raw(b'\xa5' * 100))]
5170
5171         rxs = self.send_and_expect(self.pg0, p, self.pg7)
5172
5173         self.assertEqual(rxs[0][VXLAN].vni, 445)
5174         self.assertEqual(rxs[1][VXLAN].vni, 446)
5175
5176         #
5177         # Same LB test for v6
5178         #
5179         n3 = VppNeighbor(self,
5180                          vx_tun_r1.sw_if_index,
5181                          "00:0c:0c:0c:0c:0c",
5182                          self.pg7.remote_ip6)
5183         n3.add_vpp_config()
5184         n4 = VppNeighbor(self,
5185                          vx_tun_r2.sw_if_index,
5186                          "00:0c:0c:0c:0c:0c",
5187                          self.pg7.remote_ip6)
5188         n4.add_vpp_config()
5189
5190         ip_222_6 = VppIpRoute(self, "10:222::", 64,
5191                               [VppRoutePath(self.pg7.remote_ip6,
5192                                             vx_tun_r1.sw_if_index),
5193                                VppRoutePath(self.pg7.remote_ip6,
5194                                             vx_tun_r2.sw_if_index)],
5195                               table_id=t6.table_id)
5196         ip_222_6.add_vpp_config()
5197
5198         l3o_222_6 = VppGbpSubnet(
5199             self, rd1, "10:222::", 64,
5200             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
5201             sclass=4222)
5202         l3o_222_6.add_vpp_config()
5203
5204         p = [(Ether(src=eep1.mac, dst=str(self.router_mac)) /
5205               Dot1Q(vlan=100) /
5206               IPv6(src="10:220::1", dst="10:222::1") /
5207               UDP(sport=1234, dport=1234) /
5208               Raw(b'\xa5' * 100)),
5209              (Ether(src=eep1.mac, dst=str(self.router_mac)) /
5210               Dot1Q(vlan=100) /
5211               IPv6(src="10:220::1", dst="10:222::1") /
5212               UDP(sport=7777, dport=8881) /
5213               Raw(b'\xa5' * 100))]
5214
5215         self.logger.info(self.vapi.cli("sh ip6 fib 10:222::1"))
5216         rxs = self.send_and_expect(self.pg0, p, self.pg7)
5217
5218         self.assertEqual(rxs[0][VXLAN].vni, 445)
5219         self.assertEqual(rxs[1][VXLAN].vni, 446)
5220
5221         #
5222         # ping from host in remote to local external subnets
5223         # there's no contract for this, but the A bit is set.
5224         #
5225         p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) /
5226              IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) /
5227              UDP(sport=1234, dport=48879) /
5228              VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') /
5229              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
5230              IP(src="10.222.0.1", dst="10.220.0.1") /
5231              UDP(sport=1234, dport=1234) /
5232              Raw(b'\xa5' * 100))
5233
5234         rxs = self.send_and_expect(self.pg7, p * 3, self.pg0)
5235         self.assertFalse(find_gbp_endpoint(self, ip="10.222.0.1"))
5236
5237         #
5238         # ping from host in remote to remote external subnets
5239         #   this is dropped by reflection check.
5240         #
5241         p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) /
5242              IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) /
5243              UDP(sport=1234, dport=48879) /
5244              VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') /
5245              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
5246              IP(src="10.222.0.1", dst="10.222.0.2") /
5247              UDP(sport=1234, dport=1234) /
5248              Raw(b'\xa5' * 100))
5249
5250         rxs = self.send_and_assert_no_replies(self.pg7, p * 3)
5251
5252         p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) /
5253              IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) /
5254              UDP(sport=1234, dport=48879) /
5255              VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') /
5256              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
5257              IPv6(src="10:222::1", dst="10:222::2") /
5258              UDP(sport=1234, dport=1234) /
5259              Raw(b'\xa5' * 100))
5260
5261         rxs = self.send_and_assert_no_replies(self.pg7, p * 3)
5262
5263         #
5264         # local EP
5265         #
5266         lep1 = VppGbpEndpoint(self, vlan_144,
5267                               epg_220, None,
5268                               "10.0.0.44", "11.0.0.44",
5269                               "2001:10::44", "3001::44")
5270         lep1.add_vpp_config()
5271
5272         #
5273         # local EP to local ip4 external subnet
5274         #
5275         p = (Ether(src=lep1.mac, dst=str(self.router_mac)) /
5276              Dot1Q(vlan=144) /
5277              IP(src=lep1.ip4, dst="10.220.0.1") /
5278              UDP(sport=1234, dport=1234) /
5279              Raw(b'\xa5' * 100))
5280
5281         rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
5282
5283         for rx in rxs:
5284             self.assertEqual(rx[Ether].src, str(self.router_mac))
5285             self.assertEqual(rx[Ether].dst, eep1.mac)
5286             self.assertEqual(rx[Dot1Q].vlan, 100)
5287
5288         #
5289         # local EP to local ip6 external subnet
5290         #
5291         p = (Ether(src=lep1.mac, dst=str(self.router_mac)) /
5292              Dot1Q(vlan=144) /
5293              IPv6(src=lep1.ip6, dst="10:220::1") /
5294              UDP(sport=1234, dport=1234) /
5295              Raw(b'\xa5' * 100))
5296
5297         rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
5298
5299         for rx in rxs:
5300             self.assertEqual(rx[Ether].src, str(self.router_mac))
5301             self.assertEqual(rx[Ether].dst, eep1.mac)
5302             self.assertEqual(rx[Dot1Q].vlan, 100)
5303
5304         #
5305         # ip4 and ip6 subnets that load-balance
5306         #
5307         ip_20 = VppIpRoute(self, "10.20.0.0", 24,
5308                            [VppRoutePath(eep1.ip4,
5309                                          eep1.epg.bvi.sw_if_index),
5310                             VppRoutePath(eep2.ip4,
5311                                          eep2.epg.bvi.sw_if_index)],
5312                            table_id=t4.table_id)
5313         ip_20.add_vpp_config()
5314
5315         l3o_20 = VppGbpSubnet(
5316             self, rd1, "10.20.0.0", 24,
5317             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
5318             sclass=4220)
5319         l3o_20.add_vpp_config()
5320
5321         ip6_20 = VppIpRoute(self, "10:20::", 64,
5322                             [VppRoutePath(eep1.ip6,
5323                                           eep1.epg.bvi.sw_if_index),
5324                              VppRoutePath(eep2.ip6,
5325                                           eep2.epg.bvi.sw_if_index)],
5326                             table_id=t6.table_id)
5327         ip6_20.add_vpp_config()
5328
5329         l3o6_20 = VppGbpSubnet(
5330             self, rd1, "10:20::", 64,
5331             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
5332             sclass=4220)
5333         l3o6_20.add_vpp_config()
5334
5335         self.logger.info(self.vapi.cli("sh ip fib 10.20.0.1"))
5336         self.logger.info(self.vapi.cli("sh ip6 fib 10:20::1"))
5337
5338         # two ip6 packets whose port are chosen so they load-balance
5339         p = [(Ether(src=lep1.mac, dst=str(self.router_mac)) /
5340               Dot1Q(vlan=144) /
5341               IPv6(src=lep1.ip6, dst="10:20::1") /
5342               UDP(sport=1234, dport=1234) /
5343               Raw(b'\xa5' * 100)),
5344              (Ether(src=lep1.mac, dst=str(self.router_mac)) /
5345               Dot1Q(vlan=144) /
5346               IPv6(src=lep1.ip6, dst="10:20::1") /
5347               UDP(sport=124, dport=1230) /
5348               Raw(b'\xa5' * 100))]
5349
5350         rxs = self.send_and_expect(self.pg0, p, self.pg0, 2)
5351
5352         self.assertEqual(rxs[0][Dot1Q].vlan, 101)
5353         self.assertEqual(rxs[1][Dot1Q].vlan, 100)
5354
5355         # two ip4 packets whose port are chosen so they load-balance
5356         p = [(Ether(src=lep1.mac, dst=str(self.router_mac)) /
5357               Dot1Q(vlan=144) /
5358               IP(src=lep1.ip4, dst="10.20.0.1") /
5359               UDP(sport=1235, dport=1235) /
5360               Raw(b'\xa5' * 100)),
5361              (Ether(src=lep1.mac, dst=str(self.router_mac)) /
5362               Dot1Q(vlan=144) /
5363               IP(src=lep1.ip4, dst="10.20.0.1") /
5364               UDP(sport=124, dport=1230) /
5365               Raw(b'\xa5' * 100))]
5366
5367         rxs = self.send_and_expect(self.pg0, p, self.pg0, 2)
5368
5369         self.assertEqual(rxs[0][Dot1Q].vlan, 101)
5370         self.assertEqual(rxs[1][Dot1Q].vlan, 100)
5371
5372         #
5373         # cleanup
5374         #
5375         ip_222.remove_vpp_config()
5376         self.pg7.unconfig_ip4()
5377         self.vlan_101.set_vtr(L2_VTR_OP.L2_DISABLED)
5378         self.vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED)
5379
5380     def test_gbp_anon_l3_out(self):
5381         """ GBP Anonymous L3 Out """
5382
5383         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
5384         self.vapi.cli("set logging class gbp level debug")
5385
5386         routed_dst_mac = "00:0c:0c:0c:0c:0c"
5387         routed_src_mac = "00:22:bd:f8:19:ff"
5388
5389         #
5390         # IP tables
5391         #
5392         t4 = VppIpTable(self, 1)
5393         t4.add_vpp_config()
5394         t6 = VppIpTable(self, 1, True)
5395         t6.add_vpp_config()
5396
5397         rd1 = VppGbpRouteDomain(self, 2, 55, t4, t6)
5398         rd1.add_vpp_config()
5399
5400         self.loop0.set_mac(self.router_mac)
5401
5402         #
5403         # Bind the BVI to the RD
5404         #
5405         VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
5406         VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
5407
5408         #
5409         # Pg7 hosts a BD's BUM
5410         # Pg1 some other l3 interface
5411         #
5412         self.pg7.config_ip4()
5413         self.pg7.resolve_arp()
5414
5415         #
5416         # a GBP external bridge domains for the EPs
5417         #
5418         bd1 = VppBridgeDomain(self, 1)
5419         bd1.add_vpp_config()
5420         gbd1 = VppGbpBridgeDomain(self, bd1, rd1, self.loop0, None, None)
5421         gbd1.add_vpp_config()
5422
5423         #
5424         # The Endpoint-groups in which the external endpoints exist
5425         #
5426         epg_220 = VppGbpEndpointGroup(self, 220, 113, rd1, gbd1,
5427                                       None, gbd1.bvi,
5428                                       "10.0.0.128",
5429                                       "2001:10::128",
5430                                       VppGbpEndpointRetention(2))
5431         epg_220.add_vpp_config()
5432
5433         # the BVIs have the subnet applied ...
5434         ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 24)
5435         ip4_addr.add_vpp_config()
5436
5437         # ... which is an Anonymous L3-out subnets
5438         l3o_1 = VppGbpSubnet(
5439             self, rd1, "10.0.0.0", 24,
5440             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_ANON_L3_OUT,
5441             sclass=113)
5442         l3o_1.add_vpp_config()
5443
5444         #
5445         # an external interface attached to the outside world and the
5446         # external BD
5447         #
5448         VppL2Vtr(self, self.vlan_100, L2_VTR_OP.L2_POP_1).add_vpp_config()
5449         VppL2Vtr(self, self.vlan_101, L2_VTR_OP.L2_POP_1).add_vpp_config()
5450
5451         #
5452         # vlan_100 and vlan_101 are anonymous l3-out interfaces
5453         #
5454         ext_itf = VppGbpExtItf(self, self.vlan_100, bd1, rd1, anon=True)
5455         ext_itf.add_vpp_config()
5456         ext_itf = VppGbpExtItf(self, self.vlan_101, bd1, rd1, anon=True)
5457         ext_itf.add_vpp_config()
5458
5459         #
5460         # an unicast vxlan-gbp for inter-RD traffic
5461         #
5462         vx_tun_l3 = VppGbpVxlanTunnel(
5463             self, 444, rd1.rd_id,
5464             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
5465             self.pg2.local_ip4)
5466         vx_tun_l3.add_vpp_config()
5467
5468         #
5469         # A remote external endpoint
5470         #
5471         rep = VppGbpEndpoint(self, vx_tun_l3,
5472                              epg_220, None,
5473                              "10.0.0.201", "11.0.0.201",
5474                              "2001:10::201", "3001::101",
5475                              ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
5476                              self.pg7.local_ip4,
5477                              self.pg7.remote_ip4,
5478                              mac=None)
5479         rep.add_vpp_config()
5480
5481         #
5482         # ARP packet from host in external subnet are accepted, flooded and
5483         # replied to. We expect 2 packets:
5484         #   - APR request flooded over the other vlan subif
5485         #   - ARP reply from BVI
5486         #
5487         p_arp = (Ether(src=self.vlan_100.remote_mac,
5488                        dst="ff:ff:ff:ff:ff:ff") /
5489                  Dot1Q(vlan=100) /
5490                  ARP(op="who-has",
5491                      psrc="10.0.0.100",
5492                      pdst="10.0.0.128",
5493                      hwsrc=self.vlan_100.remote_mac,
5494                      hwdst="ff:ff:ff:ff:ff:ff"))
5495         rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0, n_rx=2)
5496
5497         p_arp = (Ether(src=self.vlan_101.remote_mac,
5498                        dst="ff:ff:ff:ff:ff:ff") /
5499                  Dot1Q(vlan=101) /
5500                  ARP(op="who-has",
5501                      psrc='10.0.0.101',
5502                      pdst="10.0.0.128",
5503                      hwsrc=self.vlan_101.remote_mac,
5504                      hwdst="ff:ff:ff:ff:ff:ff"))
5505         rxs = self.send_and_expect(self.pg0, p_arp * 1, self.pg0, n_rx=2)
5506
5507         #
5508         # remote to external
5509         #
5510         p = (Ether(src=self.pg7.remote_mac,
5511                    dst=self.pg7.local_mac) /
5512              IP(src=self.pg7.remote_ip4,
5513                 dst=self.pg7.local_ip4) /
5514              UDP(sport=1234, dport=48879) /
5515              VXLAN(vni=vx_tun_l3.vni, gpid=epg_220.sclass, flags=0x88) /
5516              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
5517              IP(src=str(rep.ip4), dst="10.0.0.100") /
5518              UDP(sport=1234, dport=1234) /
5519              Raw(b'\xa5' * 100))
5520         rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
5521
5522         #
5523         # local EP pings router
5524         #
5525         p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) /
5526              Dot1Q(vlan=100) /
5527              IP(src="10.0.0.100", dst="10.0.0.128") /
5528              ICMP(type='echo-request'))
5529         rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
5530
5531         for rx in rxs:
5532             self.assertEqual(rx[Ether].src, str(self.router_mac))
5533             self.assertEqual(rx[Ether].dst, self.vlan_100.remote_mac)
5534             self.assertEqual(rx[Dot1Q].vlan, 100)
5535
5536         #
5537         # local EP pings other local EP
5538         #
5539         p = (Ether(src=self.vlan_100.remote_mac,
5540                    dst=self.vlan_101.remote_mac) /
5541              Dot1Q(vlan=100) /
5542              IP(src="10.0.0.100", dst="10.0.0.101") /
5543              ICMP(type='echo-request'))
5544         rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
5545
5546         for rx in rxs:
5547             self.assertEqual(rx[Ether].src, self.vlan_100.remote_mac)
5548             self.assertEqual(rx[Ether].dst, self.vlan_101.remote_mac)
5549             self.assertEqual(rx[Dot1Q].vlan, 101)
5550
5551         #
5552         # A subnet reachable through an external router on vlan 100
5553         #
5554         ip_220 = VppIpRoute(self, "10.220.0.0", 24,
5555                             [VppRoutePath("10.0.0.100",
5556                                           epg_220.bvi.sw_if_index)],
5557                             table_id=t4.table_id)
5558         ip_220.add_vpp_config()
5559
5560         l3o_220 = VppGbpSubnet(
5561             self, rd1, "10.220.0.0", 24,
5562             # note: this a "regular" L3 out subnet (not connected)
5563             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
5564             sclass=4220)
5565         l3o_220.add_vpp_config()
5566
5567         #
5568         # A subnet reachable through an external router on vlan 101
5569         #
5570         ip_221 = VppIpRoute(self, "10.221.0.0", 24,
5571                             [VppRoutePath("10.0.0.101",
5572                                           epg_220.bvi.sw_if_index)],
5573                             table_id=t4.table_id)
5574         ip_221.add_vpp_config()
5575
5576         l3o_221 = VppGbpSubnet(
5577             self, rd1, "10.221.0.0", 24,
5578             # note: this a "regular" L3 out subnet (not connected)
5579             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
5580             sclass=4221)
5581         l3o_221.add_vpp_config()
5582
5583         #
5584         # ping between hosts in remote subnets
5585         #  dropped without a contract
5586         #
5587         p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) /
5588              Dot1Q(vlan=100) /
5589              IP(src="10.220.0.1", dst="10.221.0.1") /
5590              ICMP(type='echo-request'))
5591
5592         rxs = self.send_and_assert_no_replies(self.pg0, p * 1)
5593
5594         #
5595         # contract for the external nets to communicate
5596         #
5597         rule4 = AclRule(is_permit=1, proto=17)
5598         rule6 = AclRule(src_prefix=IPv6Network((0, 0)),
5599                         dst_prefix=IPv6Network((0, 0)), is_permit=1, proto=17)
5600         acl = VppAcl(self, rules=[rule4, rule6])
5601         acl.add_vpp_config()
5602
5603         c1 = VppGbpContract(
5604             self, 55, 4220, 4221, acl.acl_index,
5605             [VppGbpContractRule(
5606                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
5607                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
5608                 []),
5609                 VppGbpContractRule(
5610                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
5611                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
5612                     [])],
5613             [ETH_P_IP, ETH_P_IPV6])
5614         c1.add_vpp_config()
5615
5616         #
5617         # Contracts allowing ext-net 200 to talk with external EPs
5618         #
5619         c2 = VppGbpContract(
5620             self, 55, 4220, 113, acl.acl_index,
5621             [VppGbpContractRule(
5622                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
5623                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
5624                 []),
5625                 VppGbpContractRule(
5626                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
5627                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
5628                     [])],
5629             [ETH_P_IP, ETH_P_IPV6])
5630         c2.add_vpp_config()
5631         c3 = VppGbpContract(
5632             self, 55, 113, 4220, acl.acl_index,
5633             [VppGbpContractRule(
5634                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
5635                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
5636                 []),
5637                 VppGbpContractRule(
5638                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
5639                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
5640                     [])],
5641             [ETH_P_IP, ETH_P_IPV6])
5642         c3.add_vpp_config()
5643
5644         #
5645         # ping between hosts in remote subnets
5646         #
5647         p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) /
5648              Dot1Q(vlan=100) /
5649              IP(src="10.220.0.1", dst="10.221.0.1") /
5650              UDP(sport=1234, dport=1234) /
5651              Raw(b'\xa5' * 100))
5652
5653         rxs = self.send_and_expect(self.pg0, p * 1, self.pg0)
5654
5655         for rx in rxs:
5656             self.assertEqual(rx[Ether].src, str(self.router_mac))
5657             self.assertEqual(rx[Ether].dst, self.vlan_101.remote_mac)
5658             self.assertEqual(rx[Dot1Q].vlan, 101)
5659
5660         # we did not learn these external hosts
5661         self.assertFalse(find_gbp_endpoint(self, ip="10.220.0.1"))
5662         self.assertFalse(find_gbp_endpoint(self, ip="10.221.0.1"))
5663
5664         #
5665         # from remote external EP to local external EP
5666         #
5667         p = (Ether(src=self.pg7.remote_mac,
5668                    dst=self.pg7.local_mac) /
5669              IP(src=self.pg7.remote_ip4,
5670                 dst=self.pg7.local_ip4) /
5671              UDP(sport=1234, dport=48879) /
5672              VXLAN(vni=444, gpid=113, flags=0x88) /
5673              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
5674              IP(src=rep.ip4, dst="10.220.0.1") /
5675              UDP(sport=1234, dport=1234) /
5676              Raw(b'\xa5' * 100))
5677
5678         rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
5679
5680         #
5681         # ping from an external host to the remote external EP
5682         #
5683         p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) /
5684              Dot1Q(vlan=100) /
5685              IP(src="10.220.0.1", dst=rep.ip4) /
5686              UDP(sport=1234, dport=1234) /
5687              Raw(b'\xa5' * 100))
5688
5689         rxs = self.send_and_expect(self.pg0, p * 1, self.pg7)
5690
5691         for rx in rxs:
5692             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
5693             # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
5694             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
5695             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
5696             self.assertEqual(rx[VXLAN].vni, 444)
5697             self.assertTrue(rx[VXLAN].flags.G)
5698             self.assertTrue(rx[VXLAN].flags.Instance)
5699             # the sclass of the ext-net the packet came from
5700             self.assertEqual(rx[VXLAN].gpid, 4220)
5701             # policy was applied to the original IP packet
5702             self.assertTrue(rx[VXLAN].gpflags.A)
5703             # since it's an external host the reciever should not learn it
5704             self.assertTrue(rx[VXLAN].gpflags.D)
5705             inner = rx[VXLAN].payload
5706             self.assertEqual(inner[IP].src, "10.220.0.1")
5707             self.assertEqual(inner[IP].dst, rep.ip4)
5708
5709         #
5710         # An external subnet reachable via the remote external EP
5711         #
5712
5713         #
5714         # first the VXLAN-GBP tunnel over which it is reached
5715         #
5716         vx_tun_r = VppVxlanGbpTunnel(
5717             self, self.pg7.local_ip4,
5718             self.pg7.remote_ip4, 445,
5719             mode=(VppEnum.vl_api_vxlan_gbp_api_tunnel_mode_t.
5720                   VXLAN_GBP_API_TUNNEL_MODE_L3))
5721         vx_tun_r.add_vpp_config()
5722         VppIpInterfaceBind(self, vx_tun_r, t4).add_vpp_config()
5723
5724         self.logger.info(self.vapi.cli("sh vxlan-gbp tunnel"))
5725
5726         #
5727         # then the special adj to resolve through on that tunnel
5728         #
5729         n1 = VppNeighbor(self,
5730                          vx_tun_r.sw_if_index,
5731                          "00:0c:0c:0c:0c:0c",
5732                          self.pg7.remote_ip4)
5733         n1.add_vpp_config()
5734
5735         #
5736         # the route via the adj above
5737         #
5738         ip_222 = VppIpRoute(self, "10.222.0.0", 24,
5739                             [VppRoutePath(self.pg7.remote_ip4,
5740                                           vx_tun_r.sw_if_index)],
5741                             table_id=t4.table_id)
5742         ip_222.add_vpp_config()
5743
5744         l3o_222 = VppGbpSubnet(
5745             self, rd1, "10.222.0.0", 24,
5746             # note: this a "regular" l3out subnet (not connected)
5747             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
5748             sclass=4222)
5749         l3o_222.add_vpp_config()
5750
5751         #
5752         # ping between hosts in local and remote external subnets
5753         #  dropped without a contract
5754         #
5755         p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) /
5756              Dot1Q(vlan=100) /
5757              IP(src="10.220.0.1", dst="10.222.0.1") /
5758              UDP(sport=1234, dport=1234) /
5759              Raw(b'\xa5' * 100))
5760
5761         rxs = self.send_and_assert_no_replies(self.pg0, p * 1)
5762
5763         #
5764         # Add contracts ext-nets for 220 -> 222
5765         #
5766         c4 = VppGbpContract(
5767             self, 55, 4220, 4222, acl.acl_index,
5768             [VppGbpContractRule(
5769                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
5770                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
5771                 []),
5772                 VppGbpContractRule(
5773                     VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
5774                     VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
5775                     [])],
5776             [ETH_P_IP, ETH_P_IPV6])
5777         c4.add_vpp_config()
5778
5779         #
5780         # ping from host in local to remote external subnets
5781         #
5782         p = (Ether(src=self.vlan_100.remote_mac, dst=str(self.router_mac)) /
5783              Dot1Q(vlan=100) /
5784              IP(src="10.220.0.1", dst="10.222.0.1") /
5785              UDP(sport=1234, dport=1234) /
5786              Raw(b'\xa5' * 100))
5787
5788         rxs = self.send_and_expect(self.pg0, p * 3, self.pg7)
5789
5790         for rx in rxs:
5791             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
5792             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
5793             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
5794             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
5795             self.assertEqual(rx[VXLAN].vni, 445)
5796             self.assertTrue(rx[VXLAN].flags.G)
5797             self.assertTrue(rx[VXLAN].flags.Instance)
5798             # the sclass of the ext-net the packet came from
5799             self.assertEqual(rx[VXLAN].gpid, 4220)
5800             # policy was applied to the original IP packet
5801             self.assertTrue(rx[VXLAN].gpflags.A)
5802             # since it's an external host the reciever should not learn it
5803             self.assertTrue(rx[VXLAN].gpflags.D)
5804             inner = rx[VXLAN].payload
5805             self.assertEqual(inner[Ether].dst, "00:0c:0c:0c:0c:0c")
5806             self.assertEqual(inner[IP].src, "10.220.0.1")
5807             self.assertEqual(inner[IP].dst, "10.222.0.1")
5808
5809         #
5810         # ping from host in remote to local external subnets
5811         # there's no contract for this, but the A bit is set.
5812         #
5813         p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) /
5814              IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) /
5815              UDP(sport=1234, dport=48879) /
5816              VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') /
5817              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
5818              IP(src="10.222.0.1", dst="10.220.0.1") /
5819              UDP(sport=1234, dport=1234) /
5820              Raw(b'\xa5' * 100))
5821
5822         rxs = self.send_and_expect(self.pg7, p * 3, self.pg0)
5823         self.assertFalse(find_gbp_endpoint(self, ip="10.222.0.1"))
5824
5825         #
5826         # ping from host in remote to remote external subnets
5827         #   this is dropped by reflection check.
5828         #
5829         p = (Ether(src=self.pg7.remote_mac, dst=self.pg7.local_mac) /
5830              IP(src=self.pg7.remote_ip4, dst=self.pg7.local_ip4) /
5831              UDP(sport=1234, dport=48879) /
5832              VXLAN(vni=445, gpid=4222, flags=0x88, gpflags='A') /
5833              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
5834              IP(src="10.222.0.1", dst="10.222.0.2") /
5835              UDP(sport=1234, dport=1234) /
5836              Raw(b'\xa5' * 100))
5837
5838         rxs = self.send_and_assert_no_replies(self.pg7, p * 3)
5839
5840         #
5841         # cleanup
5842         #
5843         self.vlan_101.set_vtr(L2_VTR_OP.L2_DISABLED)
5844         self.vlan_100.set_vtr(L2_VTR_OP.L2_DISABLED)
5845         self.pg7.unconfig_ip4()
5846
5847
5848 if __name__ == '__main__':
5849     unittest.main(testRunner=VppTestRunner)