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