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