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