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