session: send tx notification to app
[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
16   - IP4 to GRE6 encap
17   - IP6 to GRE4 encap
18   - IP6 to GRE6 encap
19   - IP4 to L3DSR encap
20   - IP4 to NAT4 encap
21   - IP6 to NAT6 encap
22
23  As stated in comments below, GRE has issues with IPv6.
24  All test cases involving IPv6 are executed, but
25  received packets are not parsed and checked.
26
27 """
28
29
30 class TestLB(VppTestCase):
31     """ Load Balancer Test Case """
32
33     @classmethod
34     def setUpClass(cls):
35         super(TestLB, cls).setUpClass()
36
37         cls.ass = range(5)
38         cls.packets = range(100)
39
40         try:
41             cls.create_pg_interfaces(range(2))
42             cls.interfaces = list(cls.pg_interfaces)
43
44             for i in cls.interfaces:
45                 i.admin_up()
46                 i.config_ip4()
47                 i.config_ip6()
48                 i.disable_ipv6_ra()
49                 i.resolve_arp()
50                 i.resolve_ndp()
51             dst4 = socket.inet_pton(socket.AF_INET, "10.0.0.0")
52             dst6 = socket.inet_pton(socket.AF_INET6, "2002::")
53             cls.vapi.ip_add_del_route(dst4, 24, cls.pg1.remote_ip4n)
54             cls.vapi.ip_add_del_route(dst6, 16, cls.pg1.remote_ip6n, is_ipv6=1)
55             cls.vapi.cli("lb conf ip4-src-address 39.40.41.42")
56             cls.vapi.cli("lb conf ip6-src-address 2004::1")
57         except Exception:
58             super(TestLB, cls).tearDownClass()
59             raise
60
61     def tearDown(self):
62         super(TestLB, self).tearDown()
63         if not self.vpp_dead:
64             self.logger.info(self.vapi.cli("show lb vip verbose"))
65
66     def getIPv4Flow(self, id):
67         return (IP(dst="90.0.%u.%u" % (id / 255, id % 255),
68                    src="40.0.%u.%u" % (id / 255, id % 255)) /
69                 UDP(sport=10000 + id, dport=20000 + id))
70
71     def getIPv6Flow(self, id):
72         return (IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) /
73                 UDP(sport=10000 + id, dport=20000 + id))
74
75     def generatePackets(self, src_if, isv4):
76         self.reset_packet_infos()
77         pkts = []
78         for pktid in self.packets:
79             info = self.create_packet_info(src_if, self.pg1)
80             payload = self.info_to_payload(info)
81             ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
82             packet = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
83                       ip /
84                       Raw(payload))
85             self.extend_packet(packet, 128)
86             info.data = packet.copy()
87             pkts.append(packet)
88         return pkts
89
90     def checkInner(self, gre, isv4):
91         IPver = IP if isv4 else IPv6
92         self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
93         self.assertEqual(gre.flags, 0)
94         self.assertEqual(gre.version, 0)
95         inner = IPver(str(gre.payload))
96         payload_info = self.payload_to_info(str(inner[Raw]))
97         self.info = self.packet_infos[payload_info.index]
98         self.assertEqual(payload_info.src, self.pg0.sw_if_index)
99         self.assertEqual(str(inner), str(self.info.data[IPver]))
100
101     def checkCapture(self, encap, isv4):
102         self.pg0.assert_nothing_captured()
103         out = self.pg1.get_capture(len(self.packets))
104
105         load = [0] * len(self.ass)
106         self.info = None
107         for p in out:
108             try:
109                 asid = 0
110                 gre = None
111                 if (encap == 'gre4'):
112                     ip = p[IP]
113                     asid = int(ip.dst.split(".")[3])
114                     self.assertEqual(ip.version, 4)
115                     self.assertEqual(ip.flags, 0)
116                     self.assertEqual(ip.src, "39.40.41.42")
117                     self.assertEqual(ip.dst, "10.0.0.%u" % asid)
118                     self.assertEqual(ip.proto, 47)
119                     self.assertEqual(len(ip.options), 0)
120                     self.assertGreaterEqual(ip.ttl, 64)
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.assertGreaterEqual(ip.hlim, 64)
138                     # self.assertEqual(len(ip.options), 0)
139                     gre = GRE(str(p[IPv6].payload))
140                     self.checkInner(gre, isv4)
141                 elif (encap == 'l3dsr'):
142                     ip = p[IP]
143                     asid = int(ip.dst.split(".")[3])
144                     self.assertEqual(ip.version, 4)
145                     self.assertEqual(ip.flags, 0)
146                     self.assertEqual(ip.dst, "10.0.0.%u" % asid)
147                     self.assertEqual(ip.tos, 0x1c)
148                     self.assertEqual(len(ip.options), 0)
149                     self.assert_ip_checksum_valid(p)
150                     if ip.proto == IP_PROTOS.tcp:
151                         self.assert_tcp_checksum_valid(p)
152                     elif ip.proto == IP_PROTOS.udp:
153                         self.assert_udp_checksum_valid(p)
154                 elif (encap == 'nat4'):
155                     ip = p[IP]
156                     asid = int(ip.dst.split(".")[3])
157                     self.assertEqual(ip.version, 4)
158                     self.assertEqual(ip.flags, 0)
159                     self.assertEqual(ip.dst, "10.0.0.%u" % asid)
160                     self.assertEqual(ip.proto, 17)
161                     self.assertEqual(len(ip.options), 0)
162                     self.assertGreaterEqual(ip.ttl, 63)
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(str(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 roughly 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 """
196         try:
197             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4")
198             for asid in self.ass:
199                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
200
201             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
202             self.pg_enable_capture(self.pg_interfaces)
203             self.pg_start()
204             self.checkCapture(encap='gre4', isv4=True)
205
206         finally:
207             for asid in self.ass:
208                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
209             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4 del")
210             self.vapi.cli("test lb flowtable flush")
211
212     def test_lb_ip6_gre4(self):
213         """ Load Balancer IP6 GRE4 """
214
215         try:
216             self.vapi.cli("lb vip 2001::/16 encap gre4")
217             for asid in self.ass:
218                 self.vapi.cli("lb as 2001::/16 10.0.0.%u" % (asid))
219
220             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
221             self.pg_enable_capture(self.pg_interfaces)
222             self.pg_start()
223
224             self.checkCapture(encap='gre4', isv4=False)
225         finally:
226             for asid in self.ass:
227                 self.vapi.cli("lb as 2001::/16 10.0.0.%u del" % (asid))
228             self.vapi.cli("lb vip 2001::/16 encap gre4 del")
229             self.vapi.cli("test lb flowtable flush")
230
231     def test_lb_ip4_gre6(self):
232         """ Load Balancer IP4 GRE6 """
233         try:
234             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6")
235             for asid in self.ass:
236                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u" % (asid))
237
238             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
239             self.pg_enable_capture(self.pg_interfaces)
240             self.pg_start()
241
242             self.checkCapture(encap='gre6', isv4=True)
243         finally:
244             for asid in self.ass:
245                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u del" % (asid))
246             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6 del")
247             self.vapi.cli("test lb flowtable flush")
248
249     def test_lb_ip6_gre6(self):
250         """ Load Balancer IP6 GRE6 """
251         try:
252             self.vapi.cli("lb vip 2001::/16 encap gre6")
253             for asid in self.ass:
254                 self.vapi.cli("lb as 2001::/16 2002::%u" % (asid))
255
256             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
257             self.pg_enable_capture(self.pg_interfaces)
258             self.pg_start()
259
260             self.checkCapture(encap='gre6', isv4=False)
261         finally:
262             for asid in self.ass:
263                 self.vapi.cli("lb as 2001::/16 2002::%u del" % (asid))
264             self.vapi.cli("lb vip 2001::/16 encap gre6 del")
265             self.vapi.cli("test lb flowtable flush")
266
267     def test_lb_ip4_l3dsr(self):
268         """ Load Balancer IP4 L3DSR """
269         try:
270             self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7")
271             for asid in self.ass:
272                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
273
274             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
275             self.pg_enable_capture(self.pg_interfaces)
276             self.pg_start()
277             self.checkCapture(encap='l3dsr', isv4=True)
278
279         finally:
280             for asid in self.ass:
281                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
282             self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7 del")
283             self.vapi.cli("test lb flowtable flush")
284
285     def test_lb_ip4_nat4(self):
286         """ Load Balancer IP4 NAT4 """
287         try:
288             self.vapi.cli("lb vip 90.0.0.0/8 encap nat4"
289                           " type clusterip port 3306 target_port 3307")
290             for asid in self.ass:
291                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
292
293             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
294             self.pg_enable_capture(self.pg_interfaces)
295             self.pg_start()
296             self.checkCapture(encap='nat4', isv4=True)
297
298         finally:
299             for asid in self.ass:
300                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
301             self.vapi.cli("lb vip 90.0.0.0/8 encap nat4"
302                           " type clusterip port 3306 target_port 3307 del")
303             self.vapi.cli("test lb flowtable flush")
304
305     def test_lb_ip6_nat6(self):
306         """ Load Balancer IP6 NAT6 """
307         try:
308             self.vapi.cli("lb vip 2001::/16 encap nat6"
309                           " type clusterip port 3306 target_port 3307")
310             for asid in self.ass:
311                 self.vapi.cli("lb as 2001::/16 2002::%u" % (asid))
312
313             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
314             self.pg_enable_capture(self.pg_interfaces)
315             self.pg_start()
316             self.checkCapture(encap='nat6', isv4=False)
317
318         finally:
319             for asid in self.ass:
320                 self.vapi.cli("lb as 2001::/16 2002::%u del" % (asid))
321             self.vapi.cli("lb vip 2001::/16 encap nat6"
322                           " type clusterip port 3306 target_port 3307 del")
323             self.vapi.cli("test lb flowtable flush")