tests: replace pycodestyle with black
[vpp.git] / test / test_lb.py
1 import socket
2
3 import scapy.compat
4 from scapy.layers.inet import IP, UDP
5 from scapy.layers.inet6 import IPv6
6 from scapy.layers.l2 import Ether, GRE
7 from scapy.packet import Raw
8 from scapy.data import IP_PROTOS
9
10 from framework import VppTestCase
11 from util import ppp
12 from vpp_ip_route import VppIpRoute, VppRoutePath
13 from vpp_ip import INVALID_INDEX
14
15 """ TestLB is a subclass of  VPPTestCase classes.
16
17  TestLB class defines Load Balancer test cases for:
18   - IP4 to GRE4 encap on per-port vip case
19   - IP4 to GRE6 encap on per-port vip case
20   - IP6 to GRE4 encap on per-port vip case
21   - IP6 to GRE6 encap on per-port vip case
22   - IP4 to L3DSR encap on vip case
23   - IP4 to L3DSR encap on per-port vip case
24   - IP4 to NAT4 encap on per-port vip case
25   - IP6 to NAT6 encap on per-port vip case
26
27  As stated in comments below, GRE has issues with IPv6.
28  All test cases involving IPv6 are executed, but
29  received packets are not parsed and checked.
30
31 """
32
33
34 class TestLB(VppTestCase):
35     """Load Balancer Test Case"""
36
37     @classmethod
38     def setUpClass(cls):
39         super(TestLB, cls).setUpClass()
40
41         cls.ass = range(5)
42         cls.packets = range(1)
43
44         try:
45             cls.create_pg_interfaces(range(2))
46             cls.interfaces = list(cls.pg_interfaces)
47
48             for i in cls.interfaces:
49                 i.admin_up()
50                 i.config_ip4()
51                 i.config_ip6()
52                 i.disable_ipv6_ra()
53                 i.resolve_arp()
54                 i.resolve_ndp()
55
56             dst4 = VppIpRoute(
57                 cls,
58                 "10.0.0.0",
59                 24,
60                 [VppRoutePath(cls.pg1.remote_ip4, INVALID_INDEX)],
61                 register=False,
62             )
63             dst4.add_vpp_config()
64             dst6 = VppIpRoute(
65                 cls,
66                 "2002::",
67                 16,
68                 [VppRoutePath(cls.pg1.remote_ip6, INVALID_INDEX)],
69                 register=False,
70             )
71             dst6.add_vpp_config()
72             cls.vapi.lb_conf(ip4_src_address="39.40.41.42", ip6_src_address="2004::1")
73         except Exception:
74             super(TestLB, cls).tearDownClass()
75             raise
76
77     @classmethod
78     def tearDownClass(cls):
79         super(TestLB, cls).tearDownClass()
80
81     def tearDown(self):
82         super(TestLB, self).tearDown()
83
84     def show_commands_at_teardown(self):
85         self.logger.info(self.vapi.cli("show lb vip verbose"))
86
87     def getIPv4Flow(self, id):
88         return IP(
89             dst="90.0.%u.%u" % (id / 255, id % 255),
90             src="40.0.%u.%u" % (id / 255, id % 255),
91         ) / UDP(sport=10000 + id, dport=20000)
92
93     def getIPv6Flow(self, id):
94         return IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) / UDP(
95             sport=10000 + id, dport=20000
96         )
97
98     def generatePackets(self, src_if, isv4):
99         self.reset_packet_infos()
100         pkts = []
101         for pktid in self.packets:
102             info = self.create_packet_info(src_if, self.pg1)
103             payload = self.info_to_payload(info)
104             ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
105             packet = (
106                 Ether(dst=src_if.local_mac, src=src_if.remote_mac) / ip / Raw(payload)
107             )
108             self.extend_packet(packet, 128)
109             info.data = packet.copy()
110             pkts.append(packet)
111         return pkts
112
113     def checkInner(self, gre, isv4):
114         IPver = IP if isv4 else IPv6
115         self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
116         self.assertEqual(gre.flags, 0)
117         self.assertEqual(gre.version, 0)
118         inner = IPver(scapy.compat.raw(gre.payload))
119         payload_info = self.payload_to_info(inner[Raw])
120         self.info = self.packet_infos[payload_info.index]
121         self.assertEqual(payload_info.src, self.pg0.sw_if_index)
122         self.assertEqual(
123             scapy.compat.raw(inner), scapy.compat.raw(self.info.data[IPver])
124         )
125
126     def checkCapture(self, encap, isv4):
127         self.pg0.assert_nothing_captured()
128         out = self.pg1.get_capture(len(self.packets))
129
130         load = [0] * len(self.ass)
131         self.info = None
132         for p in out:
133             try:
134                 asid = 0
135                 gre = None
136                 if encap == "gre4":
137                     ip = p[IP]
138                     asid = int(ip.dst.split(".")[3])
139                     self.assertEqual(ip.version, 4)
140                     self.assertEqual(ip.flags, 0)
141                     self.assertEqual(ip.src, "39.40.41.42")
142                     self.assertEqual(ip.dst, "10.0.0.%u" % asid)
143                     self.assertEqual(ip.proto, 47)
144                     self.assertEqual(len(ip.options), 0)
145                     gre = p[GRE]
146                     self.checkInner(gre, isv4)
147                 elif encap == "gre6":
148                     ip = p[IPv6]
149                     asid = ip.dst.split(":")
150                     asid = asid[len(asid) - 1]
151                     asid = 0 if asid == "" else int(asid)
152                     self.assertEqual(ip.version, 6)
153                     self.assertEqual(ip.tc, 0)
154                     self.assertEqual(ip.fl, 0)
155                     self.assertEqual(ip.src, "2004::1")
156                     self.assertEqual(
157                         socket.inet_pton(socket.AF_INET6, ip.dst),
158                         socket.inet_pton(socket.AF_INET6, "2002::%u" % asid),
159                     )
160                     self.assertEqual(ip.nh, 47)
161                     # self.assertEqual(len(ip.options), 0)
162                     gre = GRE(scapy.compat.raw(p[IPv6].payload))
163                     self.checkInner(gre, isv4)
164                 elif encap == "l3dsr":
165                     ip = p[IP]
166                     asid = int(ip.dst.split(".")[3])
167                     self.assertEqual(ip.version, 4)
168                     self.assertEqual(ip.flags, 0)
169                     self.assertEqual(ip.dst, "10.0.0.%u" % asid)
170                     self.assertEqual(ip.tos, 0x1C)
171                     self.assertEqual(len(ip.options), 0)
172                     self.assert_ip_checksum_valid(p)
173                     if ip.proto == IP_PROTOS.tcp:
174                         self.assert_tcp_checksum_valid(p)
175                     elif ip.proto == IP_PROTOS.udp:
176                         self.assert_udp_checksum_valid(p)
177                 elif encap == "nat4":
178                     ip = p[IP]
179                     asid = int(ip.dst.split(".")[3])
180                     self.assertEqual(ip.version, 4)
181                     self.assertEqual(ip.flags, 0)
182                     self.assertEqual(ip.dst, "10.0.0.%u" % asid)
183                     self.assertEqual(ip.proto, 17)
184                     self.assertEqual(len(ip.options), 0)
185                     udp = p[UDP]
186                     self.assertEqual(udp.dport, 3307)
187                 elif encap == "nat6":
188                     ip = p[IPv6]
189                     asid = ip.dst.split(":")
190                     asid = asid[len(asid) - 1]
191                     asid = 0 if asid == "" else int(asid)
192                     self.assertEqual(ip.version, 6)
193                     self.assertEqual(ip.tc, 0)
194                     self.assertEqual(ip.fl, 0)
195                     self.assertEqual(
196                         socket.inet_pton(socket.AF_INET6, ip.dst),
197                         socket.inet_pton(socket.AF_INET6, "2002::%u" % asid),
198                     )
199                     self.assertEqual(ip.nh, 17)
200                     self.assertGreaterEqual(ip.hlim, 63)
201                     udp = UDP(scapy.compat.raw(p[IPv6].payload))
202                     self.assertEqual(udp.dport, 3307)
203                 load[asid] += 1
204             except:
205                 self.logger.error(ppp("Unexpected or invalid packet:", p))
206                 raise
207
208         # This is just to roughly check that the balancing algorithm
209         # is not completely biased.
210         for asid in self.ass:
211             if load[asid] < int(len(self.packets) / (len(self.ass) * 2)):
212                 self.logger.error(
213                     "ASS is not balanced: load[%d] = %d" % (asid, load[asid])
214                 )
215                 raise Exception("Load Balancer algorithm is biased")
216
217     def test_lb_ip4_gre4(self):
218         """Load Balancer IP4 GRE4 on vip case"""
219         try:
220             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4")
221             for asid in self.ass:
222                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
223
224             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
225             self.pg_enable_capture(self.pg_interfaces)
226             self.pg_start()
227             self.checkCapture(encap="gre4", isv4=True)
228
229         finally:
230             for asid in self.ass:
231                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
232             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4 del")
233             self.vapi.cli("test lb flowtable flush")
234
235     def test_lb_ip6_gre4(self):
236         """Load Balancer IP6 GRE4 on vip case"""
237
238         try:
239             self.vapi.cli("lb vip 2001::/16 encap gre4")
240             for asid in self.ass:
241                 self.vapi.cli("lb as 2001::/16 10.0.0.%u" % (asid))
242
243             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
244             self.pg_enable_capture(self.pg_interfaces)
245             self.pg_start()
246
247             self.checkCapture(encap="gre4", isv4=False)
248         finally:
249             for asid in self.ass:
250                 self.vapi.cli("lb as 2001::/16 10.0.0.%u del" % (asid))
251             self.vapi.cli("lb vip 2001::/16 encap gre4 del")
252             self.vapi.cli("test lb flowtable flush")
253
254     def test_lb_ip4_gre6(self):
255         """Load Balancer IP4 GRE6 on vip case"""
256         try:
257             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6")
258             for asid in self.ass:
259                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u" % (asid))
260
261             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
262             self.pg_enable_capture(self.pg_interfaces)
263             self.pg_start()
264
265             self.checkCapture(encap="gre6", isv4=True)
266         finally:
267             for asid in self.ass:
268                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u del" % (asid))
269             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6 del")
270             self.vapi.cli("test lb flowtable flush")
271
272     def test_lb_ip6_gre6(self):
273         """Load Balancer IP6 GRE6 on vip case"""
274         try:
275             self.vapi.cli("lb vip 2001::/16 encap gre6")
276             for asid in self.ass:
277                 self.vapi.cli("lb as 2001::/16 2002::%u" % (asid))
278
279             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
280             self.pg_enable_capture(self.pg_interfaces)
281             self.pg_start()
282
283             self.checkCapture(encap="gre6", isv4=False)
284         finally:
285             for asid in self.ass:
286                 self.vapi.cli("lb as 2001::/16 2002::%u del" % (asid))
287             self.vapi.cli("lb vip 2001::/16 encap gre6 del")
288             self.vapi.cli("test lb flowtable flush")
289
290     def test_lb_ip4_gre4_port(self):
291         """Load Balancer IP4 GRE4 on per-port-vip case"""
292         try:
293             self.vapi.cli("lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4")
294             for asid in self.ass:
295                 self.vapi.cli(
296                     "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u" % (asid)
297                 )
298
299             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
300             self.pg_enable_capture(self.pg_interfaces)
301             self.pg_start()
302             self.checkCapture(encap="gre4", isv4=True)
303
304         finally:
305             for asid in self.ass:
306                 self.vapi.cli(
307                     "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del" % (asid)
308                 )
309             self.vapi.cli("lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4 del")
310             self.vapi.cli("test lb flowtable flush")
311
312     def test_lb_ip6_gre4_port(self):
313         """Load Balancer IP6 GRE4 on per-port-vip case"""
314
315         try:
316             self.vapi.cli("lb vip 2001::/16 protocol udp port 20000 encap gre4")
317             for asid in self.ass:
318                 self.vapi.cli(
319                     "lb as 2001::/16 protocol udp port 20000 10.0.0.%u" % (asid)
320                 )
321
322             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
323             self.pg_enable_capture(self.pg_interfaces)
324             self.pg_start()
325
326             self.checkCapture(encap="gre4", isv4=False)
327         finally:
328             for asid in self.ass:
329                 self.vapi.cli(
330                     "lb as 2001::/16 protocol udp port 20000 10.0.0.%u del" % (asid)
331                 )
332             self.vapi.cli("lb vip 2001::/16 protocol udp port 20000 encap gre4 del")
333             self.vapi.cli("test lb flowtable flush")
334
335     def test_lb_ip4_gre6_port(self):
336         """Load Balancer IP4 GRE6 on per-port-vip case"""
337         try:
338             self.vapi.cli("lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6")
339             for asid in self.ass:
340                 self.vapi.cli(
341                     "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u" % (asid)
342                 )
343
344             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
345             self.pg_enable_capture(self.pg_interfaces)
346             self.pg_start()
347
348             self.checkCapture(encap="gre6", isv4=True)
349         finally:
350             for asid in self.ass:
351                 self.vapi.cli(
352                     "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u del" % (asid)
353                 )
354             self.vapi.cli("lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6 del")
355             self.vapi.cli("test lb flowtable flush")
356
357     def test_lb_ip6_gre6_port(self):
358         """Load Balancer IP6 GRE6 on per-port-vip case"""
359         try:
360             self.vapi.cli("lb vip 2001::/16 protocol udp port 20000 encap gre6")
361             for asid in self.ass:
362                 self.vapi.cli(
363                     "lb as 2001::/16 protocol udp port 20000 2002::%u" % (asid)
364                 )
365
366             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
367             self.pg_enable_capture(self.pg_interfaces)
368             self.pg_start()
369
370             self.checkCapture(encap="gre6", isv4=False)
371         finally:
372             for asid in self.ass:
373                 self.vapi.cli(
374                     "lb as 2001::/16 protocol udp port 20000 2002::%u del" % (asid)
375                 )
376             self.vapi.cli("lb vip 2001::/16 protocol udp port 20000 encap gre6 del")
377             self.vapi.cli("test lb flowtable flush")
378
379     def test_lb_ip4_l3dsr(self):
380         """Load Balancer IP4 L3DSR on vip case"""
381         try:
382             self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7")
383             for asid in self.ass:
384                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
385
386             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
387             self.pg_enable_capture(self.pg_interfaces)
388             self.pg_start()
389             self.checkCapture(encap="l3dsr", isv4=True)
390
391         finally:
392             for asid in self.ass:
393                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
394             self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7 del")
395             self.vapi.cli("test lb flowtable flush")
396
397     def test_lb_ip4_l3dsr_port(self):
398         """Load Balancer IP4 L3DSR on per-port-vip case"""
399         try:
400             self.vapi.cli(
401                 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr dscp 7"
402             )
403             for asid in self.ass:
404                 self.vapi.cli(
405                     "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u" % (asid)
406                 )
407
408             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
409             self.pg_enable_capture(self.pg_interfaces)
410             self.pg_start()
411             self.checkCapture(encap="l3dsr", isv4=True)
412
413         finally:
414             for asid in self.ass:
415                 self.vapi.cli(
416                     "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del" % (asid)
417                 )
418             self.vapi.cli(
419                 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr dscp 7 del"
420             )
421             self.vapi.cli("test lb flowtable flush")
422
423     def test_lb_ip4_nat4_port(self):
424         """Load Balancer IP4 NAT4 on per-port-vip case"""
425         try:
426             self.vapi.cli(
427                 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
428                 " type clusterip target_port 3307"
429             )
430             for asid in self.ass:
431                 self.vapi.cli(
432                     "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u" % (asid)
433                 )
434
435             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
436             self.pg_enable_capture(self.pg_interfaces)
437             self.pg_start()
438             self.checkCapture(encap="nat4", isv4=True)
439
440         finally:
441             for asid in self.ass:
442                 self.vapi.cli(
443                     "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del" % (asid)
444                 )
445             self.vapi.cli(
446                 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
447                 " type clusterip target_port 3307 del"
448             )
449             self.vapi.cli("test lb flowtable flush")
450
451     def test_lb_ip6_nat6_port(self):
452         """Load Balancer IP6 NAT6 on per-port-vip case"""
453         try:
454             self.vapi.cli(
455                 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
456                 " type clusterip target_port 3307"
457             )
458             for asid in self.ass:
459                 self.vapi.cli(
460                     "lb as 2001::/16 protocol udp port 20000 2002::%u" % (asid)
461                 )
462
463             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
464             self.pg_enable_capture(self.pg_interfaces)
465             self.pg_start()
466             self.checkCapture(encap="nat6", isv4=False)
467
468         finally:
469             for asid in self.ass:
470                 self.vapi.cli(
471                     "lb as 2001::/16 protocol udp port 20000 2002::%u del" % (asid)
472                 )
473             self.vapi.cli(
474                 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
475                 " type clusterip target_port 3307 del"
476             )
477             self.vapi.cli("test lb flowtable flush")