fa4900d2b5fa2d4f57d3a9c0eade0f790029c59e
[vpp.git] / test / test_lb.py
1 import socket
2 from logging import *
3
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
9 from framework import VppTestCase
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
19  As stated in comments below, GRE has issues with IPv6.
20  All test cases involving IPv6 are executed, but
21  received packets are not parsed and checked.
22
23 """
24
25
26 class TestLB(VppTestCase):
27     """ Load Balancer Test Case """
28
29     @classmethod
30     def setUpClass(cls):
31         super(TestLB, cls).setUpClass()
32
33         cls.ass = range(5)
34         cls.packets = range(100)
35
36         try:
37             cls.create_pg_interfaces(range(2))
38             cls.interfaces = list(cls.pg_interfaces)
39
40             for i in cls.interfaces:
41                 i.admin_up()
42                 i.config_ip4()
43                 i.config_ip6()
44                 i.disable_ipv6_ra()
45                 i.resolve_arp()
46                 i.resolve_ndp()
47             dst4 = socket.inet_pton(socket.AF_INET, "10.0.0.0")
48             dst6 = socket.inet_pton(socket.AF_INET6, "2002::")
49             cls.vapi.ip_add_del_route(dst4, 24, cls.pg1.remote_ip4n)
50             cls.vapi.ip_add_del_route(dst6, 16, cls.pg1.remote_ip6n, is_ipv6=1)
51             cls.vapi.cli("lb conf ip4-src-address 39.40.41.42")
52             cls.vapi.cli("lb conf ip6-src-address 2004::1")
53         except Exception:
54             super(TestLB, cls).tearDownClass()
55             raise
56
57     def tearDown(self):
58         super(TestLB, self).tearDown()
59         if not self.vpp_dead:
60             info(self.vapi.cli("show lb vip verbose"))
61
62     def getIPv4Flow(self, id):
63         return (IP(dst="90.0.%u.%u" % (id / 255, id % 255),
64                    src="40.0.%u.%u" % (id / 255, id % 255)) /
65                 UDP(sport=10000 + id, dport=20000 + id))
66
67     def getIPv6Flow(self, id):
68         return (IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) /
69                 UDP(sport=10000 + id, dport=20000 + id))
70
71     def generatePackets(self, src_if, isv4):
72         self.packet_infos = {}
73         pkts = []
74         for pktid in self.packets:
75             info = self.create_packet_info(src_if.sw_if_index, pktid)
76             payload = self.info_to_payload(info)
77             ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
78             packet = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
79                       ip /
80                       Raw(payload))
81             self.extend_packet(packet, 128)
82             info.data = packet.copy()
83             pkts.append(packet)
84         return pkts
85
86     def checkInner(self, gre, isv4):
87         IPver = IP if isv4 else IPv6
88         self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
89         self.assertEqual(gre.flags, 0)
90         self.assertEqual(gre.version, 0)
91         inner = IPver(str(gre.payload))
92         payload_info = self.payload_to_info(str(inner[Raw]))
93         self.info = self.get_next_packet_info_for_interface2(
94             self.pg0.sw_if_index, payload_info.dst, self.info)
95         self.assertEqual(str(inner), str(self.info.data[IPver]))
96
97     def checkCapture(self, gre4, isv4):
98         out = self.pg0.get_capture()
99         # This check is edited because RA appears in output, maybe disable RA?
100         # self.assertEqual(len(out), 0)
101         self.assertLess(len(out), 20)
102         out = self.pg1.get_capture()
103         self.assertEqual(len(out), 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 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                 else:
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                 load[asid] += 1
141             except:
142                 error("Unexpected or invalid packet:")
143                 p.show()
144                 raise
145
146         # This is just to roughly check that the balancing algorithm
147         # is not completly biased.
148         for asid in self.ass:
149             if load[asid] < len(self.packets) / (len(self.ass) * 2):
150                 self.log(
151                     "ASS is not balanced: load[%d] = %d" % (asid, load[asid]))
152                 raise Exception("Load Balancer algorithm is biased")
153
154     def test_lb_ip4_gre4(self):
155         """ Load Balancer IP4 GRE4 """
156         try:
157             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4")
158             for asid in self.ass:
159                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
160
161             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
162             self.pg_enable_capture(self.pg_interfaces)
163             self.pg_start()
164             self.checkCapture(gre4=True, isv4=True)
165
166         finally:
167             for asid in self.ass:
168                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
169             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4 del")
170
171     def test_lb_ip6_gre4(self):
172         """ Load Balancer IP6 GRE4 """
173
174         try:
175             self.vapi.cli("lb vip 2001::/16 encap gre4")
176             for asid in self.ass:
177                 self.vapi.cli("lb as 2001::/16 10.0.0.%u" % (asid))
178
179             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
180             self.pg_enable_capture(self.pg_interfaces)
181             self.pg_start()
182
183             self.checkCapture(gre4=True, isv4=False)
184         finally:
185             for asid in self.ass:
186                 self.vapi.cli("lb as 2001::/16 10.0.0.%u del" % (asid))
187             self.vapi.cli("lb vip 2001::/16 encap gre4 del")
188
189     def test_lb_ip4_gre6(self):
190         """ Load Balancer IP4 GRE6 """
191         try:
192             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6")
193             for asid in self.ass:
194                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u" % (asid))
195
196             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
197             self.pg_enable_capture(self.pg_interfaces)
198             self.pg_start()
199
200             self.checkCapture(gre4=False, isv4=True)
201         finally:
202             for asid in self.ass:
203                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u" % (asid))
204             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6 del")
205
206     def test_lb_ip6_gre6(self):
207         """ Load Balancer IP6 GRE6 """
208         try:
209             self.vapi.cli("lb vip 2001::/16 encap gre6")
210             for asid in self.ass:
211                 self.vapi.cli("lb as 2001::/16 2002::%u" % (asid))
212
213             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
214             self.pg_enable_capture(self.pg_interfaces)
215             self.pg_start()
216
217             self.checkCapture(gre4=False, isv4=False)
218         finally:
219             for asid in self.ass:
220                 self.vapi.cli("lb as 2001::/16 2002::%u del" % (asid))
221             self.vapi.cli("lb vip 2001::/16 encap gre6 del")