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