Rework kube-proxy into LB plugin
[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
8 from framework import VppTestCase
9 from util import ppp
10
11 """ TestLB is a subclass of  VPPTestCase classes.
12
13  TestLB class defines Load Balancer test cases for:
14   - IP4 to GRE4 encap
15   - IP4 to GRE6 encap
16   - IP6 to GRE4 encap
17   - IP6 to GRE6 encap
18   - IP4 to L3DSR encap
19   - IP4 to NAT4 encap
20   - IP6 to NAT6 encap
21
22  As stated in comments below, GRE has issues with IPv6.
23  All test cases involving IPv6 are executed, but
24  received packets are not parsed and checked.
25
26 """
27
28
29 class TestLB(VppTestCase):
30     """ Load Balancer Test Case """
31
32     @classmethod
33     def setUpClass(cls):
34         super(TestLB, cls).setUpClass()
35
36         cls.ass = range(5)
37         cls.packets = range(100)
38
39         try:
40             cls.create_pg_interfaces(range(2))
41             cls.interfaces = list(cls.pg_interfaces)
42
43             for i in cls.interfaces:
44                 i.admin_up()
45                 i.config_ip4()
46                 i.config_ip6()
47                 i.disable_ipv6_ra()
48                 i.resolve_arp()
49                 i.resolve_ndp()
50             dst4 = socket.inet_pton(socket.AF_INET, "10.0.0.0")
51             dst6 = socket.inet_pton(socket.AF_INET6, "2002::")
52             cls.vapi.ip_add_del_route(dst4, 24, cls.pg1.remote_ip4n)
53             cls.vapi.ip_add_del_route(dst6, 16, cls.pg1.remote_ip6n, is_ipv6=1)
54             cls.vapi.cli("lb conf ip4-src-address 39.40.41.42")
55             cls.vapi.cli("lb conf ip6-src-address 2004::1")
56         except Exception:
57             super(TestLB, cls).tearDownClass()
58             raise
59
60     def tearDown(self):
61         super(TestLB, self).tearDown()
62         if not self.vpp_dead:
63             self.logger.info(self.vapi.cli("show lb vip verbose"))
64
65     def getIPv4Flow(self, id):
66         return (IP(dst="90.0.%u.%u" % (id / 255, id % 255),
67                    src="40.0.%u.%u" % (id / 255, id % 255)) /
68                 UDP(sport=10000 + id, dport=20000 + id))
69
70     def getIPv6Flow(self, id):
71         return (IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) /
72                 UDP(sport=10000 + id, dport=20000 + id))
73
74     def generatePackets(self, src_if, isv4):
75         self.reset_packet_infos()
76         pkts = []
77         for pktid in self.packets:
78             info = self.create_packet_info(src_if, self.pg1)
79             payload = self.info_to_payload(info)
80             ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
81             packet = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
82                       ip /
83                       Raw(payload))
84             self.extend_packet(packet, 128)
85             info.data = packet.copy()
86             pkts.append(packet)
87         return pkts
88
89     def checkInner(self, gre, isv4):
90         IPver = IP if isv4 else IPv6
91         self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
92         self.assertEqual(gre.flags, 0)
93         self.assertEqual(gre.version, 0)
94         inner = IPver(str(gre.payload))
95         payload_info = self.payload_to_info(str(inner[Raw]))
96         self.info = self.packet_infos[payload_info.index]
97         self.assertEqual(payload_info.src, self.pg0.sw_if_index)
98         self.assertEqual(str(inner), str(self.info.data[IPver]))
99
100     def checkCapture(self, encap, isv4):
101         self.pg0.assert_nothing_captured()
102         out = self.pg1.get_capture(len(self.packets))
103
104         load = [0] * len(self.ass)
105         self.info = None
106         for p in out:
107             try:
108                 asid = 0
109                 gre = None
110                 if (encap == 'gre4'):
111                     ip = p[IP]
112                     asid = int(ip.dst.split(".")[3])
113                     self.assertEqual(ip.version, 4)
114                     self.assertEqual(ip.flags, 0)
115                     self.assertEqual(ip.src, "39.40.41.42")
116                     self.assertEqual(ip.dst, "10.0.0.%u" % asid)
117                     self.assertEqual(ip.proto, 47)
118                     self.assertEqual(len(ip.options), 0)
119                     self.assertGreaterEqual(ip.ttl, 64)
120                     gre = p[GRE]
121                     self.checkInner(gre, isv4)
122                 elif (encap == 'gre6'):
123                     ip = p[IPv6]
124                     asid = ip.dst.split(":")
125                     asid = asid[len(asid) - 1]
126                     asid = 0 if asid == "" else int(asid)
127                     self.assertEqual(ip.version, 6)
128                     self.assertEqual(ip.tc, 0)
129                     self.assertEqual(ip.fl, 0)
130                     self.assertEqual(ip.src, "2004::1")
131                     self.assertEqual(
132                         socket.inet_pton(socket.AF_INET6, ip.dst),
133                         socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
134                     )
135                     self.assertEqual(ip.nh, 47)
136                     self.assertGreaterEqual(ip.hlim, 64)
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                 elif (encap == 'nat4'):
149                     ip = p[IP]
150                     asid = int(ip.dst.split(".")[3])
151                     self.assertEqual(ip.version, 4)
152                     self.assertEqual(ip.flags, 0)
153                     self.assertEqual(ip.dst, "10.0.0.%u" % asid)
154                     self.assertEqual(ip.proto, 17)
155                     self.assertEqual(len(ip.options), 0)
156                     self.assertGreaterEqual(ip.ttl, 63)
157                     udp = p[UDP]
158                     self.assertEqual(udp.dport, 3307)
159                 elif (encap == 'nat6'):
160                     ip = p[IPv6]
161                     asid = ip.dst.split(":")
162                     asid = asid[len(asid) - 1]
163                     asid = 0 if asid == "" else int(asid)
164                     self.assertEqual(ip.version, 6)
165                     self.assertEqual(ip.tc, 0)
166                     self.assertEqual(ip.fl, 0)
167                     self.assertEqual(
168                         socket.inet_pton(socket.AF_INET6, ip.dst),
169                         socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
170                     )
171                     self.assertEqual(ip.nh, 17)
172                     self.assertGreaterEqual(ip.hlim, 63)
173                     udp = UDP(str(p[IPv6].payload))
174                     self.assertEqual(udp.dport, 3307)
175                 load[asid] += 1
176             except:
177                 self.logger.error(ppp("Unexpected or invalid packet:", p))
178                 raise
179
180         # This is just to roughly check that the balancing algorithm
181         # is not completly biased.
182         for asid in self.ass:
183             if load[asid] < len(self.packets) / (len(self.ass) * 2):
184                 self.logger.error(
185                     "ASS is not balanced: load[%d] = %d" % (asid, load[asid]))
186                 raise Exception("Load Balancer algorithm is biased")
187
188     def test_lb_ip4_gre4(self):
189         """ Load Balancer IP4 GRE4 """
190         try:
191             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4")
192             for asid in self.ass:
193                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
194
195             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
196             self.pg_enable_capture(self.pg_interfaces)
197             self.pg_start()
198             self.checkCapture(encap='gre4', isv4=True)
199
200         finally:
201             for asid in self.ass:
202                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
203             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4 del")
204             self.vapi.cli("test lb flowtable flush")
205
206     def test_lb_ip6_gre4(self):
207         """ Load Balancer IP6 GRE4 """
208
209         try:
210             self.vapi.cli("lb vip 2001::/16 encap gre4")
211             for asid in self.ass:
212                 self.vapi.cli("lb as 2001::/16 10.0.0.%u" % (asid))
213
214             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
215             self.pg_enable_capture(self.pg_interfaces)
216             self.pg_start()
217
218             self.checkCapture(encap='gre4', isv4=False)
219         finally:
220             for asid in self.ass:
221                 self.vapi.cli("lb as 2001::/16 10.0.0.%u del" % (asid))
222             self.vapi.cli("lb vip 2001::/16 encap gre4 del")
223             self.vapi.cli("test lb flowtable flush")
224
225     def test_lb_ip4_gre6(self):
226         """ Load Balancer IP4 GRE6 """
227         try:
228             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6")
229             for asid in self.ass:
230                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u" % (asid))
231
232             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
233             self.pg_enable_capture(self.pg_interfaces)
234             self.pg_start()
235
236             self.checkCapture(encap='gre6', isv4=True)
237         finally:
238             for asid in self.ass:
239                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u del" % (asid))
240             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6 del")
241             self.vapi.cli("test lb flowtable flush")
242
243     def test_lb_ip6_gre6(self):
244         """ Load Balancer IP6 GRE6 """
245         try:
246             self.vapi.cli("lb vip 2001::/16 encap gre6")
247             for asid in self.ass:
248                 self.vapi.cli("lb as 2001::/16 2002::%u" % (asid))
249
250             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
251             self.pg_enable_capture(self.pg_interfaces)
252             self.pg_start()
253
254             self.checkCapture(encap='gre6', isv4=False)
255         finally:
256             for asid in self.ass:
257                 self.vapi.cli("lb as 2001::/16 2002::%u del" % (asid))
258             self.vapi.cli("lb vip 2001::/16 encap gre6 del")
259             self.vapi.cli("test lb flowtable flush")
260
261     def test_lb_ip4_l3dsr(self):
262         """ Load Balancer IP4 L3DSR """
263         try:
264             self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7")
265             for asid in self.ass:
266                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
267
268             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
269             self.pg_enable_capture(self.pg_interfaces)
270             self.pg_start()
271             self.checkCapture(encap='l3dsr', isv4=True)
272
273         finally:
274             for asid in self.ass:
275                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
276             self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7 del")
277             self.vapi.cli("test lb flowtable flush")
278
279     def test_lb_ip4_nat4(self):
280         """ Load Balancer IP4 NAT4 """
281         try:
282             self.vapi.cli("lb vip 90.0.0.0/8 encap nat4"
283                           " type clusterip port 3306 target_port 3307")
284             for asid in self.ass:
285                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
286
287             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
288             self.pg_enable_capture(self.pg_interfaces)
289             self.pg_start()
290             self.checkCapture(encap='nat4', isv4=True)
291
292         finally:
293             for asid in self.ass:
294                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
295             self.vapi.cli("lb vip 90.0.0.0/8 encap nat4"
296                           " type clusterip port 3306 target_port 3307 del")
297             self.vapi.cli("test lb flowtable flush")
298
299     def test_lb_ip6_nat6(self):
300         """ Load Balancer IP6 NAT6 """
301         try:
302             self.vapi.cli("lb vip 2001::/16 encap nat6"
303                           " type clusterip port 3306 target_port 3307")
304             for asid in self.ass:
305                 self.vapi.cli("lb as 2001::/16 2002::%u" % (asid))
306
307             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
308             self.pg_enable_capture(self.pg_interfaces)
309             self.pg_start()
310             self.checkCapture(encap='nat6', isv4=False)
311
312         finally:
313             for asid in self.ass:
314                 self.vapi.cli("lb as 2001::/16 2002::%u del" % (asid))
315             self.vapi.cli("lb vip 2001::/16 encap nat6"
316                           " type clusterip port 3306 target_port 3307 del")
317             self.vapi.cli("test lb flowtable flush")