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