Add L3DSR feature in 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
20  As stated in comments below, GRE has issues with IPv6.
21  All test cases involving IPv6 are executed, but
22  received packets are not parsed and checked.
23
24 """
25
26
27 class TestLB(VppTestCase):
28     """ Load Balancer Test Case """
29
30     @classmethod
31     def setUpClass(cls):
32         super(TestLB, cls).setUpClass()
33
34         cls.ass = range(5)
35         cls.packets = range(100)
36
37         try:
38             cls.create_pg_interfaces(range(2))
39             cls.interfaces = list(cls.pg_interfaces)
40
41             for i in cls.interfaces:
42                 i.admin_up()
43                 i.config_ip4()
44                 i.config_ip6()
45                 i.disable_ipv6_ra()
46                 i.resolve_arp()
47                 i.resolve_ndp()
48             dst4 = socket.inet_pton(socket.AF_INET, "10.0.0.0")
49             dst6 = socket.inet_pton(socket.AF_INET6, "2002::")
50             cls.vapi.ip_add_del_route(dst4, 24, cls.pg1.remote_ip4n)
51             cls.vapi.ip_add_del_route(dst6, 16, cls.pg1.remote_ip6n, is_ipv6=1)
52             cls.vapi.cli("lb conf ip4-src-address 39.40.41.42")
53             cls.vapi.cli("lb conf ip6-src-address 2004::1")
54         except Exception:
55             super(TestLB, cls).tearDownClass()
56             raise
57
58     def tearDown(self):
59         super(TestLB, self).tearDown()
60         if not self.vpp_dead:
61             self.logger.info(self.vapi.cli("show lb vip verbose"))
62
63     def getIPv4Flow(self, id):
64         return (IP(dst="90.0.%u.%u" % (id / 255, id % 255),
65                    src="40.0.%u.%u" % (id / 255, id % 255)) /
66                 UDP(sport=10000 + id, dport=20000 + id))
67
68     def getIPv6Flow(self, id):
69         return (IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) /
70                 UDP(sport=10000 + id, dport=20000 + id))
71
72     def generatePackets(self, src_if, isv4):
73         self.reset_packet_infos()
74         pkts = []
75         for pktid in self.packets:
76             info = self.create_packet_info(src_if, self.pg1)
77             payload = self.info_to_payload(info)
78             ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
79             packet = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
80                       ip /
81                       Raw(payload))
82             self.extend_packet(packet, 128)
83             info.data = packet.copy()
84             pkts.append(packet)
85         return pkts
86
87     def checkInner(self, gre, isv4):
88         IPver = IP if isv4 else IPv6
89         self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
90         self.assertEqual(gre.flags, 0)
91         self.assertEqual(gre.version, 0)
92         inner = IPver(str(gre.payload))
93         payload_info = self.payload_to_info(str(inner[Raw]))
94         self.info = self.packet_infos[payload_info.index]
95         self.assertEqual(payload_info.src, self.pg0.sw_if_index)
96         self.assertEqual(str(inner), str(self.info.data[IPver]))
97
98     def checkCapture(self, encap, isv4):
99         self.pg0.assert_nothing_captured()
100         out = self.pg1.get_capture(len(self.packets))
101
102         load = [0] * len(self.ass)
103         self.info = None
104         for p in out:
105             try:
106                 asid = 0
107                 gre = None
108                 if (encap == 'gre4'):
109                     ip = p[IP]
110                     asid = int(ip.dst.split(".")[3])
111                     self.assertEqual(ip.version, 4)
112                     self.assertEqual(ip.flags, 0)
113                     self.assertEqual(ip.src, "39.40.41.42")
114                     self.assertEqual(ip.dst, "10.0.0.%u" % asid)
115                     self.assertEqual(ip.proto, 47)
116                     self.assertEqual(len(ip.options), 0)
117                     self.assertGreaterEqual(ip.ttl, 64)
118                     gre = p[GRE]
119                     self.checkInner(gre, isv4)
120                 elif (encap == 'gre6'):
121                     ip = p[IPv6]
122                     asid = ip.dst.split(":")
123                     asid = asid[len(asid) - 1]
124                     asid = 0 if asid == "" else int(asid)
125                     self.assertEqual(ip.version, 6)
126                     self.assertEqual(ip.tc, 0)
127                     self.assertEqual(ip.fl, 0)
128                     self.assertEqual(ip.src, "2004::1")
129                     self.assertEqual(
130                         socket.inet_pton(socket.AF_INET6, ip.dst),
131                         socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
132                     )
133                     self.assertEqual(ip.nh, 47)
134                     self.assertGreaterEqual(ip.hlim, 64)
135                     # self.assertEqual(len(ip.options), 0)
136                     gre = GRE(str(p[IPv6].payload))
137                     self.checkInner(gre, isv4)
138                 if (encap == 'l3dsr'):
139                     ip = p[IP]
140                     asid = int(ip.dst.split(".")[3])
141                     self.assertEqual(ip.version, 4)
142                     self.assertEqual(ip.flags, 0)
143                     self.assertEqual(ip.dst, "10.0.0.%u" % asid)
144                     self.assertEqual(ip.tos, 0x1c)
145                     self.assertEqual(len(ip.options), 0)
146                 load[asid] += 1
147             except:
148                 self.logger.error(ppp("Unexpected or invalid packet:", p))
149                 raise
150
151         # This is just to roughly check that the balancing algorithm
152         # is not completly biased.
153         for asid in self.ass:
154             if load[asid] < len(self.packets) / (len(self.ass) * 2):
155                 self.logger.error(
156                     "ASS is not balanced: load[%d] = %d" % (asid, load[asid]))
157                 raise Exception("Load Balancer algorithm is biased")
158
159     def test_lb_ip4_gre4(self):
160         """ Load Balancer IP4 GRE4 """
161         try:
162             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4")
163             for asid in self.ass:
164                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
165
166             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
167             self.pg_enable_capture(self.pg_interfaces)
168             self.pg_start()
169             self.checkCapture(encap='gre4', isv4=True)
170
171         finally:
172             for asid in self.ass:
173                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
174             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4 del")
175             self.vapi.cli("test lb flowtable flush")
176
177     def test_lb_ip6_gre4(self):
178         """ Load Balancer IP6 GRE4 """
179
180         try:
181             self.vapi.cli("lb vip 2001::/16 encap gre4")
182             for asid in self.ass:
183                 self.vapi.cli("lb as 2001::/16 10.0.0.%u" % (asid))
184
185             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
186             self.pg_enable_capture(self.pg_interfaces)
187             self.pg_start()
188
189             self.checkCapture(encap='gre4', isv4=False)
190         finally:
191             for asid in self.ass:
192                 self.vapi.cli("lb as 2001::/16 10.0.0.%u del" % (asid))
193             self.vapi.cli("lb vip 2001::/16 encap gre4 del")
194             self.vapi.cli("test lb flowtable flush")
195
196     def test_lb_ip4_gre6(self):
197         """ Load Balancer IP4 GRE6 """
198         try:
199             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6")
200             for asid in self.ass:
201                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u" % (asid))
202
203             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
204             self.pg_enable_capture(self.pg_interfaces)
205             self.pg_start()
206
207             self.checkCapture(encap='gre6', isv4=True)
208         finally:
209             for asid in self.ass:
210                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u del" % (asid))
211             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6 del")
212             self.vapi.cli("test lb flowtable flush")
213
214     def test_lb_ip6_gre6(self):
215         """ Load Balancer IP6 GRE6 """
216         try:
217             self.vapi.cli("lb vip 2001::/16 encap gre6")
218             for asid in self.ass:
219                 self.vapi.cli("lb as 2001::/16 2002::%u" % (asid))
220
221             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
222             self.pg_enable_capture(self.pg_interfaces)
223             self.pg_start()
224
225             self.checkCapture(encap='gre6', isv4=False)
226         finally:
227             for asid in self.ass:
228                 self.vapi.cli("lb as 2001::/16 2002::%u del" % (asid))
229             self.vapi.cli("lb vip 2001::/16 encap gre6 del")
230             self.vapi.cli("test lb flowtable flush")
231
232     def test_lb_ip4_l3dsr(self):
233         """ Load Balancer IP4 L3DSR """
234         try:
235             self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7")
236             for asid in self.ass:
237                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
238
239             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
240             self.pg_enable_capture(self.pg_interfaces)
241             self.pg_start()
242             self.checkCapture(encap='l3dsr', isv4=True)
243
244         finally:
245             for asid in self.ass:
246                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
247             self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7 del")
248             self.vapi.cli("test lb flowtable flush")