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