9b5baaea13270fba104f1b9517d77865673a33f2
[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
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             self.logger.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         self.pg0.assert_nothing_captured()
99         out = self.pg1.get_capture()
100         self.assertEqual(len(out), 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 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                 else:
120                     ip = p[IPv6]
121                     asid = ip.dst.split(":")
122                     asid = asid[len(asid) - 1]
123                     asid = 0 if asid == "" else int(asid)
124                     self.assertEqual(ip.version, 6)
125                     self.assertEqual(ip.tc, 0)
126                     self.assertEqual(ip.fl, 0)
127                     self.assertEqual(ip.src, "2004::1")
128                     self.assertEqual(
129                         socket.inet_pton(socket.AF_INET6, ip.dst),
130                         socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
131                     )
132                     self.assertEqual(ip.nh, 47)
133                     self.assertGreaterEqual(ip.hlim, 64)
134                     # self.assertEqual(len(ip.options), 0)
135                     gre = GRE(str(p[IPv6].payload))
136                 self.checkInner(gre, isv4)
137                 load[asid] += 1
138             except:
139                 self.logger.error(ppp("Unexpected or invalid packet:", p))
140                 raise
141
142         # This is just to roughly check that the balancing algorithm
143         # is not completly biased.
144         for asid in self.ass:
145             if load[asid] < len(self.packets) / (len(self.ass) * 2):
146                 self.log(
147                     "ASS is not balanced: load[%d] = %d" % (asid, load[asid]))
148                 raise Exception("Load Balancer algorithm is biased")
149
150     def test_lb_ip4_gre4(self):
151         """ Load Balancer IP4 GRE4 """
152         try:
153             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4")
154             for asid in self.ass:
155                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
156
157             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
158             self.pg_enable_capture(self.pg_interfaces)
159             self.pg_start()
160             self.checkCapture(gre4=True, isv4=True)
161
162         finally:
163             for asid in self.ass:
164                 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
165             self.vapi.cli("lb vip 90.0.0.0/8 encap gre4 del")
166
167     def test_lb_ip6_gre4(self):
168         """ Load Balancer IP6 GRE4 """
169
170         try:
171             self.vapi.cli("lb vip 2001::/16 encap gre4")
172             for asid in self.ass:
173                 self.vapi.cli("lb as 2001::/16 10.0.0.%u" % (asid))
174
175             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
176             self.pg_enable_capture(self.pg_interfaces)
177             self.pg_start()
178
179             self.checkCapture(gre4=True, isv4=False)
180         finally:
181             for asid in self.ass:
182                 self.vapi.cli("lb as 2001::/16 10.0.0.%u del" % (asid))
183             self.vapi.cli("lb vip 2001::/16 encap gre4 del")
184
185     def test_lb_ip4_gre6(self):
186         """ Load Balancer IP4 GRE6 """
187         try:
188             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6")
189             for asid in self.ass:
190                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u" % (asid))
191
192             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
193             self.pg_enable_capture(self.pg_interfaces)
194             self.pg_start()
195
196             self.checkCapture(gre4=False, isv4=True)
197         finally:
198             for asid in self.ass:
199                 self.vapi.cli("lb as 90.0.0.0/8 2002::%u" % (asid))
200             self.vapi.cli("lb vip 90.0.0.0/8 encap gre6 del")
201
202     def test_lb_ip6_gre6(self):
203         """ Load Balancer IP6 GRE6 """
204         try:
205             self.vapi.cli("lb vip 2001::/16 encap gre6")
206             for asid in self.ass:
207                 self.vapi.cli("lb as 2001::/16 2002::%u" % (asid))
208
209             self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
210             self.pg_enable_capture(self.pg_interfaces)
211             self.pg_start()
212
213             self.checkCapture(gre4=False, isv4=False)
214         finally:
215             for asid in self.ass:
216                 self.vapi.cli("lb as 2001::/16 2002::%u del" % (asid))
217             self.vapi.cli("lb vip 2001::/16 encap gre6 del")