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 from scapy.data import IP_PROTOS
10 from framework import VppTestCase
12 from vpp_ip_route import VppIpRoute, VppRoutePath
13 from vpp_ip import INVALID_INDEX
15 """ TestLB is a subclass of VPPTestCase classes.
17 TestLB class defines Load Balancer test cases for:
18 - IP4 to GRE4 encap on per-port vip case
19 - IP4 to GRE6 encap on per-port vip case
20 - IP6 to GRE4 encap on per-port vip case
21 - IP6 to GRE6 encap on per-port vip case
22 - IP4 to L3DSR encap on vip case
23 - IP4 to L3DSR encap on per-port vip case
24 - IP4 to NAT4 encap on per-port vip case
25 - IP6 to NAT6 encap on per-port vip case
27 As stated in comments below, GRE has issues with IPv6.
28 All test cases involving IPv6 are executed, but
29 received packets are not parsed and checked.
34 class TestLB(VppTestCase):
35 """Load Balancer Test Case"""
39 super(TestLB, cls).setUpClass()
42 cls.packets = range(1)
45 cls.create_pg_interfaces(range(2))
46 cls.interfaces = list(cls.pg_interfaces)
48 for i in cls.interfaces:
60 [VppRoutePath(cls.pg1.remote_ip4, INVALID_INDEX)],
68 [VppRoutePath(cls.pg1.remote_ip6, INVALID_INDEX)],
72 cls.vapi.lb_conf(ip4_src_address="39.40.41.42", ip6_src_address="2004::1")
74 super(TestLB, cls).tearDownClass()
78 def tearDownClass(cls):
79 super(TestLB, cls).tearDownClass()
82 super(TestLB, self).tearDown()
84 def show_commands_at_teardown(self):
85 self.logger.info(self.vapi.cli("show lb vip verbose"))
87 def getIPv4Flow(self, id):
89 dst="90.0.%u.%u" % (id / 255, id % 255),
90 src="40.0.%u.%u" % (id / 255, id % 255),
91 ) / UDP(sport=10000 + id, dport=20000)
93 def getIPv6Flow(self, id):
94 return IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) / UDP(
95 sport=10000 + id, dport=20000
98 def generatePackets(self, src_if, isv4):
99 self.reset_packet_infos()
101 for pktid in self.packets:
102 info = self.create_packet_info(src_if, self.pg1)
103 payload = self.info_to_payload(info)
104 ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
106 Ether(dst=src_if.local_mac, src=src_if.remote_mac) / ip / Raw(payload)
108 self.extend_packet(packet, 128)
109 info.data = packet.copy()
113 def checkInner(self, gre, isv4):
114 IPver = IP if isv4 else IPv6
115 self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
116 self.assertEqual(gre.flags, 0)
117 self.assertEqual(gre.version, 0)
118 inner = IPver(scapy.compat.raw(gre.payload))
119 payload_info = self.payload_to_info(inner[Raw])
120 self.info = self.packet_infos[payload_info.index]
121 self.assertEqual(payload_info.src, self.pg0.sw_if_index)
123 scapy.compat.raw(inner), scapy.compat.raw(self.info.data[IPver])
126 def checkCapture(self, encap, isv4):
127 self.pg0.assert_nothing_captured()
128 out = self.pg1.get_capture(len(self.packets))
130 load = [0] * len(self.ass)
138 asid = int(ip.dst.split(".")[3])
139 self.assertEqual(ip.version, 4)
140 self.assertEqual(ip.flags, 0)
141 self.assertEqual(ip.src, "39.40.41.42")
142 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
143 self.assertEqual(ip.proto, 47)
144 self.assertEqual(len(ip.options), 0)
146 self.checkInner(gre, isv4)
147 elif encap == "gre6":
149 asid = ip.dst.split(":")
150 asid = asid[len(asid) - 1]
151 asid = 0 if asid == "" else int(asid)
152 self.assertEqual(ip.version, 6)
153 self.assertEqual(ip.tc, 0)
154 self.assertEqual(ip.fl, 0)
155 self.assertEqual(ip.src, "2004::1")
157 socket.inet_pton(socket.AF_INET6, ip.dst),
158 socket.inet_pton(socket.AF_INET6, "2002::%u" % asid),
160 self.assertEqual(ip.nh, 47)
161 # self.assertEqual(len(ip.options), 0)
162 gre = GRE(scapy.compat.raw(p[IPv6].payload))
163 self.checkInner(gre, isv4)
164 elif encap == "l3dsr":
166 asid = int(ip.dst.split(".")[3])
167 self.assertEqual(ip.version, 4)
168 self.assertEqual(ip.flags, 0)
169 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
170 self.assertEqual(ip.tos, 0x1C)
171 self.assertEqual(len(ip.options), 0)
172 self.assert_ip_checksum_valid(p)
173 if ip.proto == IP_PROTOS.tcp:
174 self.assert_tcp_checksum_valid(p)
175 elif ip.proto == IP_PROTOS.udp:
176 self.assert_udp_checksum_valid(p)
177 elif encap == "nat4":
179 asid = int(ip.dst.split(".")[3])
180 self.assertEqual(ip.version, 4)
181 self.assertEqual(ip.flags, 0)
182 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
183 self.assertEqual(ip.proto, 17)
184 self.assertEqual(len(ip.options), 0)
186 self.assertEqual(udp.dport, 3307)
187 elif encap == "nat6":
189 asid = ip.dst.split(":")
190 asid = asid[len(asid) - 1]
191 asid = 0 if asid == "" else int(asid)
192 self.assertEqual(ip.version, 6)
193 self.assertEqual(ip.tc, 0)
194 self.assertEqual(ip.fl, 0)
196 socket.inet_pton(socket.AF_INET6, ip.dst),
197 socket.inet_pton(socket.AF_INET6, "2002::%u" % asid),
199 self.assertEqual(ip.nh, 17)
200 self.assertGreaterEqual(ip.hlim, 63)
201 udp = UDP(scapy.compat.raw(p[IPv6].payload))
202 self.assertEqual(udp.dport, 3307)
205 self.logger.error(ppp("Unexpected or invalid packet:", p))
208 # This is just to roughly check that the balancing algorithm
209 # is not completely biased.
210 for asid in self.ass:
211 if load[asid] < int(len(self.packets) / (len(self.ass) * 2)):
213 "ASS is not balanced: load[%d] = %d" % (asid, load[asid])
215 raise Exception("Load Balancer algorithm is biased")
217 def test_lb_ip4_gre4(self):
218 """Load Balancer IP4 GRE4 on vip case"""
220 self.vapi.cli("lb vip 90.0.0.0/8 encap gre4")
221 for asid in self.ass:
222 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
224 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
225 self.pg_enable_capture(self.pg_interfaces)
227 self.checkCapture(encap="gre4", isv4=True)
230 for asid in self.ass:
231 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
232 self.vapi.cli("lb vip 90.0.0.0/8 encap gre4 del")
233 self.vapi.cli("test lb flowtable flush")
235 def test_lb_ip6_gre4(self):
236 """Load Balancer IP6 GRE4 on vip case"""
239 self.vapi.cli("lb vip 2001::/16 encap gre4")
240 for asid in self.ass:
241 self.vapi.cli("lb as 2001::/16 10.0.0.%u" % (asid))
243 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
244 self.pg_enable_capture(self.pg_interfaces)
247 self.checkCapture(encap="gre4", isv4=False)
249 for asid in self.ass:
250 self.vapi.cli("lb as 2001::/16 10.0.0.%u del" % (asid))
251 self.vapi.cli("lb vip 2001::/16 encap gre4 del")
252 self.vapi.cli("test lb flowtable flush")
254 def test_lb_ip4_gre6(self):
255 """Load Balancer IP4 GRE6 on vip case"""
257 self.vapi.cli("lb vip 90.0.0.0/8 encap gre6")
258 for asid in self.ass:
259 self.vapi.cli("lb as 90.0.0.0/8 2002::%u" % (asid))
261 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
262 self.pg_enable_capture(self.pg_interfaces)
265 self.checkCapture(encap="gre6", isv4=True)
267 for asid in self.ass:
268 self.vapi.cli("lb as 90.0.0.0/8 2002::%u del" % (asid))
269 self.vapi.cli("lb vip 90.0.0.0/8 encap gre6 del")
270 self.vapi.cli("test lb flowtable flush")
272 def test_lb_ip6_gre6(self):
273 """Load Balancer IP6 GRE6 on vip case"""
275 self.vapi.cli("lb vip 2001::/16 encap gre6")
276 for asid in self.ass:
277 self.vapi.cli("lb as 2001::/16 2002::%u" % (asid))
279 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
280 self.pg_enable_capture(self.pg_interfaces)
283 self.checkCapture(encap="gre6", isv4=False)
285 for asid in self.ass:
286 self.vapi.cli("lb as 2001::/16 2002::%u del" % (asid))
287 self.vapi.cli("lb vip 2001::/16 encap gre6 del")
288 self.vapi.cli("test lb flowtable flush")
290 def test_lb_ip4_gre4_port(self):
291 """Load Balancer IP4 GRE4 on per-port-vip case"""
293 self.vapi.cli("lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4")
294 for asid in self.ass:
296 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u" % (asid)
299 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
300 self.pg_enable_capture(self.pg_interfaces)
302 self.checkCapture(encap="gre4", isv4=True)
305 for asid in self.ass:
307 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del" % (asid)
309 self.vapi.cli("lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4 del")
310 self.vapi.cli("test lb flowtable flush")
312 def test_lb_ip6_gre4_port(self):
313 """Load Balancer IP6 GRE4 on per-port-vip case"""
316 self.vapi.cli("lb vip 2001::/16 protocol udp port 20000 encap gre4")
317 for asid in self.ass:
319 "lb as 2001::/16 protocol udp port 20000 10.0.0.%u" % (asid)
322 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
323 self.pg_enable_capture(self.pg_interfaces)
326 self.checkCapture(encap="gre4", isv4=False)
328 for asid in self.ass:
330 "lb as 2001::/16 protocol udp port 20000 10.0.0.%u del" % (asid)
332 self.vapi.cli("lb vip 2001::/16 protocol udp port 20000 encap gre4 del")
333 self.vapi.cli("test lb flowtable flush")
335 def test_lb_ip4_gre6_port(self):
336 """Load Balancer IP4 GRE6 on per-port-vip case"""
338 self.vapi.cli("lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6")
339 for asid in self.ass:
341 "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u" % (asid)
344 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
345 self.pg_enable_capture(self.pg_interfaces)
348 self.checkCapture(encap="gre6", isv4=True)
350 for asid in self.ass:
352 "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u del" % (asid)
354 self.vapi.cli("lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6 del")
355 self.vapi.cli("test lb flowtable flush")
357 def test_lb_ip6_gre6_port(self):
358 """Load Balancer IP6 GRE6 on per-port-vip case"""
360 self.vapi.cli("lb vip 2001::/16 protocol udp port 20000 encap gre6")
361 for asid in self.ass:
363 "lb as 2001::/16 protocol udp port 20000 2002::%u" % (asid)
366 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
367 self.pg_enable_capture(self.pg_interfaces)
370 self.checkCapture(encap="gre6", isv4=False)
372 for asid in self.ass:
374 "lb as 2001::/16 protocol udp port 20000 2002::%u del" % (asid)
376 self.vapi.cli("lb vip 2001::/16 protocol udp port 20000 encap gre6 del")
377 self.vapi.cli("test lb flowtable flush")
379 def test_lb_ip4_l3dsr(self):
380 """Load Balancer IP4 L3DSR on vip case"""
382 self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7")
383 for asid in self.ass:
384 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u" % (asid))
386 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
387 self.pg_enable_capture(self.pg_interfaces)
389 self.checkCapture(encap="l3dsr", isv4=True)
392 for asid in self.ass:
393 self.vapi.cli("lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
394 self.vapi.cli("lb vip 90.0.0.0/8 encap l3dsr dscp 7 del")
395 self.vapi.cli("test lb flowtable flush")
397 def test_lb_ip4_l3dsr_port(self):
398 """Load Balancer IP4 L3DSR on per-port-vip case"""
401 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr dscp 7"
403 for asid in self.ass:
405 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u" % (asid)
408 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
409 self.pg_enable_capture(self.pg_interfaces)
411 self.checkCapture(encap="l3dsr", isv4=True)
414 for asid in self.ass:
416 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del" % (asid)
419 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr dscp 7 del"
421 self.vapi.cli("test lb flowtable flush")
423 def test_lb_ip4_nat4_port(self):
424 """Load Balancer IP4 NAT4 on per-port-vip case"""
427 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
428 " type clusterip target_port 3307"
430 for asid in self.ass:
432 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u" % (asid)
435 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
436 self.pg_enable_capture(self.pg_interfaces)
438 self.checkCapture(encap="nat4", isv4=True)
441 for asid in self.ass:
443 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del" % (asid)
446 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
447 " type clusterip target_port 3307 del"
449 self.vapi.cli("test lb flowtable flush")
451 def test_lb_ip6_nat6_port(self):
452 """Load Balancer IP6 NAT6 on per-port-vip case"""
455 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
456 " type clusterip target_port 3307"
458 for asid in self.ass:
460 "lb as 2001::/16 protocol udp port 20000 2002::%u" % (asid)
463 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
464 self.pg_enable_capture(self.pg_interfaces)
466 self.checkCapture(encap="nat6", isv4=False)
469 for asid in self.ass:
471 "lb as 2001::/16 protocol udp port 20000 2002::%u del" % (asid)
474 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
475 " type clusterip target_port 3307 del"
477 self.vapi.cli("test lb flowtable flush")