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