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