GBP: contracts API fixed length of allowed ethertypes
[vpp.git] / test / test_gbp.py
1 #!/usr/bin/env python
2
3 from socket import AF_INET, AF_INET6
4 import unittest
5
6 from scapy.packet import Raw
7 from scapy.layers.l2 import Ether, ARP, Dot1Q
8 from scapy.layers.inet import IP, UDP
9 from scapy.layers.inet6 import IPv6, ICMPv6ND_NS,  ICMPv6NDOptSrcLLAddr, \
10     ICMPv6ND_NA
11 from scapy.utils6 import in6_getnsma, in6_getnsmac
12 from scapy.layers.vxlan import VXLAN
13 from scapy.data import ETH_P_IP, ETH_P_IPV6
14 from scapy.utils import inet_pton, inet_ntop
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
21 from vpp_l2 import VppBridgeDomain, VppBridgeDomainPort, \
22     VppBridgeDomainArpEntry, VppL2FibEntry, find_bridge_domain_port
23 from vpp_sub_interface import VppDot1QSubint
24 from vpp_ip import VppIpAddress, VppIpPrefix
25 from vpp_papi import VppEnum, MACAddress
26 from vpp_papi_provider import L2_VTR_OP
27 from vpp_vxlan_gbp_tunnel import find_vxlan_gbp_tunnel, INDEX_INVALID, \
28     VppVxlanGbpTunnel
29
30
31 def find_gbp_endpoint(test, sw_if_index=None, ip=None, mac=None):
32     if ip:
33         vip = VppIpAddress(ip)
34     if mac:
35         vmac = MACAddress(mac)
36
37     eps = test.vapi.gbp_endpoint_dump()
38
39     for ep in eps:
40         if sw_if_index:
41             if ep.endpoint.sw_if_index != sw_if_index:
42                 continue
43         if ip:
44             for eip in ep.endpoint.ips:
45                 if vip == eip:
46                     return True
47         if mac:
48             if vmac.packed == ep.endpoint.mac:
49                 return True
50     return False
51
52
53 def find_gbp_vxlan(test, vni):
54     ts = test.vapi.gbp_vxlan_tunnel_dump()
55     for t in ts:
56         if t.tunnel.vni == vni:
57             return True
58     return False
59
60
61 class VppGbpEndpoint(VppObject):
62     """
63     GBP Endpoint
64     """
65
66     @property
67     def mac(self):
68         return str(self.vmac)
69
70     @property
71     def mac(self):
72         return self.itf.remote_mac
73
74     @property
75     def ip4(self):
76         return self._ip4
77
78     @property
79     def fip4(self):
80         return self._fip4
81
82     @property
83     def ip6(self):
84         return self._ip6
85
86     @property
87     def fip6(self):
88         return self._fip6
89
90     @property
91     def ips(self):
92         return [self.ip4, self.ip6]
93
94     @property
95     def fips(self):
96         return [self.fip4, self.fip6]
97
98     def __init__(self, test, itf, epg, recirc, ip4, fip4, ip6, fip6,
99                  flags=0,
100                  tun_src="0.0.0.0",
101                  tun_dst="0.0.0.0",
102                  mac=True):
103         self._test = test
104         self.itf = itf
105         self.epg = epg
106         self.recirc = recirc
107
108         self._ip4 = VppIpAddress(ip4)
109         self._fip4 = VppIpAddress(fip4)
110         self._ip6 = VppIpAddress(ip6)
111         self._fip6 = VppIpAddress(fip6)
112
113         if mac:
114             self.vmac = MACAddress(self.itf.remote_mac)
115         else:
116             self.vmac = MACAddress("00:00:00:00:00:00")
117
118         self.flags = flags
119         self.tun_src = VppIpAddress(tun_src)
120         self.tun_dst = VppIpAddress(tun_dst)
121
122     def add_vpp_config(self):
123         res = self._test.vapi.gbp_endpoint_add(
124             self.itf.sw_if_index,
125             [self.ip4.encode(), self.ip6.encode()],
126             self.vmac.packed,
127             self.epg.sclass,
128             self.flags,
129             self.tun_src.encode(),
130             self.tun_dst.encode())
131         self.handle = res.handle
132         self._test.registry.register(self, self._test.logger)
133
134     def remove_vpp_config(self):
135         self._test.vapi.gbp_endpoint_del(self.handle)
136
137     def __str__(self):
138         return self.object_id()
139
140     def object_id(self):
141         return "gbp-endpoint:[%d==%d:%s:%d]" % (self.handle,
142                                                 self.itf.sw_if_index,
143                                                 self.ip4.address,
144                                                 self.epg.sclass)
145
146     def query_vpp_config(self):
147         return find_gbp_endpoint(self._test,
148                                  self.itf.sw_if_index,
149                                  self.ip4.address)
150
151
152 class VppGbpRecirc(VppObject):
153     """
154     GBP Recirculation Interface
155     """
156
157     def __init__(self, test, epg, recirc, is_ext=False):
158         self._test = test
159         self.recirc = recirc
160         self.epg = epg
161         self.is_ext = is_ext
162
163     def add_vpp_config(self):
164         self._test.vapi.gbp_recirc_add_del(
165             1,
166             self.recirc.sw_if_index,
167             self.epg.sclass,
168             self.is_ext)
169         self._test.registry.register(self, self._test.logger)
170
171     def remove_vpp_config(self):
172         self._test.vapi.gbp_recirc_add_del(
173             0,
174             self.recirc.sw_if_index,
175             self.epg.sclass,
176             self.is_ext)
177
178     def __str__(self):
179         return self.object_id()
180
181     def object_id(self):
182         return "gbp-recirc:[%d]" % (self.recirc.sw_if_index)
183
184     def query_vpp_config(self):
185         rs = self._test.vapi.gbp_recirc_dump()
186         for r in rs:
187             if r.recirc.sw_if_index == self.recirc.sw_if_index:
188                 return True
189         return False
190
191
192 class VppGbpExtItf(VppObject):
193     """
194     GBP ExtItfulation Interface
195     """
196
197     def __init__(self, test, itf, bd, rd):
198         self._test = test
199         self.itf = itf
200         self.bd = bd
201         self.rd = rd
202
203     def add_vpp_config(self):
204         self._test.vapi.gbp_ext_itf_add_del(
205             1,
206             self.itf.sw_if_index,
207             self.bd.bd_id,
208             self.rd.rd_id)
209         self._test.registry.register(self, self._test.logger)
210
211     def remove_vpp_config(self):
212         self._test.vapi.gbp_ext_itf_add_del(
213             0,
214             self.itf.sw_if_index,
215             self.bd.bd_id,
216             self.rd.rd_id)
217
218     def __str__(self):
219         return self.object_id()
220
221     def object_id(self):
222         return "gbp-ext-itf:[%d]" % (self.itf.sw_if_index)
223
224     def query_vpp_config(self):
225         rs = self._test.vapi.gbp_ext_itf_dump()
226         for r in rs:
227             if r.ext_itf.sw_if_index == self.itf.sw_if_index:
228                 return True
229         return False
230
231
232 class VppGbpSubnet(VppObject):
233     """
234     GBP Subnet
235     """
236     def __init__(self, test, rd, address, address_len,
237                  type, sw_if_index=None, epg=None):
238         self._test = test
239         self.rd_id = rd.rd_id
240         self.prefix = VppIpPrefix(address, address_len)
241         self.type = type
242         self.sw_if_index = sw_if_index
243         self.epg = epg
244
245     def add_vpp_config(self):
246         self._test.vapi.gbp_subnet_add_del(
247             1,
248             self.rd_id,
249             self.prefix.encode(),
250             self.type,
251             sw_if_index=self.sw_if_index if self.sw_if_index else 0xffffffff,
252             sclass=self.epg.sclass if self.epg else 0xffff)
253         self._test.registry.register(self, self._test.logger)
254
255     def remove_vpp_config(self):
256         self._test.vapi.gbp_subnet_add_del(
257             0,
258             self.rd_id,
259             self.prefix.encode(),
260             self.type)
261
262     def __str__(self):
263         return self.object_id()
264
265     def object_id(self):
266         return "gbp-subnet:[%d-%s]" % (self.rd_id, self.prefix)
267
268     def query_vpp_config(self):
269         ss = self._test.vapi.gbp_subnet_dump()
270         for s in ss:
271             if s.subnet.rd_id == self.rd_id and \
272                s.subnet.type == self.type and \
273                s.subnet.prefix == self.prefix:
274                 return True
275         return False
276
277
278 class VppGbpEndpointRetention(object):
279     def __init__(self, remote_ep_timeout=0xffffffff):
280         self.remote_ep_timeout = remote_ep_timeout
281
282     def encode(self):
283         return {'remote_ep_timeout': self.remote_ep_timeout}
284
285
286 class VppGbpEndpointGroup(VppObject):
287     """
288     GBP Endpoint Group
289     """
290
291     def __init__(self, test, vnid, sclass, rd, bd, uplink,
292                  bvi, bvi_ip4, bvi_ip6=None,
293                  retention=VppGbpEndpointRetention()):
294         self._test = test
295         self.uplink = uplink
296         self.bvi = bvi
297         self.bvi_ip4 = VppIpAddress(bvi_ip4)
298         self.bvi_ip6 = VppIpAddress(bvi_ip6)
299         self.vnid = vnid
300         self.bd = bd
301         self.rd = rd
302         self.sclass = sclass
303         if 0 == self.sclass:
304             self.sclass = 0xffff
305         self.retention = retention
306
307     def add_vpp_config(self):
308         self._test.vapi.gbp_endpoint_group_add(
309             self.vnid,
310             self.sclass,
311             self.bd.bd.bd_id,
312             self.rd.rd_id,
313             self.uplink.sw_if_index if self.uplink else INDEX_INVALID,
314             self.retention.encode())
315         self._test.registry.register(self, self._test.logger)
316
317     def remove_vpp_config(self):
318         self._test.vapi.gbp_endpoint_group_del(self.sclass)
319
320     def __str__(self):
321         return self.object_id()
322
323     def object_id(self):
324         return "gbp-endpoint-group:[%d]" % (self.vnid)
325
326     def query_vpp_config(self):
327         epgs = self._test.vapi.gbp_endpoint_group_dump()
328         for epg in epgs:
329             if epg.epg.vnid == self.vnid:
330                 return True
331         return False
332
333
334 class VppGbpBridgeDomain(VppObject):
335     """
336     GBP Bridge Domain
337     """
338
339     def __init__(self, test, bd, bvi, uu_fwd=None,
340                  bm_flood=None, learn=True):
341         self._test = test
342         self.bvi = bvi
343         self.uu_fwd = uu_fwd
344         self.bm_flood = bm_flood
345         self.bd = bd
346
347         e = VppEnum.vl_api_gbp_bridge_domain_flags_t
348         if (learn):
349             self.learn = e.GBP_BD_API_FLAG_NONE
350         else:
351             self.learn = e.GBP_BD_API_FLAG_DO_NOT_LEARN
352
353     def add_vpp_config(self):
354         self._test.vapi.gbp_bridge_domain_add(
355             self.bd.bd_id,
356             self.learn,
357             self.bvi.sw_if_index,
358             self.uu_fwd.sw_if_index if self.uu_fwd else INDEX_INVALID,
359             self.bm_flood.sw_if_index if self.bm_flood else INDEX_INVALID)
360         self._test.registry.register(self, self._test.logger)
361
362     def remove_vpp_config(self):
363         self._test.vapi.gbp_bridge_domain_del(self.bd.bd_id)
364
365     def __str__(self):
366         return self.object_id()
367
368     def object_id(self):
369         return "gbp-bridge-domain:[%d]" % (self.bd.bd_id)
370
371     def query_vpp_config(self):
372         bds = self._test.vapi.gbp_bridge_domain_dump()
373         for bd in bds:
374             if bd.bd.bd_id == self.bd.bd_id:
375                 return True
376         return False
377
378
379 class VppGbpRouteDomain(VppObject):
380     """
381     GBP Route Domain
382     """
383
384     def __init__(self, test, rd_id, t4, t6, ip4_uu=None, ip6_uu=None):
385         self._test = test
386         self.rd_id = rd_id
387         self.t4 = t4
388         self.t6 = t6
389         self.ip4_uu = ip4_uu
390         self.ip6_uu = ip6_uu
391
392     def add_vpp_config(self):
393         self._test.vapi.gbp_route_domain_add(
394             self.rd_id,
395             self.t4.table_id,
396             self.t6.table_id,
397             self.ip4_uu.sw_if_index if self.ip4_uu else INDEX_INVALID,
398             self.ip6_uu.sw_if_index if self.ip6_uu else INDEX_INVALID)
399         self._test.registry.register(self, self._test.logger)
400
401     def remove_vpp_config(self):
402         self._test.vapi.gbp_route_domain_del(self.rd_id)
403
404     def __str__(self):
405         return self.object_id()
406
407     def object_id(self):
408         return "gbp-route-domain:[%d]" % (self.rd_id)
409
410     def query_vpp_config(self):
411         rds = self._test.vapi.gbp_route_domain_dump()
412         for rd in rds:
413             if rd.rd.rd_id == self.rd_id:
414                 return True
415         return False
416
417
418 class VppGbpContractNextHop():
419     def __init__(self, mac, bd, ip, rd):
420         self.mac = mac
421         self.ip = ip
422         self.bd = bd
423         self.rd = rd
424
425     def encode(self):
426         return {'ip': self.ip.encode(),
427                 'mac': self.mac.packed,
428                 'bd_id': self.bd.bd.bd_id,
429                 'rd_id': self.rd.rd_id}
430
431
432 class VppGbpContractRule():
433     def __init__(self, action, hash_mode, nhs=[]):
434         self.action = action
435         self.hash_mode = hash_mode
436         self.nhs = nhs
437
438     def encode(self):
439         nhs = []
440         for nh in self.nhs:
441             nhs.append(nh.encode())
442         while len(nhs) < 8:
443             nhs.append({})
444         return {'action': self.action,
445                 'nh_set': {
446                     'hash_mode': self.hash_mode,
447                     'n_nhs': len(self.nhs),
448                     'nhs': nhs}}
449
450
451 class VppGbpContract(VppObject):
452     """
453     GBP Contract
454     """
455
456     def __init__(self, test, src_epg, dst_epg, acl_index,
457                  rules, allowed_ethertypes):
458         self._test = test
459         self.acl_index = acl_index
460         self.src_epg = src_epg
461         self.dst_epg = dst_epg
462         self.rules = rules
463         self.allowed_ethertypes = allowed_ethertypes
464         while (len(self.allowed_ethertypes) < 16):
465             self.allowed_ethertypes.append(0)
466
467     def add_vpp_config(self):
468         rules = []
469         for r in self.rules:
470             rules.append(r.encode())
471         self._test.vapi.gbp_contract_add_del(
472             1,
473             self.src_epg.sclass,
474             self.dst_epg.sclass,
475             self.acl_index,
476             rules,
477             self.allowed_ethertypes)
478         self._test.registry.register(self, self._test.logger)
479
480     def remove_vpp_config(self):
481         self._test.vapi.gbp_contract_add_del(
482             0,
483             self.src_epg.sclass,
484             self.dst_epg.sclass,
485             self.acl_index,
486             [],
487             self.allowed_ethertypes)
488
489     def __str__(self):
490         return self.object_id()
491
492     def object_id(self):
493         return "gbp-contract:[%d:%s:%d]" % (self.src_epg.sclass,
494                                             self.dst_epg.sclass,
495                                             self.acl_index)
496
497     def query_vpp_config(self):
498         cs = self._test.vapi.gbp_contract_dump()
499         for c in cs:
500             if c.contract.sclass == self.src_epg.sclass \
501                and c.contract.dclass == self.dst_epg.sclass:
502                 return True
503         return False
504
505
506 class VppGbpVxlanTunnel(VppInterface):
507     """
508     GBP VXLAN tunnel
509     """
510
511     def __init__(self, test, vni, bd_rd_id, mode, src):
512         super(VppGbpVxlanTunnel, self).__init__(test)
513         self._test = test
514         self.vni = vni
515         self.bd_rd_id = bd_rd_id
516         self.mode = mode
517         self.src = src
518
519     def add_vpp_config(self):
520         r = self._test.vapi.gbp_vxlan_tunnel_add(
521             self.vni,
522             self.bd_rd_id,
523             self.mode,
524             self.src)
525         self.set_sw_if_index(r.sw_if_index)
526         self._test.registry.register(self, self._test.logger)
527
528     def remove_vpp_config(self):
529         self._test.vapi.gbp_vxlan_tunnel_del(self.vni)
530
531     def __str__(self):
532         return self.object_id()
533
534     def object_id(self):
535         return "gbp-vxlan:%d" % (self.sw_if_index)
536
537     def query_vpp_config(self):
538         return find_gbp_vxlan(self._test, self.vni)
539
540
541 class VppGbpAcl(VppObject):
542     """
543     GBP Acl
544     """
545
546     def __init__(self, test):
547         self._test = test
548         self.acl_index = 4294967295
549
550     def create_rule(self, is_ipv6=0, permit_deny=0, proto=-1,
551                     s_prefix=0, s_ip=b'\x00\x00\x00\x00', sport_from=0,
552                     sport_to=65535, d_prefix=0, d_ip=b'\x00\x00\x00\x00',
553                     dport_from=0, dport_to=65535):
554         if proto == -1 or proto == 0:
555             sport_to = 0
556             dport_to = sport_to
557         elif proto == 1 or proto == 58:
558             sport_to = 255
559             dport_to = sport_to
560         rule = ({'is_permit': permit_deny, 'is_ipv6': is_ipv6, 'proto': proto,
561                  'srcport_or_icmptype_first': sport_from,
562                  'srcport_or_icmptype_last': sport_to,
563                  'src_ip_prefix_len': s_prefix,
564                  'src_ip_addr': s_ip,
565                  'dstport_or_icmpcode_first': dport_from,
566                  'dstport_or_icmpcode_last': dport_to,
567                  'dst_ip_prefix_len': d_prefix,
568                  'dst_ip_addr': d_ip})
569         return rule
570
571     def add_vpp_config(self, rules):
572
573         reply = self._test.vapi.acl_add_replace(self.acl_index,
574                                                 r=rules,
575                                                 tag=b'GBPTest')
576         self.acl_index = reply.acl_index
577         return self.acl_index
578
579     def remove_vpp_config(self):
580         self._test.vapi.acl_del(self.acl_index)
581
582     def __str__(self):
583         return self.object_id()
584
585     def object_id(self):
586         return "gbp-acl:[%d]" % (self.acl_index)
587
588     def query_vpp_config(self):
589         cs = self._test.vapi.acl_dump()
590         for c in cs:
591             if c.acl_index == self.acl_index:
592                 return True
593         return False
594
595
596 class TestGBP(VppTestCase):
597     """ GBP Test Case """
598
599     def setUp(self):
600         super(TestGBP, self).setUp()
601
602         self.create_pg_interfaces(range(9))
603         self.create_loopback_interfaces(8)
604
605         self.router_mac = MACAddress("00:11:22:33:44:55")
606
607         for i in self.pg_interfaces:
608             i.admin_up()
609         for i in self.lo_interfaces:
610             i.admin_up()
611
612     def tearDown(self):
613         for i in self.pg_interfaces:
614             i.admin_down()
615
616         super(TestGBP, self).tearDown()
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_natted(self, src, tx, dst, src_ip):
649         rx = self.send_and_expect(src, tx, dst)
650
651         for r in rx:
652             self.assertEqual(r[Ether].src, tx[0][Ether].src)
653             self.assertEqual(r[Ether].dst, tx[0][Ether].dst)
654             self.assertEqual(r[IP].src, src_ip)
655             self.assertEqual(r[IP].dst, tx[0][IP].dst)
656         return rx
657
658     def send_and_expect_natted6(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[IPv6].src, src_ip)
665             self.assertEqual(r[IPv6].dst, tx[0][IPv6].dst)
666         return rx
667
668     def send_and_expect_unnatted(self, src, tx, dst, dst_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[IP].dst, dst_ip)
675             self.assertEqual(r[IP].src, tx[0][IP].src)
676         return rx
677
678     def send_and_expect_unnatted6(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[IPv6].dst, dst_ip)
685             self.assertEqual(r[IPv6].src, tx[0][IPv6].src)
686         return rx
687
688     def send_and_expect_double_natted(self, src, tx, dst, src_ip, dst_ip):
689         rx = self.send_and_expect(src, tx, dst)
690
691         for r in rx:
692             self.assertEqual(r[Ether].src, str(self.router_mac))
693             self.assertEqual(r[Ether].dst, dst.remote_mac)
694             self.assertEqual(r[IP].dst, dst_ip)
695             self.assertEqual(r[IP].src, src_ip)
696         return rx
697
698     def send_and_expect_double_natted6(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[IPv6].dst, dst_ip)
705             self.assertEqual(r[IPv6].src, src_ip)
706         return rx
707
708     def test_gbp(self):
709         """ Group Based Policy """
710
711         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
712
713         #
714         # Bridge Domains
715         #
716         bd1 = VppBridgeDomain(self, 1)
717         bd2 = VppBridgeDomain(self, 2)
718         bd20 = VppBridgeDomain(self, 20)
719
720         bd1.add_vpp_config()
721         bd2.add_vpp_config()
722         bd20.add_vpp_config()
723
724         gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0)
725         gbd2 = VppGbpBridgeDomain(self, bd2, self.loop1)
726         gbd20 = VppGbpBridgeDomain(self, bd20, self.loop2)
727
728         gbd1.add_vpp_config()
729         gbd2.add_vpp_config()
730         gbd20.add_vpp_config()
731
732         #
733         # Route Domains
734         #
735         gt4 = VppIpTable(self, 0)
736         gt4.add_vpp_config()
737         gt6 = VppIpTable(self, 0, is_ip6=True)
738         gt6.add_vpp_config()
739         nt4 = VppIpTable(self, 20)
740         nt4.add_vpp_config()
741         nt6 = VppIpTable(self, 20, is_ip6=True)
742         nt6.add_vpp_config()
743
744         rd0 = VppGbpRouteDomain(self, 0, gt4, gt6, None, None)
745         rd20 = VppGbpRouteDomain(self, 20, nt4, nt6, None, None)
746
747         rd0.add_vpp_config()
748         rd20.add_vpp_config()
749
750         #
751         # 3 EPGs, 2 of which share a BD.
752         # 2 NAT EPGs, one for floating-IP subnets, the other for internet
753         #
754         epgs = [VppGbpEndpointGroup(self, 220, 1220, rd0, gbd1,
755                                     self.pg4, self.loop0,
756                                     "10.0.0.128", "2001:10::128"),
757                 VppGbpEndpointGroup(self, 221, 1221, rd0, gbd1,
758                                     self.pg5, self.loop0,
759                                     "10.0.1.128", "2001:10:1::128"),
760                 VppGbpEndpointGroup(self, 222, 1222, rd0, gbd2,
761                                     self.pg6, self.loop1,
762                                     "10.0.2.128", "2001:10:2::128"),
763                 VppGbpEndpointGroup(self, 333, 1333, rd20, gbd20,
764                                     self.pg7, self.loop2,
765                                     "11.0.0.128", "3001::128"),
766                 VppGbpEndpointGroup(self, 444, 1444, rd20, gbd20,
767                                     self.pg8, self.loop2,
768                                     "11.0.0.129", "3001::129")]
769         recircs = [VppGbpRecirc(self, epgs[0], self.loop3),
770                    VppGbpRecirc(self, epgs[1], self.loop4),
771                    VppGbpRecirc(self, epgs[2], self.loop5),
772                    VppGbpRecirc(self, epgs[3], self.loop6, is_ext=True),
773                    VppGbpRecirc(self, epgs[4], self.loop7, is_ext=True)]
774
775         epg_nat = epgs[3]
776         recirc_nat = recircs[3]
777
778         #
779         # 4 end-points, 2 in the same subnet, 3 in the same BD
780         #
781         eps = [VppGbpEndpoint(self, self.pg0,
782                               epgs[0], recircs[0],
783                               "10.0.0.1", "11.0.0.1",
784                               "2001:10::1", "3001::1"),
785                VppGbpEndpoint(self, self.pg1,
786                               epgs[0], recircs[0],
787                               "10.0.0.2", "11.0.0.2",
788                               "2001:10::2", "3001::2"),
789                VppGbpEndpoint(self, self.pg2,
790                               epgs[1], recircs[1],
791                               "10.0.1.1", "11.0.0.3",
792                               "2001:10:1::1", "3001::3"),
793                VppGbpEndpoint(self, self.pg3,
794                               epgs[2], recircs[2],
795                               "10.0.2.1", "11.0.0.4",
796                               "2001:10:2::1", "3001::4")]
797
798         #
799         # Config related to each of the EPGs
800         #
801         for epg in epgs:
802             # IP config on the BVI interfaces
803             if epg != epgs[1] and epg != epgs[4]:
804                 VppIpInterfaceBind(self, epg.bvi, epg.rd.t4).add_vpp_config()
805                 VppIpInterfaceBind(self, epg.bvi, epg.rd.t6).add_vpp_config()
806                 self.vapi.sw_interface_set_mac_address(
807                     epg.bvi.sw_if_index,
808                     self.router_mac.packed)
809
810                 # The BVIs are NAT inside interfaces
811                 self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
812                                                           is_inside=1,
813                                                           is_add=1)
814                 self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
815                                                   is_inside=1,
816                                                   is_add=1)
817
818             if_ip4 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip4, 32)
819             if_ip6 = VppIpInterfaceAddress(self, epg.bvi, epg.bvi_ip6, 128)
820             if_ip4.add_vpp_config()
821             if_ip6.add_vpp_config()
822
823             # EPG uplink interfaces in the RD
824             VppIpInterfaceBind(self, epg.uplink, epg.rd.t4).add_vpp_config()
825             VppIpInterfaceBind(self, epg.uplink, epg.rd.t6).add_vpp_config()
826
827             # add the BD ARP termination entry for BVI IP
828             epg.bd_arp_ip4 = VppBridgeDomainArpEntry(self, epg.bd.bd,
829                                                      str(self.router_mac),
830                                                      epg.bvi_ip4)
831             epg.bd_arp_ip6 = VppBridgeDomainArpEntry(self, epg.bd.bd,
832                                                      str(self.router_mac),
833                                                      epg.bvi_ip6)
834             epg.bd_arp_ip4.add_vpp_config()
835             epg.bd_arp_ip6.add_vpp_config()
836
837             # EPG in VPP
838             epg.add_vpp_config()
839
840         for recirc in recircs:
841             # EPG's ingress recirculation interface maps to its RD
842             VppIpInterfaceBind(self, recirc.recirc,
843                                recirc.epg.rd.t4).add_vpp_config()
844             VppIpInterfaceBind(self, recirc.recirc,
845                                recirc.epg.rd.t6).add_vpp_config()
846
847             self.vapi.nat44_interface_add_del_feature(
848                 recirc.recirc.sw_if_index,
849                 is_inside=0,
850                 is_add=1)
851             self.vapi.nat66_add_del_interface(
852                 recirc.recirc.sw_if_index,
853                 is_inside=0,
854                 is_add=1)
855
856             recirc.add_vpp_config()
857
858         for recirc in recircs:
859             self.assertTrue(find_bridge_domain_port(self,
860                                                     recirc.epg.bd.bd.bd_id,
861                                                     recirc.recirc.sw_if_index))
862
863         for ep in eps:
864             self.pg_enable_capture(self.pg_interfaces)
865             self.pg_start()
866             #
867             # routes to the endpoints. We need these since there are no
868             # adj-fibs due to the fact the the BVI address has /32 and
869             # the subnet is not attached.
870             #
871             for (ip, fip) in zip(ep.ips, ep.fips):
872                 # Add static mappings for each EP from the 10/8 to 11/8 network
873                 if ip.af == AF_INET:
874                     self.vapi.nat44_add_del_static_mapping(ip.bytes,
875                                                            fip.bytes,
876                                                            vrf_id=0,
877                                                            addr_only=1)
878                 else:
879                     self.vapi.nat66_add_del_static_mapping(ip.bytes,
880                                                            fip.bytes,
881                                                            vrf_id=0)
882
883             # VPP EP create ...
884             ep.add_vpp_config()
885
886             self.logger.info(self.vapi.cli("sh gbp endpoint"))
887
888             # ... results in a Gratuitous ARP/ND on the EPG's uplink
889             rx = ep.epg.uplink.get_capture(len(ep.ips), timeout=0.2)
890
891             for ii, ip in enumerate(ep.ips):
892                 p = rx[ii]
893
894                 if ip.is_ip6:
895                     self.assertTrue(p.haslayer(ICMPv6ND_NA))
896                     self.assertEqual(p[ICMPv6ND_NA].tgt, ip.address)
897                 else:
898                     self.assertTrue(p.haslayer(ARP))
899                     self.assertEqual(p[ARP].psrc, ip.address)
900                     self.assertEqual(p[ARP].pdst, ip.address)
901
902             # add the BD ARP termination entry for floating IP
903             for fip in ep.fips:
904                 ba = VppBridgeDomainArpEntry(self, epg_nat.bd.bd, ep.mac, fip)
905                 ba.add_vpp_config()
906
907                 # floating IPs route via EPG recirc
908                 r = VppIpRoute(self, fip.address, fip.length,
909                                [VppRoutePath(fip.address,
910                                              ep.recirc.recirc.sw_if_index,
911                                              is_dvr=1,
912                                              proto=fip.dpo_proto)],
913                                table_id=20,
914                                is_ip6=fip.is_ip6)
915                 r.add_vpp_config()
916
917             # L2 FIB entries in the NAT EPG BD to bridge the packets from
918             # the outside direct to the internal EPG
919             lf = VppL2FibEntry(self, epg_nat.bd.bd, ep.mac,
920                                ep.recirc.recirc, bvi_mac=0)
921             lf.add_vpp_config()
922
923         #
924         # ARP packets for unknown IP are sent to the EPG uplink
925         #
926         pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
927                          src=self.pg0.remote_mac) /
928                    ARP(op="who-has",
929                        hwdst="ff:ff:ff:ff:ff:ff",
930                        hwsrc=self.pg0.remote_mac,
931                        pdst="10.0.0.88",
932                        psrc="10.0.0.99"))
933
934         self.vapi.cli("clear trace")
935         self.pg0.add_stream(pkt_arp)
936
937         self.pg_enable_capture(self.pg_interfaces)
938         self.pg_start()
939
940         rxd = epgs[0].uplink.get_capture(1)
941
942         #
943         # ARP/ND packets get a response
944         #
945         pkt_arp = (Ether(dst="ff:ff:ff:ff:ff:ff",
946                          src=self.pg0.remote_mac) /
947                    ARP(op="who-has",
948                        hwdst="ff:ff:ff:ff:ff:ff",
949                        hwsrc=self.pg0.remote_mac,
950                        pdst=epgs[0].bvi_ip4.address,
951                        psrc=eps[0].ip4.address))
952
953         self.send_and_expect(self.pg0, [pkt_arp], self.pg0)
954
955         nsma = in6_getnsma(inet_pton(AF_INET6, eps[0].ip6.address))
956         d = inet_ntop(AF_INET6, nsma)
957         pkt_nd = (Ether(dst=in6_getnsmac(nsma),
958                         src=self.pg0.remote_mac) /
959                   IPv6(dst=d, src=eps[0].ip6.address) /
960                   ICMPv6ND_NS(tgt=epgs[0].bvi_ip6.address) /
961                   ICMPv6NDOptSrcLLAddr(lladdr=self.pg0.remote_mac))
962         self.send_and_expect(self.pg0, [pkt_nd], self.pg0)
963
964         #
965         # broadcast packets are flooded
966         #
967         pkt_bcast = (Ether(dst="ff:ff:ff:ff:ff:ff",
968                            src=self.pg0.remote_mac) /
969                      IP(src=eps[0].ip4.address, dst="232.1.1.1") /
970                      UDP(sport=1234, dport=1234) /
971                      Raw('\xa5' * 100))
972
973         self.vapi.cli("clear trace")
974         self.pg0.add_stream(pkt_bcast)
975
976         self.pg_enable_capture(self.pg_interfaces)
977         self.pg_start()
978
979         rxd = eps[1].itf.get_capture(1)
980         self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
981         rxd = epgs[0].uplink.get_capture(1)
982         self.assertEqual(rxd[0][Ether].dst, pkt_bcast[Ether].dst)
983
984         #
985         # packets to non-local L3 destinations dropped
986         #
987         pkt_intra_epg_220_ip4 = (Ether(src=self.pg0.remote_mac,
988                                        dst=str(self.router_mac)) /
989                                  IP(src=eps[0].ip4.address,
990                                     dst="10.0.0.99") /
991                                  UDP(sport=1234, dport=1234) /
992                                  Raw('\xa5' * 100))
993         pkt_inter_epg_222_ip4 = (Ether(src=self.pg0.remote_mac,
994                                        dst=str(self.router_mac)) /
995                                  IP(src=eps[0].ip4.address,
996                                     dst="10.0.1.99") /
997                                  UDP(sport=1234, dport=1234) /
998                                  Raw('\xa5' * 100))
999
1000         self.send_and_assert_no_replies(self.pg0, pkt_intra_epg_220_ip4 * 65)
1001
1002         pkt_inter_epg_222_ip6 = (Ether(src=self.pg0.remote_mac,
1003                                        dst=str(self.router_mac)) /
1004                                  IPv6(src=eps[0].ip6.address,
1005                                       dst="2001:10::99") /
1006                                  UDP(sport=1234, dport=1234) /
1007                                  Raw('\xa5' * 100))
1008         self.send_and_assert_no_replies(self.pg0, pkt_inter_epg_222_ip6 * 65)
1009
1010         #
1011         # Add the subnet routes
1012         #
1013         s41 = VppGbpSubnet(
1014             self, rd0, "10.0.0.0", 24,
1015             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1016         s42 = VppGbpSubnet(
1017             self, rd0, "10.0.1.0", 24,
1018             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1019         s43 = VppGbpSubnet(
1020             self, rd0, "10.0.2.0", 24,
1021             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1022         s61 = VppGbpSubnet(
1023             self, rd0, "2001:10::1", 64,
1024             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1025         s62 = VppGbpSubnet(
1026             self, rd0, "2001:10:1::1", 64,
1027             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1028         s63 = VppGbpSubnet(
1029             self, rd0, "2001:10:2::1", 64,
1030             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_INTERNAL)
1031         s41.add_vpp_config()
1032         s42.add_vpp_config()
1033         s43.add_vpp_config()
1034         s61.add_vpp_config()
1035         s62.add_vpp_config()
1036         s63.add_vpp_config()
1037
1038         self.send_and_expect_bridged(eps[0].itf,
1039                                      pkt_intra_epg_220_ip4 * 65,
1040                                      eps[0].epg.uplink)
1041         self.send_and_expect_bridged(eps[0].itf,
1042                                      pkt_inter_epg_222_ip4 * 65,
1043                                      eps[0].epg.uplink)
1044         self.send_and_expect_bridged6(eps[0].itf,
1045                                       pkt_inter_epg_222_ip6 * 65,
1046                                       eps[0].epg.uplink)
1047
1048         self.logger.info(self.vapi.cli("sh ip fib 11.0.0.2"))
1049         self.logger.info(self.vapi.cli("sh gbp endpoint-group"))
1050         self.logger.info(self.vapi.cli("sh gbp endpoint"))
1051         self.logger.info(self.vapi.cli("sh gbp recirc"))
1052         self.logger.info(self.vapi.cli("sh int"))
1053         self.logger.info(self.vapi.cli("sh int addr"))
1054         self.logger.info(self.vapi.cli("sh int feat loop6"))
1055         self.logger.info(self.vapi.cli("sh vlib graph ip4-gbp-src-classify"))
1056         self.logger.info(self.vapi.cli("sh int feat loop3"))
1057         self.logger.info(self.vapi.cli("sh int feat pg0"))
1058
1059         #
1060         # Packet destined to unknown unicast is sent on the epg uplink ...
1061         #
1062         pkt_intra_epg_220_to_uplink = (Ether(src=self.pg0.remote_mac,
1063                                              dst="00:00:00:33:44:55") /
1064                                        IP(src=eps[0].ip4.address,
1065                                           dst="10.0.0.99") /
1066                                        UDP(sport=1234, dport=1234) /
1067                                        Raw('\xa5' * 100))
1068
1069         self.send_and_expect_bridged(eps[0].itf,
1070                                      pkt_intra_epg_220_to_uplink * 65,
1071                                      eps[0].epg.uplink)
1072         # ... and nowhere else
1073         self.pg1.get_capture(0, timeout=0.1)
1074         self.pg1.assert_nothing_captured(remark="Flood onto other VMS")
1075
1076         pkt_intra_epg_221_to_uplink = (Ether(src=self.pg2.remote_mac,
1077                                              dst="00:00:00:33:44:66") /
1078                                        IP(src=eps[0].ip4.address,
1079                                           dst="10.0.0.99") /
1080                                        UDP(sport=1234, dport=1234) /
1081                                        Raw('\xa5' * 100))
1082
1083         self.send_and_expect_bridged(eps[2].itf,
1084                                      pkt_intra_epg_221_to_uplink * 65,
1085                                      eps[2].epg.uplink)
1086
1087         #
1088         # Packets from the uplink are forwarded in the absence of a contract
1089         #
1090         pkt_intra_epg_220_from_uplink = (Ether(src="00:00:00:33:44:55",
1091                                                dst=self.pg0.remote_mac) /
1092                                          IP(src=eps[0].ip4.address,
1093                                             dst="10.0.0.99") /
1094                                          UDP(sport=1234, dport=1234) /
1095                                          Raw('\xa5' * 100))
1096
1097         self.send_and_expect_bridged(self.pg4,
1098                                      pkt_intra_epg_220_from_uplink * 65,
1099                                      self.pg0)
1100
1101         #
1102         # in the absence of policy, endpoints in the same EPG
1103         # can communicate
1104         #
1105         pkt_intra_epg = (Ether(src=self.pg0.remote_mac,
1106                                dst=self.pg1.remote_mac) /
1107                          IP(src=eps[0].ip4.address,
1108                             dst=eps[1].ip4.address) /
1109                          UDP(sport=1234, dport=1234) /
1110                          Raw('\xa5' * 100))
1111
1112         self.send_and_expect_bridged(self.pg0, pkt_intra_epg * 65, self.pg1)
1113
1114         #
1115         # in the abscense of policy, endpoints in the different EPG
1116         # cannot communicate
1117         #
1118         pkt_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
1119                                           dst=self.pg2.remote_mac) /
1120                                     IP(src=eps[0].ip4.address,
1121                                        dst=eps[2].ip4.address) /
1122                                     UDP(sport=1234, dport=1234) /
1123                                     Raw('\xa5' * 100))
1124         pkt_inter_epg_221_to_220 = (Ether(src=self.pg2.remote_mac,
1125                                           dst=self.pg0.remote_mac) /
1126                                     IP(src=eps[2].ip4.address,
1127                                        dst=eps[0].ip4.address) /
1128                                     UDP(sport=1234, dport=1234) /
1129                                     Raw('\xa5' * 100))
1130         pkt_inter_epg_220_to_222 = (Ether(src=self.pg0.remote_mac,
1131                                           dst=str(self.router_mac)) /
1132                                     IP(src=eps[0].ip4.address,
1133                                        dst=eps[3].ip4.address) /
1134                                     UDP(sport=1234, dport=1234) /
1135                                     Raw('\xa5' * 100))
1136
1137         self.send_and_assert_no_replies(eps[0].itf,
1138                                         pkt_inter_epg_220_to_221 * 65)
1139         self.send_and_assert_no_replies(eps[0].itf,
1140                                         pkt_inter_epg_220_to_222 * 65)
1141
1142         #
1143         # A uni-directional contract from EPG 220 -> 221
1144         #
1145         acl = VppGbpAcl(self)
1146         rule = acl.create_rule(permit_deny=1, proto=17)
1147         rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17)
1148         acl_index = acl.add_vpp_config([rule, rule2])
1149         c1 = VppGbpContract(
1150             self, epgs[0], epgs[1], acl_index,
1151             [VppGbpContractRule(
1152                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1153                 []),
1154              VppGbpContractRule(
1155                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1156                  [])],
1157             [ETH_P_IP, ETH_P_IPV6])
1158         c1.add_vpp_config()
1159
1160         self.send_and_expect_bridged(eps[0].itf,
1161                                      pkt_inter_epg_220_to_221 * 65,
1162                                      eps[2].itf)
1163         self.send_and_assert_no_replies(eps[0].itf,
1164                                         pkt_inter_epg_220_to_222 * 65)
1165
1166         #
1167         # contract for the return direction
1168         #
1169         c2 = VppGbpContract(
1170             self, epgs[1], epgs[0], acl_index,
1171             [VppGbpContractRule(
1172                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1173                 []),
1174              VppGbpContractRule(
1175                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1176                  [])],
1177             [ETH_P_IP, ETH_P_IPV6])
1178         c2.add_vpp_config()
1179
1180         self.send_and_expect_bridged(eps[0].itf,
1181                                      pkt_inter_epg_220_to_221 * 65,
1182                                      eps[2].itf)
1183         self.send_and_expect_bridged(eps[2].itf,
1184                                      pkt_inter_epg_221_to_220 * 65,
1185                                      eps[0].itf)
1186
1187         #
1188         # the contract does not allow non-IP
1189         #
1190         pkt_non_ip_inter_epg_220_to_221 = (Ether(src=self.pg0.remote_mac,
1191                                                  dst=self.pg2.remote_mac) /
1192                                            ARP())
1193         self.send_and_assert_no_replies(eps[0].itf,
1194                                         pkt_non_ip_inter_epg_220_to_221 * 17)
1195
1196         #
1197         # check that inter group is still disabled for the groups
1198         # not in the contract.
1199         #
1200         self.send_and_assert_no_replies(eps[0].itf,
1201                                         pkt_inter_epg_220_to_222 * 65)
1202
1203         #
1204         # A uni-directional contract from EPG 220 -> 222 'L3 routed'
1205         #
1206         c3 = VppGbpContract(
1207             self, epgs[0], epgs[2], acl_index,
1208             [VppGbpContractRule(
1209                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1210                 []),
1211              VppGbpContractRule(
1212                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1213                  [])],
1214             [ETH_P_IP, ETH_P_IPV6])
1215         c3.add_vpp_config()
1216
1217         self.logger.info(self.vapi.cli("sh gbp contract"))
1218
1219         self.send_and_expect_routed(eps[0].itf,
1220                                     pkt_inter_epg_220_to_222 * 65,
1221                                     eps[3].itf,
1222                                     str(self.router_mac))
1223
1224         #
1225         # remove both contracts, traffic stops in both directions
1226         #
1227         c2.remove_vpp_config()
1228         c1.remove_vpp_config()
1229         c3.remove_vpp_config()
1230         acl.remove_vpp_config()
1231
1232         self.send_and_assert_no_replies(eps[2].itf,
1233                                         pkt_inter_epg_221_to_220 * 65)
1234         self.send_and_assert_no_replies(eps[0].itf,
1235                                         pkt_inter_epg_220_to_221 * 65)
1236         self.send_and_expect_bridged(eps[0].itf,
1237                                      pkt_intra_epg * 65,
1238                                      eps[1].itf)
1239
1240         #
1241         # EPs to the outside world
1242         #
1243
1244         # in the EP's RD an external subnet via the NAT EPG's recirc
1245         se1 = VppGbpSubnet(
1246             self, rd0, "0.0.0.0", 0,
1247             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1248             sw_if_index=recirc_nat.recirc.sw_if_index,
1249             epg=epg_nat)
1250         se2 = VppGbpSubnet(
1251             self, rd0, "11.0.0.0", 8,
1252             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1253             sw_if_index=recirc_nat.recirc.sw_if_index,
1254             epg=epg_nat)
1255         se16 = VppGbpSubnet(
1256             self, rd0, "::", 0,
1257             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1258             sw_if_index=recirc_nat.recirc.sw_if_index,
1259             epg=epg_nat)
1260         # in the NAT RD an external subnet via the NAT EPG's uplink
1261         se3 = VppGbpSubnet(
1262             self, rd20, "0.0.0.0", 0,
1263             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1264             sw_if_index=epg_nat.uplink.sw_if_index,
1265             epg=epg_nat)
1266         se36 = VppGbpSubnet(
1267             self, rd20, "::", 0,
1268             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1269             sw_if_index=epg_nat.uplink.sw_if_index,
1270             epg=epg_nat)
1271         se4 = VppGbpSubnet(
1272             self, rd20, "11.0.0.0", 8,
1273             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_STITCHED_EXTERNAL,
1274             sw_if_index=epg_nat.uplink.sw_if_index,
1275             epg=epg_nat)
1276         se1.add_vpp_config()
1277         se2.add_vpp_config()
1278         se16.add_vpp_config()
1279         se3.add_vpp_config()
1280         se36.add_vpp_config()
1281         se4.add_vpp_config()
1282
1283         self.logger.info(self.vapi.cli("sh ip fib 0.0.0.0/0"))
1284         self.logger.info(self.vapi.cli("sh ip fib 11.0.0.1"))
1285         self.logger.info(self.vapi.cli("sh ip6 fib ::/0"))
1286         self.logger.info(self.vapi.cli("sh ip6 fib %s" %
1287                                        eps[0].fip6))
1288
1289         #
1290         # From an EP to an outside addess: IN2OUT
1291         #
1292         pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
1293                                              dst=str(self.router_mac)) /
1294                                        IP(src=eps[0].ip4.address,
1295                                           dst="1.1.1.1") /
1296                                        UDP(sport=1234, dport=1234) /
1297                                        Raw('\xa5' * 100))
1298
1299         # no policy yet
1300         self.send_and_assert_no_replies(eps[0].itf,
1301                                         pkt_inter_epg_220_to_global * 65)
1302
1303         acl2 = VppGbpAcl(self)
1304         rule = acl2.create_rule(permit_deny=1, proto=17, sport_from=1234,
1305                                 sport_to=1234, dport_from=1234, dport_to=1234)
1306         rule2 = acl2.create_rule(is_ipv6=1, permit_deny=1, proto=17,
1307                                  sport_from=1234, sport_to=1234,
1308                                  dport_from=1234, dport_to=1234)
1309
1310         acl_index2 = acl2.add_vpp_config([rule, rule2])
1311         c4 = VppGbpContract(
1312             self, epgs[0], epgs[3], acl_index2,
1313             [VppGbpContractRule(
1314                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1315                 []),
1316              VppGbpContractRule(
1317                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1318                  [])],
1319             [ETH_P_IP, ETH_P_IPV6])
1320         c4.add_vpp_config()
1321
1322         self.send_and_expect_natted(eps[0].itf,
1323                                     pkt_inter_epg_220_to_global * 65,
1324                                     self.pg7,
1325                                     eps[0].fip4.address)
1326
1327         pkt_inter_epg_220_to_global = (Ether(src=self.pg0.remote_mac,
1328                                              dst=str(self.router_mac)) /
1329                                        IPv6(src=eps[0].ip6.address,
1330                                             dst="6001::1") /
1331                                        UDP(sport=1234, dport=1234) /
1332                                        Raw('\xa5' * 100))
1333
1334         self.send_and_expect_natted6(self.pg0,
1335                                      pkt_inter_epg_220_to_global * 65,
1336                                      self.pg7,
1337                                      eps[0].fip6.address)
1338
1339         #
1340         # From a global address to an EP: OUT2IN
1341         #
1342         pkt_inter_epg_220_from_global = (Ether(src=str(self.router_mac),
1343                                                dst=self.pg0.remote_mac) /
1344                                          IP(dst=eps[0].fip4.address,
1345                                             src="1.1.1.1") /
1346                                          UDP(sport=1234, dport=1234) /
1347                                          Raw('\xa5' * 100))
1348
1349         self.send_and_assert_no_replies(self.pg7,
1350                                         pkt_inter_epg_220_from_global * 65)
1351
1352         c5 = VppGbpContract(
1353             self, epgs[3], epgs[0], acl_index2,
1354             [VppGbpContractRule(
1355                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1356                 []),
1357              VppGbpContractRule(
1358                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1359                  [])],
1360             [ETH_P_IP, ETH_P_IPV6])
1361         c5.add_vpp_config()
1362
1363         self.send_and_expect_unnatted(self.pg7,
1364                                       pkt_inter_epg_220_from_global * 65,
1365                                       eps[0].itf,
1366                                       eps[0].ip4.address)
1367
1368         pkt_inter_epg_220_from_global = (Ether(src=str(self.router_mac),
1369                                                dst=self.pg0.remote_mac) /
1370                                          IPv6(dst=eps[0].fip6.address,
1371                                               src="6001::1") /
1372                                          UDP(sport=1234, dport=1234) /
1373                                          Raw('\xa5' * 100))
1374
1375         self.send_and_expect_unnatted6(self.pg7,
1376                                        pkt_inter_epg_220_from_global * 65,
1377                                        eps[0].itf,
1378                                        eps[0].ip6.address)
1379
1380         #
1381         # From a local VM to another local VM using resp. public addresses:
1382         #  IN2OUT2IN
1383         #
1384         pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
1385                                           dst=str(self.router_mac)) /
1386                                     IP(src=eps[0].ip4.address,
1387                                        dst=eps[1].fip4.address) /
1388                                     UDP(sport=1234, dport=1234) /
1389                                     Raw('\xa5' * 100))
1390
1391         self.send_and_expect_double_natted(eps[0].itf,
1392                                            pkt_intra_epg_220_global * 65,
1393                                            eps[1].itf,
1394                                            eps[0].fip4.address,
1395                                            eps[1].ip4.address)
1396
1397         pkt_intra_epg_220_global = (Ether(src=self.pg0.remote_mac,
1398                                           dst=str(self.router_mac)) /
1399                                     IPv6(src=eps[0].ip6.address,
1400                                          dst=eps[1].fip6.address) /
1401                                     UDP(sport=1234, dport=1234) /
1402                                     Raw('\xa5' * 100))
1403
1404         self.send_and_expect_double_natted6(eps[0].itf,
1405                                             pkt_intra_epg_220_global * 65,
1406                                             eps[1].itf,
1407                                             eps[0].fip6.address,
1408                                             eps[1].ip6.address)
1409
1410         #
1411         # cleanup
1412         #
1413         for ep in eps:
1414             # del static mappings for each EP from the 10/8 to 11/8 network
1415             self.vapi.nat44_add_del_static_mapping(ep.ip4.bytes,
1416                                                    ep.fip4.bytes,
1417                                                    vrf_id=0,
1418                                                    addr_only=1,
1419                                                    is_add=0)
1420             self.vapi.nat66_add_del_static_mapping(ep.ip6.bytes,
1421                                                    ep.fip6.bytes,
1422                                                    vrf_id=0,
1423                                                    is_add=0)
1424
1425         for epg in epgs:
1426             # IP config on the BVI interfaces
1427             if epg != epgs[0] and epg != epgs[3]:
1428                 self.vapi.nat44_interface_add_del_feature(epg.bvi.sw_if_index,
1429                                                           is_inside=1,
1430                                                           is_add=0)
1431                 self.vapi.nat66_add_del_interface(epg.bvi.sw_if_index,
1432                                                   is_inside=1,
1433                                                   is_add=0)
1434
1435         for recirc in recircs:
1436             self.vapi.nat44_interface_add_del_feature(
1437                 recirc.recirc.sw_if_index,
1438                 is_inside=0,
1439                 is_add=0)
1440             self.vapi.nat66_add_del_interface(
1441                 recirc.recirc.sw_if_index,
1442                 is_inside=0,
1443                 is_add=0)
1444
1445     def wait_for_ep_timeout(self, sw_if_index=None, ip=None, mac=None,
1446                             n_tries=100, s_time=1):
1447         while (n_tries):
1448             if not find_gbp_endpoint(self, sw_if_index, ip, mac):
1449                 return True
1450             n_tries = n_tries - 1
1451             self.sleep(s_time)
1452         self.assertFalse(find_gbp_endpoint(self, sw_if_index, ip, mac))
1453         return False
1454
1455     def test_gbp_learn_l2(self):
1456         """ GBP L2 Endpoint Learning """
1457
1458         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
1459         learnt = [{'mac': '00:00:11:11:11:01',
1460                    'ip': '10.0.0.1',
1461                    'ip6': '2001:10::2'},
1462                   {'mac': '00:00:11:11:11:02',
1463                    'ip': '10.0.0.2',
1464                    'ip6': '2001:10::3'}]
1465
1466         #
1467         # IP tables
1468         #
1469         gt4 = VppIpTable(self, 1)
1470         gt4.add_vpp_config()
1471         gt6 = VppIpTable(self, 1, is_ip6=True)
1472         gt6.add_vpp_config()
1473
1474         rd1 = VppGbpRouteDomain(self, 1, gt4, gt6)
1475         rd1.add_vpp_config()
1476
1477         #
1478         # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs
1479         # Pg3 hosts the IP4 UU-flood VXLAN tunnel
1480         # Pg4 hosts the IP6 UU-flood VXLAN tunnel
1481         #
1482         self.pg2.config_ip4()
1483         self.pg2.resolve_arp()
1484         self.pg2.generate_remote_hosts(4)
1485         self.pg2.configure_ipv4_neighbors()
1486         self.pg3.config_ip4()
1487         self.pg3.resolve_arp()
1488         self.pg4.config_ip4()
1489         self.pg4.resolve_arp()
1490
1491         #
1492         # Add a mcast destination VXLAN-GBP tunnel for B&M traffic
1493         #
1494         tun_bm = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
1495                                    "239.1.1.1", 88,
1496                                    mcast_itf=self.pg4)
1497         tun_bm.add_vpp_config()
1498
1499         #
1500         # a GBP bridge domain with a BVI and a UU-flood interface
1501         #
1502         bd1 = VppBridgeDomain(self, 1)
1503         bd1.add_vpp_config()
1504         gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, self.pg3, tun_bm)
1505         gbd1.add_vpp_config()
1506
1507         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
1508         self.logger.info(self.vapi.cli("sh gbp bridge"))
1509
1510         # ... and has a /32 applied
1511         ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
1512         ip_addr.add_vpp_config()
1513
1514         #
1515         # The Endpoint-group in which we are learning endpoints
1516         #
1517         epg_220 = VppGbpEndpointGroup(self, 220, 112, rd1, gbd1,
1518                                       None, self.loop0,
1519                                       "10.0.0.128",
1520                                       "2001:10::128",
1521                                       VppGbpEndpointRetention(2))
1522         epg_220.add_vpp_config()
1523         epg_330 = VppGbpEndpointGroup(self, 330, 113, rd1, gbd1,
1524                                       None, self.loop1,
1525                                       "10.0.1.128",
1526                                       "2001:11::128",
1527                                       VppGbpEndpointRetention(2))
1528         epg_330.add_vpp_config()
1529
1530         #
1531         # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
1532         # leanring enabled
1533         #
1534         vx_tun_l2_1 = VppGbpVxlanTunnel(
1535             self, 99, bd1.bd_id,
1536             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2,
1537             self.pg2.local_ip4)
1538         vx_tun_l2_1.add_vpp_config()
1539
1540         #
1541         # A static endpoint that the learnt endpoints are trying to
1542         # talk to
1543         #
1544         ep = VppGbpEndpoint(self, self.pg0,
1545                             epg_220, None,
1546                             "10.0.0.127", "11.0.0.127",
1547                             "2001:10::1", "3001::1")
1548         ep.add_vpp_config()
1549
1550         self.assertTrue(find_route(self, ep.ip4.address, 32, table_id=1))
1551
1552         # a packet with an sclass from an unknwon EPG
1553         p = (Ether(src=self.pg2.remote_mac,
1554                    dst=self.pg2.local_mac) /
1555              IP(src=self.pg2.remote_hosts[0].ip4,
1556                 dst=self.pg2.local_ip4) /
1557              UDP(sport=1234, dport=48879) /
1558              VXLAN(vni=99, gpid=88, flags=0x88) /
1559              Ether(src=learnt[0]["mac"], dst=ep.mac) /
1560              IP(src=learnt[0]["ip"], dst=ep.ip4.address) /
1561              UDP(sport=1234, dport=1234) /
1562              Raw('\xa5' * 100))
1563
1564         self.send_and_assert_no_replies(self.pg2, p)
1565
1566         #
1567         # we should not have learnt a new tunnel endpoint, since
1568         # the EPG was not learnt.
1569         #
1570         self.assertEqual(INDEX_INVALID,
1571                          find_vxlan_gbp_tunnel(self,
1572                                                self.pg2.local_ip4,
1573                                                self.pg2.remote_hosts[0].ip4,
1574                                                99))
1575
1576         # epg is not learnt, becasue the EPG is unknwon
1577         self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1)
1578
1579         #
1580         # Learn new EPs from IP packets
1581         #
1582         for ii, l in enumerate(learnt):
1583             # a packet with an sclass from a knwon EPG
1584             # arriving on an unknown TEP
1585             p = (Ether(src=self.pg2.remote_mac,
1586                        dst=self.pg2.local_mac) /
1587                  IP(src=self.pg2.remote_hosts[1].ip4,
1588                     dst=self.pg2.local_ip4) /
1589                  UDP(sport=1234, dport=48879) /
1590                  VXLAN(vni=99, gpid=112, flags=0x88) /
1591                  Ether(src=l['mac'], dst=ep.mac) /
1592                  IP(src=l['ip'], dst=ep.ip4.address) /
1593                  UDP(sport=1234, dport=1234) /
1594                  Raw('\xa5' * 100))
1595
1596             rx = self.send_and_expect(self.pg2, [p], self.pg0)
1597
1598             # the new TEP
1599             tep1_sw_if_index = find_vxlan_gbp_tunnel(
1600                 self,
1601                 self.pg2.local_ip4,
1602                 self.pg2.remote_hosts[1].ip4,
1603                 99)
1604             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
1605
1606             #
1607             # the EP is learnt via the learnt TEP
1608             # both from its MAC and its IP
1609             #
1610             self.assertTrue(find_gbp_endpoint(self,
1611                                               vx_tun_l2_1.sw_if_index,
1612                                               mac=l['mac']))
1613             self.assertTrue(find_gbp_endpoint(self,
1614                                               vx_tun_l2_1.sw_if_index,
1615                                               ip=l['ip']))
1616
1617         self.logger.info(self.vapi.cli("show gbp endpoint"))
1618         self.logger.info(self.vapi.cli("show gbp vxlan"))
1619         self.logger.info(self.vapi.cli("show ip mfib"))
1620
1621         #
1622         # If we sleep for the threshold time, the learnt endpoints should
1623         # age out
1624         #
1625         for l in learnt:
1626             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
1627                                      mac=l['mac'])
1628
1629         #
1630         # Learn new EPs from GARP packets received on the BD's mcast tunnel
1631         #
1632         for ii, l in enumerate(learnt):
1633             # a packet with an sclass from a knwon EPG
1634             # arriving on an unknown TEP
1635             p = (Ether(src=self.pg2.remote_mac,
1636                        dst=self.pg2.local_mac) /
1637                  IP(src=self.pg2.remote_hosts[1].ip4,
1638                     dst="239.1.1.1") /
1639                  UDP(sport=1234, dport=48879) /
1640                  VXLAN(vni=88, gpid=112, flags=0x88) /
1641                  Ether(src=l['mac'], dst="ff:ff:ff:ff:ff:ff") /
1642                  ARP(op="who-has",
1643                      psrc=l['ip'], pdst=l['ip'],
1644                      hwsrc=l['mac'], hwdst="ff:ff:ff:ff:ff:ff"))
1645
1646             rx = self.send_and_expect(self.pg4, [p], self.pg0)
1647
1648             # the new TEP
1649             tep1_sw_if_index = find_vxlan_gbp_tunnel(
1650                 self,
1651                 self.pg2.local_ip4,
1652                 self.pg2.remote_hosts[1].ip4,
1653                 99)
1654             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
1655
1656             #
1657             # the EP is learnt via the learnt TEP
1658             # both from its MAC and its IP
1659             #
1660             self.assertTrue(find_gbp_endpoint(self,
1661                                               vx_tun_l2_1.sw_if_index,
1662                                               mac=l['mac']))
1663             self.assertTrue(find_gbp_endpoint(self,
1664                                               vx_tun_l2_1.sw_if_index,
1665                                               ip=l['ip']))
1666
1667         #
1668         # wait for the learnt endpoints to age out
1669         #
1670         for l in learnt:
1671             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
1672                                      mac=l['mac'])
1673
1674         #
1675         # Learn new EPs from L2 packets
1676         #
1677         for ii, l in enumerate(learnt):
1678             # a packet with an sclass from a knwon EPG
1679             # arriving on an unknown TEP
1680             p = (Ether(src=self.pg2.remote_mac,
1681                        dst=self.pg2.local_mac) /
1682                  IP(src=self.pg2.remote_hosts[1].ip4,
1683                     dst=self.pg2.local_ip4) /
1684                  UDP(sport=1234, dport=48879) /
1685                  VXLAN(vni=99, gpid=112, flags=0x88) /
1686                  Ether(src=l['mac'], dst=ep.mac) /
1687                  Raw('\xa5' * 100))
1688
1689             rx = self.send_and_expect(self.pg2, [p], self.pg0)
1690
1691             # the new TEP
1692             tep1_sw_if_index = find_vxlan_gbp_tunnel(
1693                 self,
1694                 self.pg2.local_ip4,
1695                 self.pg2.remote_hosts[1].ip4,
1696                 99)
1697             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
1698
1699             #
1700             # the EP is learnt via the learnt TEP
1701             # both from its MAC and its IP
1702             #
1703             self.assertTrue(find_gbp_endpoint(self,
1704                                               vx_tun_l2_1.sw_if_index,
1705                                               mac=l['mac']))
1706
1707         self.logger.info(self.vapi.cli("show gbp endpoint"))
1708         self.logger.info(self.vapi.cli("show gbp vxlan"))
1709         self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
1710
1711         #
1712         # wait for the learnt endpoints to age out
1713         #
1714         for l in learnt:
1715             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
1716                                      mac=l['mac'])
1717
1718         #
1719         # repeat. the do not learn bit is set so the EPs are not learnt
1720         #
1721         for l in learnt:
1722             # a packet with an sclass from a knwon EPG
1723             p = (Ether(src=self.pg2.remote_mac,
1724                        dst=self.pg2.local_mac) /
1725                  IP(src=self.pg2.remote_hosts[1].ip4,
1726                     dst=self.pg2.local_ip4) /
1727                  UDP(sport=1234, dport=48879) /
1728                  VXLAN(vni=99, gpid=112, flags=0x88, gpflags="D") /
1729                  Ether(src=l['mac'], dst=ep.mac) /
1730                  IP(src=l['ip'], dst=ep.ip4.address) /
1731                  UDP(sport=1234, dport=1234) /
1732                  Raw('\xa5' * 100))
1733
1734             rx = self.send_and_expect(self.pg2, p*65, self.pg0)
1735
1736         for l in learnt:
1737             self.assertFalse(find_gbp_endpoint(self,
1738                                                vx_tun_l2_1.sw_if_index,
1739                                                mac=l['mac']))
1740
1741         #
1742         # repeat
1743         #
1744         for l in learnt:
1745             # a packet with an sclass from a knwon EPG
1746             p = (Ether(src=self.pg2.remote_mac,
1747                        dst=self.pg2.local_mac) /
1748                  IP(src=self.pg2.remote_hosts[1].ip4,
1749                     dst=self.pg2.local_ip4) /
1750                  UDP(sport=1234, dport=48879) /
1751                  VXLAN(vni=99, gpid=112, flags=0x88) /
1752                  Ether(src=l['mac'], dst=ep.mac) /
1753                  IP(src=l['ip'], dst=ep.ip4.address) /
1754                  UDP(sport=1234, dport=1234) /
1755                  Raw('\xa5' * 100))
1756
1757             rx = self.send_and_expect(self.pg2, p*65, self.pg0)
1758
1759             self.assertTrue(find_gbp_endpoint(self,
1760                                               vx_tun_l2_1.sw_if_index,
1761                                               mac=l['mac']))
1762
1763         #
1764         # Static EP replies to dynamics
1765         #
1766         self.logger.info(self.vapi.cli("sh l2fib bd_id 1"))
1767         for l in learnt:
1768             p = (Ether(src=ep.mac, dst=l['mac']) /
1769                  IP(dst=l['ip'], src=ep.ip4.address) /
1770                  UDP(sport=1234, dport=1234) /
1771                  Raw('\xa5' * 100))
1772
1773             rxs = self.send_and_expect(self.pg0, p * 17, self.pg2)
1774
1775             for rx in rxs:
1776                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
1777                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
1778                 self.assertEqual(rx[UDP].dport, 48879)
1779                 # the UDP source port is a random value for hashing
1780                 self.assertEqual(rx[VXLAN].gpid, 112)
1781                 self.assertEqual(rx[VXLAN].vni, 99)
1782                 self.assertTrue(rx[VXLAN].flags.G)
1783                 self.assertTrue(rx[VXLAN].flags.Instance)
1784                 self.assertTrue(rx[VXLAN].gpflags.A)
1785                 self.assertFalse(rx[VXLAN].gpflags.D)
1786
1787         for l in learnt:
1788             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
1789                                      mac=l['mac'])
1790
1791         #
1792         # repeat in the other EPG
1793         # there's no contract between 220 and 330, but the A-bit is set
1794         # so the packet is cleared for delivery
1795         #
1796         for l in learnt:
1797             # a packet with an sclass from a knwon EPG
1798             p = (Ether(src=self.pg2.remote_mac,
1799                        dst=self.pg2.local_mac) /
1800                  IP(src=self.pg2.remote_hosts[1].ip4,
1801                     dst=self.pg2.local_ip4) /
1802                  UDP(sport=1234, dport=48879) /
1803                  VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') /
1804                  Ether(src=l['mac'], dst=ep.mac) /
1805                  IP(src=l['ip'], dst=ep.ip4.address) /
1806                  UDP(sport=1234, dport=1234) /
1807                  Raw('\xa5' * 100))
1808
1809             rx = self.send_and_expect(self.pg2, p*65, self.pg0)
1810
1811             self.assertTrue(find_gbp_endpoint(self,
1812                                               vx_tun_l2_1.sw_if_index,
1813                                               mac=l['mac']))
1814
1815         #
1816         # static EP cannot reach the learnt EPs since there is no contract
1817         # only test 1 EP as the others could timeout
1818         #
1819         p = (Ether(src=ep.mac, dst=l['mac']) /
1820              IP(dst=learnt[0]['ip'], src=ep.ip4.address) /
1821              UDP(sport=1234, dport=1234) /
1822              Raw('\xa5' * 100))
1823
1824         self.send_and_assert_no_replies(self.pg0, [p])
1825
1826         #
1827         # refresh the entries after the check for no replies above
1828         #
1829         for l in learnt:
1830             # a packet with an sclass from a knwon EPG
1831             p = (Ether(src=self.pg2.remote_mac,
1832                        dst=self.pg2.local_mac) /
1833                  IP(src=self.pg2.remote_hosts[1].ip4,
1834                     dst=self.pg2.local_ip4) /
1835                  UDP(sport=1234, dport=48879) /
1836                  VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') /
1837                  Ether(src=l['mac'], dst=ep.mac) /
1838                  IP(src=l['ip'], dst=ep.ip4.address) /
1839                  UDP(sport=1234, dport=1234) /
1840                  Raw('\xa5' * 100))
1841
1842             rx = self.send_and_expect(self.pg2, p*65, self.pg0)
1843
1844             self.assertTrue(find_gbp_endpoint(self,
1845                                               vx_tun_l2_1.sw_if_index,
1846                                               mac=l['mac']))
1847
1848         #
1849         # Add the contract so they can talk
1850         #
1851         acl = VppGbpAcl(self)
1852         rule = acl.create_rule(permit_deny=1, proto=17)
1853         rule2 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17)
1854         acl_index = acl.add_vpp_config([rule, rule2])
1855         c1 = VppGbpContract(
1856             self, epg_220, epg_330, acl_index,
1857             [VppGbpContractRule(
1858                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1859                 []),
1860              VppGbpContractRule(
1861                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
1862                  [])],
1863             [ETH_P_IP, ETH_P_IPV6])
1864         c1.add_vpp_config()
1865
1866         for l in learnt:
1867             p = (Ether(src=ep.mac, dst=l['mac']) /
1868                  IP(dst=l['ip'], src=ep.ip4.address) /
1869                  UDP(sport=1234, dport=1234) /
1870                  Raw('\xa5' * 100))
1871
1872             self.send_and_expect(self.pg0, [p], self.pg2)
1873
1874         #
1875         # send UU packets from the local EP
1876         #
1877         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
1878         self.logger.info(self.vapi.cli("sh gbp bridge"))
1879         p_uu = (Ether(src=ep.mac, dst="00:11:11:11:11:11") /
1880                 IP(dst="10.0.0.133", src=ep.ip4.address) /
1881                 UDP(sport=1234, dport=1234) /
1882                 Raw('\xa5' * 100))
1883         rxs = self.send_and_expect(ep.itf, [p_uu], gbd1.uu_fwd)
1884
1885         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
1886
1887         p_bm = (Ether(src=ep.mac, dst="ff:ff:ff:ff:ff:ff") /
1888                 IP(dst="10.0.0.133", src=ep.ip4.address) /
1889                 UDP(sport=1234, dport=1234) /
1890                 Raw('\xa5' * 100))
1891         rxs = self.send_and_expect_only(ep.itf, [p_bm], tun_bm.mcast_itf)
1892
1893         for rx in rxs:
1894             self.assertEqual(rx[IP].src, self.pg4.local_ip4)
1895             self.assertEqual(rx[IP].dst, "239.1.1.1")
1896             self.assertEqual(rx[UDP].dport, 48879)
1897             # the UDP source port is a random value for hashing
1898             self.assertEqual(rx[VXLAN].gpid, 112)
1899             self.assertEqual(rx[VXLAN].vni, 88)
1900             self.assertTrue(rx[VXLAN].flags.G)
1901             self.assertTrue(rx[VXLAN].flags.Instance)
1902             self.assertFalse(rx[VXLAN].gpflags.A)
1903             self.assertFalse(rx[VXLAN].gpflags.D)
1904
1905         #
1906         # Check v6 Endpoints
1907         #
1908         for l in learnt:
1909             # a packet with an sclass from a knwon EPG
1910             p = (Ether(src=self.pg2.remote_mac,
1911                        dst=self.pg2.local_mac) /
1912                  IP(src=self.pg2.remote_hosts[1].ip4,
1913                     dst=self.pg2.local_ip4) /
1914                  UDP(sport=1234, dport=48879) /
1915                  VXLAN(vni=99, gpid=113, flags=0x88, gpflags='A') /
1916                  Ether(src=l['mac'], dst=ep.mac) /
1917                  IPv6(src=l['ip6'], dst=ep.ip6.address) /
1918                  UDP(sport=1234, dport=1234) /
1919                  Raw('\xa5' * 100))
1920
1921             rx = self.send_and_expect(self.pg2, p*65, self.pg0)
1922
1923             self.assertTrue(find_gbp_endpoint(self,
1924                                               vx_tun_l2_1.sw_if_index,
1925                                               mac=l['mac']))
1926
1927         #
1928         # L3 Endpoint Learning
1929         #  - configured on the bridge's BVI
1930         #
1931
1932         #
1933         # clean up
1934         #
1935         for l in learnt:
1936             self.wait_for_ep_timeout(vx_tun_l2_1.sw_if_index,
1937                                      mac=l['mac'])
1938
1939         self.pg2.unconfig_ip4()
1940         self.pg3.unconfig_ip4()
1941         self.pg4.unconfig_ip4()
1942
1943         self.logger.info(self.vapi.cli("sh int"))
1944         self.logger.info(self.vapi.cli("sh gbp vxlan"))
1945
1946     def test_gbp_learn_vlan_l2(self):
1947         """ GBP L2 Endpoint w/ VLANs"""
1948
1949         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
1950         learnt = [{'mac': '00:00:11:11:11:01',
1951                    'ip': '10.0.0.1',
1952                    'ip6': '2001:10::2'},
1953                   {'mac': '00:00:11:11:11:02',
1954                    'ip': '10.0.0.2',
1955                    'ip6': '2001:10::3'}]
1956
1957         #
1958         # IP tables
1959         #
1960         gt4 = VppIpTable(self, 1)
1961         gt4.add_vpp_config()
1962         gt6 = VppIpTable(self, 1, is_ip6=True)
1963         gt6.add_vpp_config()
1964
1965         rd1 = VppGbpRouteDomain(self, 1, gt4, gt6)
1966         rd1.add_vpp_config()
1967
1968         #
1969         # Pg2 hosts the vxlan tunnel, hosts on pg2 to act as TEPs
1970         #
1971         self.pg2.config_ip4()
1972         self.pg2.resolve_arp()
1973         self.pg2.generate_remote_hosts(4)
1974         self.pg2.configure_ipv4_neighbors()
1975         self.pg3.config_ip4()
1976         self.pg3.resolve_arp()
1977
1978         #
1979         # The EP will be on a vlan sub-interface
1980         #
1981         vlan_11 = VppDot1QSubint(self, self.pg0, 11)
1982         vlan_11.admin_up()
1983         self.vapi.l2_interface_vlan_tag_rewrite(
1984             sw_if_index=vlan_11.sw_if_index, vtr_op=L2_VTR_OP.L2_POP_1,
1985             push_dot1q=11)
1986
1987         bd_uu_fwd = VppVxlanGbpTunnel(self, self.pg3.local_ip4,
1988                                       self.pg3.remote_ip4, 116)
1989         bd_uu_fwd.add_vpp_config()
1990
1991         #
1992         # a GBP bridge domain with a BVI and a UU-flood interface
1993         # The BD is marked as do not learn, so no endpoints are ever
1994         # learnt in this BD.
1995         #
1996         bd1 = VppBridgeDomain(self, 1)
1997         bd1.add_vpp_config()
1998         gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, bd_uu_fwd,
1999                                   learn=False)
2000         gbd1.add_vpp_config()
2001
2002         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
2003         self.logger.info(self.vapi.cli("sh gbp bridge"))
2004
2005         # ... and has a /32 applied
2006         ip_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
2007         ip_addr.add_vpp_config()
2008
2009         #
2010         # The Endpoint-group in which we are learning endpoints
2011         #
2012         epg_220 = VppGbpEndpointGroup(self, 220, 441, rd1, gbd1,
2013                                       None, self.loop0,
2014                                       "10.0.0.128",
2015                                       "2001:10::128",
2016                                       VppGbpEndpointRetention(2))
2017         epg_220.add_vpp_config()
2018
2019         #
2020         # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
2021         # leanring enabled
2022         #
2023         vx_tun_l2_1 = VppGbpVxlanTunnel(
2024             self, 99, bd1.bd_id,
2025             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L2,
2026             self.pg2.local_ip4)
2027         vx_tun_l2_1.add_vpp_config()
2028
2029         #
2030         # A static endpoint that the learnt endpoints are trying to
2031         # talk to
2032         #
2033         ep = VppGbpEndpoint(self, vlan_11,
2034                             epg_220, None,
2035                             "10.0.0.127", "11.0.0.127",
2036                             "2001:10::1", "3001::1")
2037         ep.add_vpp_config()
2038
2039         self.assertTrue(find_route(self, ep.ip4.address, 32, table_id=1))
2040
2041         #
2042         # Send to the static EP
2043         #
2044         for ii, l in enumerate(learnt):
2045             # a packet with an sclass from a knwon EPG
2046             # arriving on an unknown TEP
2047             p = (Ether(src=self.pg2.remote_mac,
2048                        dst=self.pg2.local_mac) /
2049                  IP(src=self.pg2.remote_hosts[1].ip4,
2050                     dst=self.pg2.local_ip4) /
2051                  UDP(sport=1234, dport=48879) /
2052                  VXLAN(vni=99, gpid=441, flags=0x88) /
2053                  Ether(src=l['mac'], dst=ep.mac) /
2054                  IP(src=l['ip'], dst=ep.ip4.address) /
2055                  UDP(sport=1234, dport=1234) /
2056                  Raw('\xa5' * 100))
2057
2058             rxs = self.send_and_expect(self.pg2, [p], self.pg0)
2059
2060             #
2061             # packet to EP has the EP's vlan tag
2062             #
2063             for rx in rxs:
2064                 self.assertEqual(rx[Dot1Q].vlan, 11)
2065
2066             #
2067             # the EP is not learnt since the BD setting prevents it
2068             # also no TEP too
2069             #
2070             self.assertFalse(find_gbp_endpoint(self,
2071                                                vx_tun_l2_1.sw_if_index,
2072                                                mac=l['mac']))
2073             self.assertEqual(INDEX_INVALID,
2074                              find_vxlan_gbp_tunnel(
2075                                  self,
2076                                  self.pg2.local_ip4,
2077                                  self.pg2.remote_hosts[1].ip4,
2078                                  99))
2079
2080         self.assertEqual(len(self.vapi.gbp_endpoint_dump()), 1)
2081
2082         #
2083         # static to remotes
2084         # we didn't learn the remotes so they are sent to the UU-fwd
2085         #
2086         for l in learnt:
2087             p = (Ether(src=ep.mac, dst=l['mac']) /
2088                  Dot1Q(vlan=11) /
2089                  IP(dst=l['ip'], src=ep.ip4.address) /
2090                  UDP(sport=1234, dport=1234) /
2091                  Raw('\xa5' * 100))
2092
2093             rxs = self.send_and_expect(self.pg0, p * 17, self.pg3)
2094
2095             for rx in rxs:
2096                 self.assertEqual(rx[IP].src, self.pg3.local_ip4)
2097                 self.assertEqual(rx[IP].dst, self.pg3.remote_ip4)
2098                 self.assertEqual(rx[UDP].dport, 48879)
2099                 # the UDP source port is a random value for hashing
2100                 self.assertEqual(rx[VXLAN].gpid, 441)
2101                 self.assertEqual(rx[VXLAN].vni, 116)
2102                 self.assertTrue(rx[VXLAN].flags.G)
2103                 self.assertTrue(rx[VXLAN].flags.Instance)
2104                 self.assertFalse(rx[VXLAN].gpflags.A)
2105                 self.assertFalse(rx[VXLAN].gpflags.D)
2106
2107         self.pg2.unconfig_ip4()
2108         self.pg3.unconfig_ip4()
2109
2110     def test_gbp_learn_l3(self):
2111         """ GBP L3 Endpoint Learning """
2112
2113         self.vapi.cli("set logging class gbp debug")
2114
2115         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
2116         routed_dst_mac = "00:0c:0c:0c:0c:0c"
2117         routed_src_mac = "00:22:bd:f8:19:ff"
2118
2119         learnt = [{'mac': '00:00:11:11:11:02',
2120                    'ip': '10.0.1.2',
2121                    'ip6': '2001:10::2'},
2122                   {'mac': '00:00:11:11:11:03',
2123                    'ip': '10.0.1.3',
2124                    'ip6': '2001:10::3'}]
2125
2126         #
2127         # IP tables
2128         #
2129         t4 = VppIpTable(self, 1)
2130         t4.add_vpp_config()
2131         t6 = VppIpTable(self, 1, True)
2132         t6.add_vpp_config()
2133
2134         tun_ip4_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
2135                                        self.pg4.remote_ip4, 114)
2136         tun_ip6_uu = VppVxlanGbpTunnel(self, self.pg4.local_ip4,
2137                                        self.pg4.remote_ip4, 116)
2138         tun_ip4_uu.add_vpp_config()
2139         tun_ip6_uu.add_vpp_config()
2140
2141         rd1 = VppGbpRouteDomain(self, 2, t4, t6, tun_ip4_uu, tun_ip6_uu)
2142         rd1.add_vpp_config()
2143
2144         self.loop0.set_mac(self.router_mac)
2145
2146         #
2147         # Bind the BVI to the RD
2148         #
2149         VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
2150         VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
2151
2152         #
2153         # Pg2 hosts the vxlan tunnel
2154         # hosts on pg2 to act as TEPs
2155         # pg3 is BD uu-fwd
2156         # pg4 is RD uu-fwd
2157         #
2158         self.pg2.config_ip4()
2159         self.pg2.resolve_arp()
2160         self.pg2.generate_remote_hosts(4)
2161         self.pg2.configure_ipv4_neighbors()
2162         self.pg3.config_ip4()
2163         self.pg3.resolve_arp()
2164         self.pg4.config_ip4()
2165         self.pg4.resolve_arp()
2166
2167         #
2168         # a GBP bridge domain with a BVI and a UU-flood interface
2169         #
2170         bd1 = VppBridgeDomain(self, 1)
2171         bd1.add_vpp_config()
2172         gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, self.pg3)
2173         gbd1.add_vpp_config()
2174
2175         self.logger.info(self.vapi.cli("sh bridge 1 detail"))
2176         self.logger.info(self.vapi.cli("sh gbp bridge"))
2177         self.logger.info(self.vapi.cli("sh gbp route"))
2178
2179         # ... and has a /32 and /128 applied
2180         ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
2181         ip4_addr.add_vpp_config()
2182         ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 128)
2183         ip6_addr.add_vpp_config()
2184
2185         #
2186         # The Endpoint-group in which we are learning endpoints
2187         #
2188         epg_220 = VppGbpEndpointGroup(self, 220, 441, rd1, gbd1,
2189                                       None, self.loop0,
2190                                       "10.0.0.128",
2191                                       "2001:10::128",
2192                                       VppGbpEndpointRetention(2))
2193         epg_220.add_vpp_config()
2194
2195         #
2196         # The VXLAN GBP tunnel is a bridge-port and has L2 endpoint
2197         # leanring enabled
2198         #
2199         vx_tun_l3 = VppGbpVxlanTunnel(
2200             self, 101, rd1.rd_id,
2201             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
2202             self.pg2.local_ip4)
2203         vx_tun_l3.add_vpp_config()
2204
2205         #
2206         # A static endpoint that the learnt endpoints are trying to
2207         # talk to
2208         #
2209         ep = VppGbpEndpoint(self, self.pg0,
2210                             epg_220, None,
2211                             "10.0.0.127", "11.0.0.127",
2212                             "2001:10::1", "3001::1")
2213         ep.add_vpp_config()
2214
2215         #
2216         # learn some remote IPv4 EPs
2217         #
2218         for ii, l in enumerate(learnt):
2219             # a packet with an sclass from a knwon EPG
2220             # arriving on an unknown TEP
2221             p = (Ether(src=self.pg2.remote_mac,
2222                        dst=self.pg2.local_mac) /
2223                  IP(src=self.pg2.remote_hosts[1].ip4,
2224                     dst=self.pg2.local_ip4) /
2225                  UDP(sport=1234, dport=48879) /
2226                  VXLAN(vni=101, gpid=441, flags=0x88) /
2227                  Ether(src=l['mac'], dst="00:00:00:11:11:11") /
2228                  IP(src=l['ip'], dst=ep.ip4.address) /
2229                  UDP(sport=1234, dport=1234) /
2230                  Raw('\xa5' * 100))
2231
2232             rx = self.send_and_expect(self.pg2, [p], self.pg0)
2233
2234             # the new TEP
2235             tep1_sw_if_index = find_vxlan_gbp_tunnel(
2236                 self,
2237                 self.pg2.local_ip4,
2238                 self.pg2.remote_hosts[1].ip4,
2239                 vx_tun_l3.vni)
2240             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
2241
2242             # endpoint learnt via the parent GBP-vxlan interface
2243             self.assertTrue(find_gbp_endpoint(self,
2244                                               vx_tun_l3._sw_if_index,
2245                                               ip=l['ip']))
2246
2247         #
2248         # Static IPv4 EP replies to learnt
2249         #
2250         for l in learnt:
2251             p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2252                  IP(dst=l['ip'], src=ep.ip4.address) /
2253                  UDP(sport=1234, dport=1234) /
2254                  Raw('\xa5' * 100))
2255
2256             rxs = self.send_and_expect(self.pg0, p*1, self.pg2)
2257
2258             for rx in rxs:
2259                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
2260                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
2261                 self.assertEqual(rx[UDP].dport, 48879)
2262                 # the UDP source port is a random value for hashing
2263                 self.assertEqual(rx[VXLAN].gpid, 441)
2264                 self.assertEqual(rx[VXLAN].vni, 101)
2265                 self.assertTrue(rx[VXLAN].flags.G)
2266                 self.assertTrue(rx[VXLAN].flags.Instance)
2267                 self.assertTrue(rx[VXLAN].gpflags.A)
2268                 self.assertFalse(rx[VXLAN].gpflags.D)
2269
2270                 inner = rx[VXLAN].payload
2271
2272                 self.assertEqual(inner[Ether].src, routed_src_mac)
2273                 self.assertEqual(inner[Ether].dst, routed_dst_mac)
2274                 self.assertEqual(inner[IP].src, ep.ip4.address)
2275                 self.assertEqual(inner[IP].dst, l['ip'])
2276
2277         for l in learnt:
2278             self.assertFalse(find_gbp_endpoint(self,
2279                                                tep1_sw_if_index,
2280                                                ip=l['ip']))
2281
2282         #
2283         # learn some remote IPv6 EPs
2284         #
2285         for ii, l in enumerate(learnt):
2286             # a packet with an sclass from a knwon EPG
2287             # arriving on an unknown TEP
2288             p = (Ether(src=self.pg2.remote_mac,
2289                        dst=self.pg2.local_mac) /
2290                  IP(src=self.pg2.remote_hosts[1].ip4,
2291                     dst=self.pg2.local_ip4) /
2292                  UDP(sport=1234, dport=48879) /
2293                  VXLAN(vni=101, gpid=441, flags=0x88) /
2294                  Ether(src=l['mac'], dst="00:00:00:11:11:11") /
2295                  IPv6(src=l['ip6'], dst=ep.ip6.address) /
2296                  UDP(sport=1234, dport=1234) /
2297                  Raw('\xa5' * 100))
2298
2299             rx = self.send_and_expect(self.pg2, [p], self.pg0)
2300
2301             # the new TEP
2302             tep1_sw_if_index = find_vxlan_gbp_tunnel(
2303                 self,
2304                 self.pg2.local_ip4,
2305                 self.pg2.remote_hosts[1].ip4,
2306                 vx_tun_l3.vni)
2307             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
2308
2309             self.logger.info(self.vapi.cli("show gbp bridge"))
2310             self.logger.info(self.vapi.cli("show vxlan-gbp tunnel"))
2311             self.logger.info(self.vapi.cli("show gbp vxlan"))
2312             self.logger.info(self.vapi.cli("show int addr"))
2313
2314             # endpoint learnt via the TEP
2315             self.assertTrue(find_gbp_endpoint(self, ip=l['ip6']))
2316
2317         self.logger.info(self.vapi.cli("show gbp endpoint"))
2318         self.logger.info(self.vapi.cli("show ip fib index 1 %s" % l['ip']))
2319
2320         #
2321         # Static EP replies to learnt
2322         #
2323         for l in learnt:
2324             p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2325                  IPv6(dst=l['ip6'], src=ep.ip6.address) /
2326                  UDP(sport=1234, dport=1234) /
2327                  Raw('\xa5' * 100))
2328
2329             rxs = self.send_and_expect(self.pg0, p*65, self.pg2)
2330
2331             for rx in rxs:
2332                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
2333                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
2334                 self.assertEqual(rx[UDP].dport, 48879)
2335                 # the UDP source port is a random value for hashing
2336                 self.assertEqual(rx[VXLAN].gpid, 441)
2337                 self.assertEqual(rx[VXLAN].vni, 101)
2338                 self.assertTrue(rx[VXLAN].flags.G)
2339                 self.assertTrue(rx[VXLAN].flags.Instance)
2340                 self.assertTrue(rx[VXLAN].gpflags.A)
2341                 self.assertFalse(rx[VXLAN].gpflags.D)
2342
2343                 inner = rx[VXLAN].payload
2344
2345                 self.assertEqual(inner[Ether].src, routed_src_mac)
2346                 self.assertEqual(inner[Ether].dst, routed_dst_mac)
2347                 self.assertEqual(inner[IPv6].src, ep.ip6.address)
2348                 self.assertEqual(inner[IPv6].dst, l['ip6'])
2349
2350         self.logger.info(self.vapi.cli("sh gbp endpoint"))
2351         for l in learnt:
2352             self.wait_for_ep_timeout(ip=l['ip'])
2353
2354         #
2355         # Static sends to unknown EP with no route
2356         #
2357         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2358              IP(dst="10.0.0.99", src=ep.ip4.address) /
2359              UDP(sport=1234, dport=1234) /
2360              Raw('\xa5' * 100))
2361
2362         self.send_and_assert_no_replies(self.pg0, [p])
2363
2364         #
2365         # Add a route to static EP's v4 and v6 subnet
2366         #  packets should be sent on the v4/v6 uu=fwd interface resp.
2367         #
2368         se_10_24 = VppGbpSubnet(
2369             self, rd1, "10.0.0.0", 24,
2370             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT)
2371         se_10_24.add_vpp_config()
2372
2373         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2374              IP(dst="10.0.0.99", src=ep.ip4.address) /
2375              UDP(sport=1234, dport=1234) /
2376              Raw('\xa5' * 100))
2377
2378         rxs = self.send_and_expect(self.pg0, [p], self.pg4)
2379         for rx in rxs:
2380             self.assertEqual(rx[IP].src, self.pg4.local_ip4)
2381             self.assertEqual(rx[IP].dst, self.pg4.remote_ip4)
2382             self.assertEqual(rx[UDP].dport, 48879)
2383             # the UDP source port is a random value for hashing
2384             self.assertEqual(rx[VXLAN].gpid, 441)
2385             self.assertEqual(rx[VXLAN].vni, 114)
2386             self.assertTrue(rx[VXLAN].flags.G)
2387             self.assertTrue(rx[VXLAN].flags.Instance)
2388             # policy is not applied to packets sent to the uu-fwd interfaces
2389             self.assertFalse(rx[VXLAN].gpflags.A)
2390             self.assertFalse(rx[VXLAN].gpflags.D)
2391
2392         #
2393         # learn some remote IPv4 EPs
2394         #
2395         for ii, l in enumerate(learnt):
2396             # a packet with an sclass from a knwon EPG
2397             # arriving on an unknown TEP
2398             p = (Ether(src=self.pg2.remote_mac,
2399                        dst=self.pg2.local_mac) /
2400                  IP(src=self.pg2.remote_hosts[2].ip4,
2401                     dst=self.pg2.local_ip4) /
2402                  UDP(sport=1234, dport=48879) /
2403                  VXLAN(vni=101, gpid=441, flags=0x88) /
2404                  Ether(src=l['mac'], dst="00:00:00:11:11:11") /
2405                  IP(src=l['ip'], dst=ep.ip4.address) /
2406                  UDP(sport=1234, dport=1234) /
2407                  Raw('\xa5' * 100))
2408
2409             rx = self.send_and_expect(self.pg2, [p], self.pg0)
2410
2411             # the new TEP
2412             tep1_sw_if_index = find_vxlan_gbp_tunnel(
2413                 self,
2414                 self.pg2.local_ip4,
2415                 self.pg2.remote_hosts[2].ip4,
2416                 vx_tun_l3.vni)
2417             self.assertNotEqual(INDEX_INVALID, tep1_sw_if_index)
2418
2419             # endpoint learnt via the parent GBP-vxlan interface
2420             self.assertTrue(find_gbp_endpoint(self,
2421                                               vx_tun_l3._sw_if_index,
2422                                               ip=l['ip']))
2423
2424         #
2425         # Add a remote endpoint from the API
2426         #
2427         rep_88 = VppGbpEndpoint(self, vx_tun_l3,
2428                                 epg_220, None,
2429                                 "10.0.0.88", "11.0.0.88",
2430                                 "2001:10::88", "3001::88",
2431                                 ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
2432                                 self.pg2.local_ip4,
2433                                 self.pg2.remote_hosts[1].ip4,
2434                                 mac=None)
2435         rep_88.add_vpp_config()
2436
2437         #
2438         # Add a remote endpoint from the API that matches an existing one
2439         #
2440         rep_2 = VppGbpEndpoint(self, vx_tun_l3,
2441                                epg_220, None,
2442                                learnt[0]['ip'], "11.0.0.101",
2443                                learnt[0]['ip6'], "3001::101",
2444                                ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
2445                                self.pg2.local_ip4,
2446                                self.pg2.remote_hosts[1].ip4,
2447                                mac=None)
2448         rep_2.add_vpp_config()
2449
2450         #
2451         # Add a route to the leanred EP's v4 subnet
2452         #  packets should be send on the v4/v6 uu=fwd interface resp.
2453         #
2454         se_10_1_24 = VppGbpSubnet(
2455             self, rd1, "10.0.1.0", 24,
2456             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_TRANSPORT)
2457         se_10_1_24.add_vpp_config()
2458
2459         self.logger.info(self.vapi.cli("show gbp endpoint"))
2460
2461         ips = ["10.0.0.88", learnt[0]['ip']]
2462         for ip in ips:
2463             p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2464                  IP(dst=ip, src=ep.ip4.address) /
2465                  UDP(sport=1234, dport=1234) /
2466                  Raw('\xa5' * 100))
2467
2468             rxs = self.send_and_expect(self.pg0, p*65, self.pg2)
2469
2470             for rx in rxs:
2471                 self.assertEqual(rx[IP].src, self.pg2.local_ip4)
2472                 self.assertEqual(rx[IP].dst, self.pg2.remote_hosts[1].ip4)
2473                 self.assertEqual(rx[UDP].dport, 48879)
2474                 # the UDP source port is a random value for hashing
2475                 self.assertEqual(rx[VXLAN].gpid, 441)
2476                 self.assertEqual(rx[VXLAN].vni, 101)
2477                 self.assertTrue(rx[VXLAN].flags.G)
2478                 self.assertTrue(rx[VXLAN].flags.Instance)
2479                 self.assertTrue(rx[VXLAN].gpflags.A)
2480                 self.assertFalse(rx[VXLAN].gpflags.D)
2481
2482                 inner = rx[VXLAN].payload
2483
2484                 self.assertEqual(inner[Ether].src, routed_src_mac)
2485                 self.assertEqual(inner[Ether].dst, routed_dst_mac)
2486                 self.assertEqual(inner[IP].src, ep.ip4.address)
2487                 self.assertEqual(inner[IP].dst, ip)
2488
2489         #
2490         # remove the API remote EPs, only API sourced is gone, the DP
2491         # learnt one remains
2492         #
2493         rep_88.remove_vpp_config()
2494         rep_2.remove_vpp_config()
2495
2496         self.assertTrue(find_gbp_endpoint(self, ip=rep_2.ip4.address))
2497
2498         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2499              IP(src=ep.ip4.address, dst=rep_2.ip4.address) /
2500              UDP(sport=1234, dport=1234) /
2501              Raw('\xa5' * 100))
2502         rxs = self.send_and_expect(self.pg0, [p], self.pg2)
2503
2504         self.assertFalse(find_gbp_endpoint(self, ip=rep_88.ip4.address))
2505
2506         p = (Ether(src=ep.mac, dst=self.loop0.local_mac) /
2507              IP(src=ep.ip4.address, dst=rep_88.ip4.address) /
2508              UDP(sport=1234, dport=1234) /
2509              Raw('\xa5' * 100))
2510         rxs = self.send_and_expect(self.pg0, [p], self.pg4)
2511
2512         #
2513         # to appease the testcase we cannot have the registered EP stll
2514         # present (because it's DP learnt) when the TC ends so wait until
2515         # it is removed
2516         #
2517         self.wait_for_ep_timeout(ip=rep_88.ip4.address)
2518         self.wait_for_ep_timeout(ip=rep_2.ip4.address)
2519
2520         #
2521         # shutdown with learnt endpoint present
2522         #
2523         p = (Ether(src=self.pg2.remote_mac,
2524                    dst=self.pg2.local_mac) /
2525              IP(src=self.pg2.remote_hosts[1].ip4,
2526                 dst=self.pg2.local_ip4) /
2527              UDP(sport=1234, dport=48879) /
2528              VXLAN(vni=101, gpid=441, flags=0x88) /
2529              Ether(src=l['mac'], dst="00:00:00:11:11:11") /
2530              IP(src=learnt[1]['ip'], dst=ep.ip4.address) /
2531              UDP(sport=1234, dport=1234) /
2532              Raw('\xa5' * 100))
2533
2534         rx = self.send_and_expect(self.pg2, [p], self.pg0)
2535
2536         # endpoint learnt via the parent GBP-vxlan interface
2537         self.assertTrue(find_gbp_endpoint(self,
2538                                           vx_tun_l3._sw_if_index,
2539                                           ip=l['ip']))
2540
2541         #
2542         # TODO
2543         # remote endpoint becomes local
2544         #
2545         self.pg2.unconfig_ip4()
2546         self.pg3.unconfig_ip4()
2547         self.pg4.unconfig_ip4()
2548
2549     def test_gbp_redirect(self):
2550         """ GBP Endpoint Redirect """
2551
2552         self.vapi.cli("set logging class gbp debug")
2553
2554         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
2555         routed_dst_mac = "00:0c:0c:0c:0c:0c"
2556         routed_src_mac = "00:22:bd:f8:19:ff"
2557
2558         learnt = [{'mac': '00:00:11:11:11:02',
2559                    'ip': '10.0.1.2',
2560                    'ip6': '2001:10::2'},
2561                   {'mac': '00:00:11:11:11:03',
2562                    'ip': '10.0.1.3',
2563                    'ip6': '2001:10::3'}]
2564
2565         #
2566         # IP tables
2567         #
2568         t4 = VppIpTable(self, 1)
2569         t4.add_vpp_config()
2570         t6 = VppIpTable(self, 1, True)
2571         t6.add_vpp_config()
2572
2573         rd1 = VppGbpRouteDomain(self, 2, t4, t6)
2574         rd1.add_vpp_config()
2575
2576         self.loop0.set_mac(self.router_mac)
2577
2578         #
2579         # Bind the BVI to the RD
2580         #
2581         VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
2582         VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
2583
2584         #
2585         # Pg7 hosts a BD's UU-fwd
2586         #
2587         self.pg7.config_ip4()
2588         self.pg7.resolve_arp()
2589
2590         #
2591         # a GBP bridge domains for the EPs
2592         #
2593         bd1 = VppBridgeDomain(self, 1)
2594         bd1.add_vpp_config()
2595         gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0)
2596         gbd1.add_vpp_config()
2597
2598         bd2 = VppBridgeDomain(self, 2)
2599         bd2.add_vpp_config()
2600         gbd2 = VppGbpBridgeDomain(self, bd2, self.loop1)
2601         gbd2.add_vpp_config()
2602
2603         # ... and has a /32 and /128 applied
2604         ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 32)
2605         ip4_addr.add_vpp_config()
2606         ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 128)
2607         ip6_addr.add_vpp_config()
2608         ip4_addr = VppIpInterfaceAddress(self, gbd2.bvi, "10.0.1.128", 32)
2609         ip4_addr.add_vpp_config()
2610         ip6_addr = VppIpInterfaceAddress(self, gbd2.bvi, "2001:11::128", 128)
2611         ip6_addr.add_vpp_config()
2612
2613         #
2614         # The Endpoint-groups in which we are learning endpoints
2615         #
2616         epg_220 = VppGbpEndpointGroup(self, 220, 440, rd1, gbd1,
2617                                       None, gbd1.bvi,
2618                                       "10.0.0.128",
2619                                       "2001:10::128",
2620                                       VppGbpEndpointRetention(2))
2621         epg_220.add_vpp_config()
2622         epg_221 = VppGbpEndpointGroup(self, 221, 441, rd1, gbd2,
2623                                       None, gbd2.bvi,
2624                                       "10.0.1.128",
2625                                       "2001:11::128",
2626                                       VppGbpEndpointRetention(2))
2627         epg_221.add_vpp_config()
2628         epg_222 = VppGbpEndpointGroup(self, 222, 442, rd1, gbd1,
2629                                       None, gbd1.bvi,
2630                                       "10.0.2.128",
2631                                       "2001:12::128",
2632                                       VppGbpEndpointRetention(2))
2633         epg_222.add_vpp_config()
2634
2635         #
2636         # a GBP bridge domains for the SEPs
2637         #
2638         bd_uu1 = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
2639                                    self.pg7.remote_ip4, 116)
2640         bd_uu1.add_vpp_config()
2641         bd_uu2 = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
2642                                    self.pg7.remote_ip4, 117)
2643         bd_uu2.add_vpp_config()
2644
2645         bd3 = VppBridgeDomain(self, 3)
2646         bd3.add_vpp_config()
2647         gbd3 = VppGbpBridgeDomain(self, bd3, self.loop2, bd_uu1, learn=False)
2648         gbd3.add_vpp_config()
2649         bd4 = VppBridgeDomain(self, 4)
2650         bd4.add_vpp_config()
2651         gbd4 = VppGbpBridgeDomain(self, bd4, self.loop3, bd_uu2, learn=False)
2652         gbd4.add_vpp_config()
2653
2654         #
2655         # EPGs in which the service endpoints exist
2656         #
2657         epg_320 = VppGbpEndpointGroup(self, 320, 550, rd1, gbd3,
2658                                       None, gbd1.bvi,
2659                                       "12.0.0.128",
2660                                       "4001:10::128",
2661                                       VppGbpEndpointRetention(2))
2662         epg_320.add_vpp_config()
2663         epg_321 = VppGbpEndpointGroup(self, 321, 551, rd1, gbd4,
2664                                       None, gbd2.bvi,
2665                                       "12.0.1.128",
2666                                       "4001:11::128",
2667                                       VppGbpEndpointRetention(2))
2668         epg_321.add_vpp_config()
2669
2670         #
2671         # three local endpoints
2672         #
2673         ep1 = VppGbpEndpoint(self, self.pg0,
2674                              epg_220, None,
2675                              "10.0.0.1", "11.0.0.1",
2676                              "2001:10::1", "3001:10::1")
2677         ep1.add_vpp_config()
2678         ep2 = VppGbpEndpoint(self, self.pg1,
2679                              epg_221, None,
2680                              "10.0.1.1", "11.0.1.1",
2681                              "2001:11::1", "3001:11::1")
2682         ep2.add_vpp_config()
2683         ep3 = VppGbpEndpoint(self, self.pg2,
2684                              epg_222, None,
2685                              "10.0.2.2", "11.0.2.2",
2686                              "2001:12::1", "3001:12::1")
2687         ep3.add_vpp_config()
2688
2689         #
2690         # service endpoints
2691         #
2692         sep1 = VppGbpEndpoint(self, self.pg3,
2693                               epg_320, None,
2694                               "12.0.0.1", "13.0.0.1",
2695                               "4001:10::1", "5001:10::1")
2696         sep1.add_vpp_config()
2697         sep2 = VppGbpEndpoint(self, self.pg4,
2698                               epg_320, None,
2699                               "12.0.0.2", "13.0.0.2",
2700                               "4001:10::2", "5001:10::2")
2701         sep2.add_vpp_config()
2702         sep3 = VppGbpEndpoint(self, self.pg5,
2703                               epg_321, None,
2704                               "12.0.1.1", "13.0.1.1",
2705                               "4001:11::1", "5001:11::1")
2706         sep3.add_vpp_config()
2707         # this EP is not installed immediately
2708         sep4 = VppGbpEndpoint(self, self.pg6,
2709                               epg_321, None,
2710                               "12.0.1.2", "13.0.1.2",
2711                               "4001:11::2", "5001:11::2")
2712
2713         #
2714         # an L2 switch packet between local EPs in different EPGs
2715         #  different dest ports on each so the are LB hashed differently
2716         #
2717         p4 = [(Ether(src=ep1.mac, dst=ep3.mac) /
2718                IP(src=ep1.ip4.address, dst=ep3.ip4.address) /
2719                UDP(sport=1234, dport=1234) /
2720                Raw('\xa5' * 100)),
2721               (Ether(src=ep3.mac, dst=ep1.mac) /
2722                IP(src=ep3.ip4.address, dst=ep1.ip4.address) /
2723                UDP(sport=1234, dport=1234) /
2724                Raw('\xa5' * 100))]
2725         p6 = [(Ether(src=ep1.mac, dst=ep3.mac) /
2726                IPv6(src=ep1.ip6.address, dst=ep3.ip6.address) /
2727                UDP(sport=1234, dport=1234) /
2728                Raw('\xa5' * 100)),
2729               (Ether(src=ep3.mac, dst=ep1.mac) /
2730                IPv6(src=ep3.ip6.address, dst=ep1.ip6.address) /
2731                UDP(sport=1234, dport=1230) /
2732                Raw('\xa5' * 100))]
2733
2734         # should be dropped since no contract yet
2735         self.send_and_assert_no_replies(self.pg0, [p4[0]])
2736         self.send_and_assert_no_replies(self.pg0, [p6[0]])
2737
2738         #
2739         # Add a contract with a rule to load-balance redirect via SEP1 and SEP2
2740         # one of the next-hops is via an EP that is not known
2741         #
2742         acl = VppGbpAcl(self)
2743         rule4 = acl.create_rule(permit_deny=1, proto=17)
2744         rule6 = acl.create_rule(is_ipv6=1, permit_deny=1, proto=17)
2745         acl_index = acl.add_vpp_config([rule4, rule6])
2746
2747         #
2748         # test the src-ip hash mode
2749         #
2750         c1 = VppGbpContract(
2751             self, epg_220, epg_222, acl_index,
2752             [VppGbpContractRule(
2753                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
2754                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2755                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
2756                                        sep1.ip4, sep1.epg.rd),
2757                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
2758                                        sep2.ip4, sep2.epg.rd)]),
2759              VppGbpContractRule(
2760                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
2761                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2762                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
2763                                         sep3.ip6, sep3.epg.rd),
2764                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
2765                                         sep4.ip6, sep4.epg.rd)])],
2766             [ETH_P_IP, ETH_P_IPV6])
2767         c1.add_vpp_config()
2768
2769         c2 = VppGbpContract(
2770             self, epg_222, epg_220, acl_index,
2771             [VppGbpContractRule(
2772                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
2773                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2774                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
2775                                        sep1.ip4, sep1.epg.rd),
2776                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
2777                                        sep2.ip4, sep2.epg.rd)]),
2778              VppGbpContractRule(
2779                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
2780                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SRC_IP,
2781                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
2782                                         sep3.ip6, sep3.epg.rd),
2783                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
2784                                         sep4.ip6, sep4.epg.rd)])],
2785             [ETH_P_IP, ETH_P_IPV6])
2786         c2.add_vpp_config()
2787
2788         #
2789         # send again with the contract preset, now packets arrive
2790         # at SEP1 or SEP2 depending on the hashing
2791         #
2792         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
2793
2794         for rx in rxs:
2795             self.assertEqual(rx[Ether].src, routed_src_mac)
2796             self.assertEqual(rx[Ether].dst, sep1.mac)
2797             self.assertEqual(rx[IP].src, ep1.ip4.address)
2798             self.assertEqual(rx[IP].dst, ep3.ip4.address)
2799
2800         rxs = self.send_and_expect(self.pg2, p4[1] * 17, sep2.itf)
2801
2802         for rx in rxs:
2803             self.assertEqual(rx[Ether].src, routed_src_mac)
2804             self.assertEqual(rx[Ether].dst, sep2.mac)
2805             self.assertEqual(rx[IP].src, ep3.ip4.address)
2806             self.assertEqual(rx[IP].dst, ep1.ip4.address)
2807
2808         rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7)
2809
2810         for rx in rxs:
2811             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
2812             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
2813             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
2814             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
2815             self.assertEqual(rx[VXLAN].vni, 117)
2816             self.assertTrue(rx[VXLAN].flags.G)
2817             self.assertTrue(rx[VXLAN].flags.Instance)
2818             # redirect policy has been applied
2819             self.assertTrue(rx[VXLAN].gpflags.A)
2820             self.assertFalse(rx[VXLAN].gpflags.D)
2821
2822             inner = rx[VXLAN].payload
2823
2824             self.assertEqual(inner[Ether].src, routed_src_mac)
2825             self.assertEqual(inner[Ether].dst, sep4.mac)
2826             self.assertEqual(inner[IPv6].src, ep1.ip6.address)
2827             self.assertEqual(inner[IPv6].dst, ep3.ip6.address)
2828
2829         rxs = self.send_and_expect(self.pg2, p6[1] * 17, sep3.itf)
2830
2831         for rx in rxs:
2832             self.assertEqual(rx[Ether].src, routed_src_mac)
2833             self.assertEqual(rx[Ether].dst, sep3.mac)
2834             self.assertEqual(rx[IPv6].src, ep3.ip6.address)
2835             self.assertEqual(rx[IPv6].dst, ep1.ip6.address)
2836
2837         #
2838         # programme the unknown EP
2839         #
2840         sep4.add_vpp_config()
2841
2842         rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf)
2843
2844         for rx in rxs:
2845             self.assertEqual(rx[Ether].src, routed_src_mac)
2846             self.assertEqual(rx[Ether].dst, sep4.mac)
2847             self.assertEqual(rx[IPv6].src, ep1.ip6.address)
2848             self.assertEqual(rx[IPv6].dst, ep3.ip6.address)
2849
2850         #
2851         # and revert back to unprogrammed
2852         #
2853         sep4.remove_vpp_config()
2854
2855         rxs = self.send_and_expect(self.pg0, p6[0] * 17, self.pg7)
2856
2857         for rx in rxs:
2858             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
2859             self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
2860             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
2861             self.assertEqual(rx[IP].dst, self.pg7.remote_ip4)
2862             self.assertEqual(rx[VXLAN].vni, 117)
2863             self.assertTrue(rx[VXLAN].flags.G)
2864             self.assertTrue(rx[VXLAN].flags.Instance)
2865             # redirect policy has been applied
2866             self.assertTrue(rx[VXLAN].gpflags.A)
2867             self.assertFalse(rx[VXLAN].gpflags.D)
2868
2869             inner = rx[VXLAN].payload
2870
2871             self.assertEqual(inner[Ether].src, routed_src_mac)
2872             self.assertEqual(inner[Ether].dst, sep4.mac)
2873             self.assertEqual(inner[IPv6].src, ep1.ip6.address)
2874             self.assertEqual(inner[IPv6].dst, ep3.ip6.address)
2875
2876         c1.remove_vpp_config()
2877         c2.remove_vpp_config()
2878
2879         #
2880         # test the symmetric hash mode
2881         #
2882         c1 = VppGbpContract(
2883             self, epg_220, epg_222, acl_index,
2884             [VppGbpContractRule(
2885                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
2886                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
2887                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
2888                                        sep1.ip4, sep1.epg.rd),
2889                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
2890                                        sep2.ip4, sep2.epg.rd)]),
2891              VppGbpContractRule(
2892                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
2893                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
2894                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
2895                                         sep3.ip6, sep3.epg.rd),
2896                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
2897                                         sep4.ip6, sep4.epg.rd)])],
2898             [ETH_P_IP, ETH_P_IPV6])
2899         c1.add_vpp_config()
2900
2901         c2 = VppGbpContract(
2902             self, epg_222, epg_220, acl_index,
2903             [VppGbpContractRule(
2904                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
2905                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
2906                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
2907                                        sep1.ip4, sep1.epg.rd),
2908                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
2909                                        sep2.ip4, sep2.epg.rd)]),
2910              VppGbpContractRule(
2911                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
2912                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
2913                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
2914                                         sep3.ip6, sep3.epg.rd),
2915                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
2916                                         sep4.ip6, sep4.epg.rd)])],
2917             [ETH_P_IP, ETH_P_IPV6])
2918         c2.add_vpp_config()
2919
2920         #
2921         # send again with the contract preset, now packets arrive
2922         # at SEP1 for both directions
2923         #
2924         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
2925
2926         for rx in rxs:
2927             self.assertEqual(rx[Ether].src, routed_src_mac)
2928             self.assertEqual(rx[Ether].dst, sep1.mac)
2929             self.assertEqual(rx[IP].src, ep1.ip4.address)
2930             self.assertEqual(rx[IP].dst, ep3.ip4.address)
2931
2932         rxs = self.send_and_expect(self.pg2, p4[1] * 17, sep1.itf)
2933
2934         for rx in rxs:
2935             self.assertEqual(rx[Ether].src, routed_src_mac)
2936             self.assertEqual(rx[Ether].dst, sep1.mac)
2937             self.assertEqual(rx[IP].src, ep3.ip4.address)
2938             self.assertEqual(rx[IP].dst, ep1.ip4.address)
2939
2940         #
2941         # programme the unknown EP for the L3 tests
2942         #
2943         sep4.add_vpp_config()
2944
2945         #
2946         # an L3 switch packet between local EPs in different EPGs
2947         #  different dest ports on each so the are LB hashed differently
2948         #
2949         p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
2950                IP(src=ep1.ip4.address, dst=ep2.ip4.address) /
2951                UDP(sport=1234, dport=1234) /
2952                Raw('\xa5' * 100)),
2953               (Ether(src=ep2.mac, dst=str(self.router_mac)) /
2954                IP(src=ep2.ip4.address, dst=ep1.ip4.address) /
2955                UDP(sport=1234, dport=1234) /
2956                Raw('\xa5' * 100))]
2957         p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
2958                IPv6(src=ep1.ip6.address, dst=ep2.ip6.address) /
2959                UDP(sport=1234, dport=1234) /
2960                Raw('\xa5' * 100)),
2961               (Ether(src=ep2.mac, dst=str(self.router_mac)) /
2962                IPv6(src=ep2.ip6.address, dst=ep1.ip6.address) /
2963                UDP(sport=1234, dport=1234) /
2964                Raw('\xa5' * 100))]
2965
2966         c3 = VppGbpContract(
2967             self, epg_220, epg_221, acl_index,
2968             [VppGbpContractRule(
2969                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
2970                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
2971                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
2972                                        sep1.ip4, sep1.epg.rd),
2973                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
2974                                        sep2.ip4, sep2.epg.rd)]),
2975              VppGbpContractRule(
2976                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
2977                  VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_SYMMETRIC,
2978                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
2979                                         sep3.ip6, sep3.epg.rd),
2980                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
2981                                         sep4.ip6, sep4.epg.rd)])],
2982             [ETH_P_IP, ETH_P_IPV6])
2983         c3.add_vpp_config()
2984
2985         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
2986
2987         for rx in rxs:
2988             self.assertEqual(rx[Ether].src, routed_src_mac)
2989             self.assertEqual(rx[Ether].dst, sep1.mac)
2990             self.assertEqual(rx[IP].src, ep1.ip4.address)
2991             self.assertEqual(rx[IP].dst, ep2.ip4.address)
2992
2993         #
2994         # learn a remote EP in EPG 221
2995         #
2996         vx_tun_l3 = VppGbpVxlanTunnel(
2997             self, 444, rd1.rd_id,
2998             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
2999             self.pg2.local_ip4)
3000         vx_tun_l3.add_vpp_config()
3001
3002         c4 = VppGbpContract(
3003             self, epg_221, epg_220, acl_index,
3004             [VppGbpContractRule(
3005                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
3006                 []),
3007              VppGbpContractRule(
3008                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_PERMIT,
3009                  [])],
3010             [ETH_P_IP, ETH_P_IPV6])
3011         c4.add_vpp_config()
3012
3013         p = (Ether(src=self.pg7.remote_mac,
3014                    dst=self.pg7.local_mac) /
3015              IP(src=self.pg7.remote_ip4,
3016                 dst=self.pg7.local_ip4) /
3017              UDP(sport=1234, dport=48879) /
3018              VXLAN(vni=444, gpid=441, flags=0x88) /
3019              Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) /
3020              IP(src="10.0.0.88", dst=ep1.ip4.address) /
3021              UDP(sport=1234, dport=1234) /
3022              Raw('\xa5' * 100))
3023
3024         rx = self.send_and_expect(self.pg7, [p], self.pg0)
3025
3026         # endpoint learnt via the parent GBP-vxlan interface
3027         self.assertTrue(find_gbp_endpoint(self,
3028                                           vx_tun_l3._sw_if_index,
3029                                           ip="10.0.0.88"))
3030
3031         p = (Ether(src=self.pg7.remote_mac,
3032                    dst=self.pg7.local_mac) /
3033              IP(src=self.pg7.remote_ip4,
3034                 dst=self.pg7.local_ip4) /
3035              UDP(sport=1234, dport=48879) /
3036              VXLAN(vni=444, gpid=441, flags=0x88) /
3037              Ether(src="00:22:22:22:22:33", dst=str(self.router_mac)) /
3038              IPv6(src="2001:10::88", dst=ep1.ip6.address) /
3039              UDP(sport=1234, dport=1234) /
3040              Raw('\xa5' * 100))
3041
3042         rx = self.send_and_expect(self.pg7, [p], self.pg0)
3043
3044         # endpoint learnt via the parent GBP-vxlan interface
3045         self.assertTrue(find_gbp_endpoint(self,
3046                                           vx_tun_l3._sw_if_index,
3047                                           ip="2001:10::88"))
3048
3049         #
3050         # L3 switch from local to remote EP
3051         #
3052         p4 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
3053                IP(src=ep1.ip4.address, dst="10.0.0.88") /
3054                UDP(sport=1234, dport=1234) /
3055                Raw('\xa5' * 100))]
3056         p6 = [(Ether(src=ep1.mac, dst=str(self.router_mac)) /
3057                IPv6(src=ep1.ip6.address, dst="2001:10::88") /
3058                UDP(sport=1234, dport=1234) /
3059                Raw('\xa5' * 100))]
3060
3061         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
3062
3063         for rx in rxs:
3064             self.assertEqual(rx[Ether].src, routed_src_mac)
3065             self.assertEqual(rx[Ether].dst, sep1.mac)
3066             self.assertEqual(rx[IP].src, ep1.ip4.address)
3067             self.assertEqual(rx[IP].dst, "10.0.0.88")
3068
3069         rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep4.itf)
3070
3071         for rx in rxs:
3072             self.assertEqual(rx[Ether].src, routed_src_mac)
3073             self.assertEqual(rx[Ether].dst, sep4.mac)
3074             self.assertEqual(rx[IPv6].src, ep1.ip6.address)
3075             self.assertEqual(rx[IPv6].dst, "2001:10::88")
3076
3077         #
3078         # test the dst-ip hash mode
3079         #
3080         c5 = VppGbpContract(
3081             self, epg_220, epg_221, acl_index,
3082             [VppGbpContractRule(
3083                 VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3084                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
3085                 [VppGbpContractNextHop(sep1.vmac, sep1.epg.bd,
3086                                        sep1.ip4, sep1.epg.rd),
3087                  VppGbpContractNextHop(sep2.vmac, sep2.epg.bd,
3088                                        sep2.ip4, sep2.epg.rd)]),
3089              VppGbpContractRule(
3090                  VppEnum.vl_api_gbp_rule_action_t.GBP_API_RULE_REDIRECT,
3091                 VppEnum.vl_api_gbp_hash_mode_t.GBP_API_HASH_MODE_DST_IP,
3092                  [VppGbpContractNextHop(sep3.vmac, sep3.epg.bd,
3093                                         sep3.ip6, sep3.epg.rd),
3094                   VppGbpContractNextHop(sep4.vmac, sep4.epg.bd,
3095                                         sep4.ip6, sep4.epg.rd)])],
3096             [ETH_P_IP, ETH_P_IPV6])
3097         c5.add_vpp_config()
3098
3099         rxs = self.send_and_expect(self.pg0, p4[0] * 17, sep1.itf)
3100
3101         for rx in rxs:
3102             self.assertEqual(rx[Ether].src, routed_src_mac)
3103             self.assertEqual(rx[Ether].dst, sep1.mac)
3104             self.assertEqual(rx[IP].src, ep1.ip4.address)
3105             self.assertEqual(rx[IP].dst, "10.0.0.88")
3106
3107         rxs = self.send_and_expect(self.pg0, p6[0] * 17, sep3.itf)
3108
3109         for rx in rxs:
3110             self.assertEqual(rx[Ether].src, routed_src_mac)
3111             self.assertEqual(rx[Ether].dst, sep3.mac)
3112             self.assertEqual(rx[IPv6].src, ep1.ip6.address)
3113             self.assertEqual(rx[IPv6].dst, "2001:10::88")
3114
3115         #
3116         # cleanup
3117         #
3118         self.pg7.unconfig_ip4()
3119
3120     def test_gbp_l3_out(self):
3121         """ GBP L3 Out """
3122
3123         ep_flags = VppEnum.vl_api_gbp_endpoint_flags_t
3124         self.vapi.cli("set logging class gbp debug")
3125
3126         routed_dst_mac = "00:0c:0c:0c:0c:0c"
3127         routed_src_mac = "00:22:bd:f8:19:ff"
3128
3129         #
3130         # IP tables
3131         #
3132         t4 = VppIpTable(self, 1)
3133         t4.add_vpp_config()
3134         t6 = VppIpTable(self, 1, True)
3135         t6.add_vpp_config()
3136
3137         rd1 = VppGbpRouteDomain(self, 2, t4, t6)
3138         rd1.add_vpp_config()
3139
3140         self.loop0.set_mac(self.router_mac)
3141
3142         #
3143         # Bind the BVI to the RD
3144         #
3145         VppIpInterfaceBind(self, self.loop0, t4).add_vpp_config()
3146         VppIpInterfaceBind(self, self.loop0, t6).add_vpp_config()
3147
3148         #
3149         # Pg7 hosts a BD's BUM
3150         # Pg1 some other l3 interface
3151         #
3152         self.pg7.config_ip4()
3153         self.pg7.resolve_arp()
3154
3155         #
3156         # a multicast vxlan-gbp tunnel for broadcast in the BD
3157         #
3158         tun_bm = VppVxlanGbpTunnel(self, self.pg7.local_ip4,
3159                                    "239.1.1.1", 88,
3160                                    mcast_itf=self.pg7)
3161         tun_bm.add_vpp_config()
3162
3163         #
3164         # a GBP external bridge domains for the EPs
3165         #
3166         bd1 = VppBridgeDomain(self, 1)
3167         bd1.add_vpp_config()
3168         gbd1 = VppGbpBridgeDomain(self, bd1, self.loop0, None, tun_bm)
3169         gbd1.add_vpp_config()
3170
3171         #
3172         # The Endpoint-groups in which the external endpoints exist
3173         #
3174         epg_220 = VppGbpEndpointGroup(self, 220, 113, rd1, gbd1,
3175                                       None, gbd1.bvi,
3176                                       "10.0.0.128",
3177                                       "2001:10::128",
3178                                       VppGbpEndpointRetention(2))
3179         epg_220.add_vpp_config()
3180
3181         # the BVIs have the subnets applied ...
3182         ip4_addr = VppIpInterfaceAddress(self, gbd1.bvi, "10.0.0.128", 24)
3183         ip4_addr.add_vpp_config()
3184         ip6_addr = VppIpInterfaceAddress(self, gbd1.bvi, "2001:10::128", 64)
3185         ip6_addr.add_vpp_config()
3186
3187         # ... which are L3-out subnets
3188         l3o_1 = VppGbpSubnet(
3189             self, rd1, "10.0.0.0", 24,
3190             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
3191             epg=epg_220)
3192         l3o_1.add_vpp_config()
3193
3194         #
3195         # an external interface attached to the outside world and the
3196         # external BD
3197         #
3198         vlan_100 = VppDot1QSubint(self, self.pg0, 100)
3199         vlan_100.admin_up()
3200         ext_itf = VppGbpExtItf(self, vlan_100, bd1, rd1)
3201         ext_itf.add_vpp_config()
3202
3203         #
3204         # an unicast vxlan-gbp for inter-RD traffic
3205         #
3206         vx_tun_l3 = VppGbpVxlanTunnel(
3207             self, 444, rd1.rd_id,
3208             VppEnum.vl_api_gbp_vxlan_tunnel_mode_t.GBP_VXLAN_TUNNEL_MODE_L3,
3209             self.pg2.local_ip4)
3210         vx_tun_l3.add_vpp_config()
3211
3212         #
3213         # packets destined to unknown addresses in the BVI's subnet
3214         # are ARP'd for
3215         #
3216         p4 = (Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
3217               Dot1Q(vlan=100) /
3218               IP(src="10.0.0.1", dst="10.0.0.88") /
3219               UDP(sport=1234, dport=1234) /
3220               Raw('\xa5' * 100))
3221         p6 = (Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
3222               Dot1Q(vlan=100) /
3223               IPv6(src="2001:10::1", dst="2001:10::88") /
3224               UDP(sport=1234, dport=1234) /
3225               Raw('\xa5' * 100))
3226
3227         rxs = self.send_and_expect(self.pg0, p4 * 1, self.pg7)
3228
3229         for rx in rxs:
3230             self.assertEqual(rx[Ether].src, self.pg7.local_mac)
3231             # self.assertEqual(rx[Ether].dst, self.pg7.remote_mac)
3232             self.assertEqual(rx[IP].src, self.pg7.local_ip4)
3233             self.assertEqual(rx[IP].dst, "239.1.1.1")
3234             self.assertEqual(rx[VXLAN].vni, 88)
3235             self.assertTrue(rx[VXLAN].flags.G)
3236             self.assertTrue(rx[VXLAN].flags.Instance)
3237             # policy was applied to the original IP packet
3238             self.assertEqual(rx[VXLAN].gpid, 113)
3239             self.assertTrue(rx[VXLAN].gpflags.A)
3240             self.assertFalse(rx[VXLAN].gpflags.D)
3241
3242             inner = rx[VXLAN].payload
3243
3244             self.assertTrue(inner.haslayer(ARP))
3245
3246         #
3247         # An external Endpoint
3248         #
3249         eep = VppGbpEndpoint(self, vlan_100,
3250                              epg_220, None,
3251                              "10.0.0.1", "11.0.0.1",
3252                              "2001:10::1", "3001::1",
3253                              ep_flags.GBP_API_ENDPOINT_FLAG_EXTERNAL)
3254         eep.add_vpp_config()
3255
3256         #
3257         # A remote endpoint
3258         #
3259         rep = VppGbpEndpoint(self, vx_tun_l3,
3260                              epg_220, None,
3261                              "10.0.0.101", "11.0.0.101",
3262                              "2001:10::101", "3001::101",
3263                              ep_flags.GBP_API_ENDPOINT_FLAG_REMOTE,
3264                              self.pg7.local_ip4,
3265                              self.pg7.remote_ip4,
3266                              mac=None)
3267         rep.add_vpp_config()
3268
3269         #
3270         # remote to external
3271         #
3272         p = (Ether(src=self.pg7.remote_mac,
3273                    dst=self.pg7.local_mac) /
3274              IP(src=self.pg7.remote_ip4,
3275                 dst=self.pg7.local_ip4) /
3276              UDP(sport=1234, dport=48879) /
3277              VXLAN(vni=444, gpid=113, flags=0x88) /
3278              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
3279              IP(src="10.0.0.101", dst="10.0.0.1") /
3280              UDP(sport=1234, dport=1234) /
3281              Raw('\xa5' * 100))
3282
3283         rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
3284
3285         #
3286         # A subnet reachable through the external EP
3287         #
3288         ip_220 = VppIpRoute(self, "10.220.0.0", 24,
3289                             [VppRoutePath(eep.ip4.address,
3290                                           eep.epg.bvi.sw_if_index)],
3291                             table_id=t4.table_id)
3292         ip_220.add_vpp_config()
3293
3294         l3o_220 = VppGbpSubnet(
3295             self, rd1, "10.220.0.0", 24,
3296             VppEnum.vl_api_gbp_subnet_type_t.GBP_API_SUBNET_L3_OUT,
3297             epg=epg_220)
3298         l3o_220.add_vpp_config()
3299
3300         p = (Ether(src=self.pg7.remote_mac,
3301                    dst=self.pg7.local_mac) /
3302              IP(src=self.pg7.remote_ip4,
3303                 dst=self.pg7.local_ip4) /
3304              UDP(sport=1234, dport=48879) /
3305              VXLAN(vni=444, gpid=113, flags=0x88) /
3306              Ether(src=self.pg0.remote_mac, dst=str(self.router_mac)) /
3307              IP(src="10.0.0.101", dst="10.220.0.1") /
3308              UDP(sport=1234, dport=1234) /
3309              Raw('\xa5' * 100))
3310
3311         rxs = self.send_and_expect(self.pg7, p * 1, self.pg0)
3312
3313         #
3314         # cleanup
3315         #
3316         self.pg7.unconfig_ip4()
3317
3318
3319 if __name__ == '__main__':
3320     unittest.main(testRunner=VppTestRunner)