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:
56 dst4 = VppIpRoute(cls, "10.0.0.0", 24,
57 [VppRoutePath(cls.pg1.remote_ip4,
61 dst6 = VppIpRoute(cls, "2002::", 16,
62 [VppRoutePath(cls.pg1.remote_ip6,
66 cls.vapi.lb_conf(ip4_src_address="39.40.41.42",
67 ip6_src_address="2004::1")
69 super(TestLB, cls).tearDownClass()
73 def tearDownClass(cls):
74 super(TestLB, cls).tearDownClass()
77 super(TestLB, self).tearDown()
79 def show_commands_at_teardown(self):
80 self.logger.info(self.vapi.cli("show lb vip verbose"))
82 def getIPv4Flow(self, id):
83 return (IP(dst="90.0.%u.%u" % (id / 255, id % 255),
84 src="40.0.%u.%u" % (id / 255, id % 255)) /
85 UDP(sport=10000 + id, dport=20000))
87 def getIPv6Flow(self, id):
88 return (IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) /
89 UDP(sport=10000 + id, dport=20000))
91 def generatePackets(self, src_if, isv4):
92 self.reset_packet_infos()
94 for pktid in self.packets:
95 info = self.create_packet_info(src_if, self.pg1)
96 payload = self.info_to_payload(info)
97 ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
98 packet = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
101 self.extend_packet(packet, 128)
102 info.data = packet.copy()
106 def checkInner(self, gre, isv4):
107 IPver = IP if isv4 else IPv6
108 self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
109 self.assertEqual(gre.flags, 0)
110 self.assertEqual(gre.version, 0)
111 inner = IPver(scapy.compat.raw(gre.payload))
112 payload_info = self.payload_to_info(inner[Raw])
113 self.info = self.packet_infos[payload_info.index]
114 self.assertEqual(payload_info.src, self.pg0.sw_if_index)
115 self.assertEqual(scapy.compat.raw(inner),
116 scapy.compat.raw(self.info.data[IPver]))
118 def checkCapture(self, encap, isv4):
119 self.pg0.assert_nothing_captured()
120 out = self.pg1.get_capture(len(self.packets))
122 load = [0] * len(self.ass)
128 if (encap == 'gre4'):
130 asid = int(ip.dst.split(".")[3])
131 self.assertEqual(ip.version, 4)
132 self.assertEqual(ip.flags, 0)
133 self.assertEqual(ip.src, "39.40.41.42")
134 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
135 self.assertEqual(ip.proto, 47)
136 self.assertEqual(len(ip.options), 0)
138 self.checkInner(gre, isv4)
139 elif (encap == 'gre6'):
141 asid = ip.dst.split(":")
142 asid = asid[len(asid) - 1]
143 asid = 0 if asid == "" else int(asid)
144 self.assertEqual(ip.version, 6)
145 self.assertEqual(ip.tc, 0)
146 self.assertEqual(ip.fl, 0)
147 self.assertEqual(ip.src, "2004::1")
149 socket.inet_pton(socket.AF_INET6, ip.dst),
150 socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
152 self.assertEqual(ip.nh, 47)
153 # self.assertEqual(len(ip.options), 0)
154 gre = GRE(scapy.compat.raw(p[IPv6].payload))
155 self.checkInner(gre, isv4)
156 elif (encap == 'l3dsr'):
158 asid = int(ip.dst.split(".")[3])
159 self.assertEqual(ip.version, 4)
160 self.assertEqual(ip.flags, 0)
161 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
162 self.assertEqual(ip.tos, 0x1c)
163 self.assertEqual(len(ip.options), 0)
164 self.assert_ip_checksum_valid(p)
165 if ip.proto == IP_PROTOS.tcp:
166 self.assert_tcp_checksum_valid(p)
167 elif ip.proto == IP_PROTOS.udp:
168 self.assert_udp_checksum_valid(p)
169 elif (encap == 'nat4'):
171 asid = int(ip.dst.split(".")[3])
172 self.assertEqual(ip.version, 4)
173 self.assertEqual(ip.flags, 0)
174 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
175 self.assertEqual(ip.proto, 17)
176 self.assertEqual(len(ip.options), 0)
178 self.assertEqual(udp.dport, 3307)
179 elif (encap == 'nat6'):
181 asid = ip.dst.split(":")
182 asid = asid[len(asid) - 1]
183 asid = 0 if asid == "" else int(asid)
184 self.assertEqual(ip.version, 6)
185 self.assertEqual(ip.tc, 0)
186 self.assertEqual(ip.fl, 0)
188 socket.inet_pton(socket.AF_INET6, ip.dst),
189 socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
191 self.assertEqual(ip.nh, 17)
192 self.assertGreaterEqual(ip.hlim, 63)
193 udp = UDP(scapy.compat.raw(p[IPv6].payload))
194 self.assertEqual(udp.dport, 3307)
197 self.logger.error(ppp("Unexpected or invalid packet:", p))
200 # This is just to roughly check that the balancing algorithm
201 # is not completely biased.
202 for asid in self.ass:
203 if load[asid] < len(self.packets) / (len(self.ass) * 2):
205 "ASS is not balanced: load[%d] = %d" % (asid, load[asid]))
206 raise Exception("Load Balancer algorithm is biased")
208 def test_lb_ip4_gre4(self):
209 """ Load Balancer IP4 GRE4 on vip case """
212 "lb vip 90.0.0.0/8 encap gre4")
213 for asid in self.ass:
215 "lb as 90.0.0.0/8 10.0.0.%u"
218 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
219 self.pg_enable_capture(self.pg_interfaces)
221 self.checkCapture(encap='gre4', isv4=True)
224 for asid in self.ass:
226 "lb as 90.0.0.0/8 10.0.0.%u del"
229 "lb vip 90.0.0.0/8 encap gre4 del")
230 self.vapi.cli("test lb flowtable flush")
232 def test_lb_ip6_gre4(self):
233 """ Load Balancer IP6 GRE4 on vip case """
237 "lb vip 2001::/16 encap gre4")
238 for asid in self.ass:
240 "lb as 2001::/16 10.0.0.%u"
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:
251 "lb as 2001::/16 10.0.0.%u del"
254 "lb vip 2001::/16 encap gre4 del")
255 self.vapi.cli("test lb flowtable flush")
257 def test_lb_ip4_gre6(self):
258 """ Load Balancer IP4 GRE6 on vip case """
261 "lb vip 90.0.0.0/8 encap gre6")
262 for asid in self.ass:
264 "lb as 90.0.0.0/8 2002::%u"
267 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
268 self.pg_enable_capture(self.pg_interfaces)
271 self.checkCapture(encap='gre6', isv4=True)
273 for asid in self.ass:
275 "lb as 90.0.0.0/8 2002::%u del"
278 "lb vip 90.0.0.0/8 encap gre6 del")
279 self.vapi.cli("test lb flowtable flush")
281 def test_lb_ip6_gre6(self):
282 """ Load Balancer IP6 GRE6 on vip case """
285 "lb vip 2001::/16 encap gre6")
286 for asid in self.ass:
288 "lb as 2001::/16 2002::%u"
291 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
292 self.pg_enable_capture(self.pg_interfaces)
295 self.checkCapture(encap='gre6', isv4=False)
297 for asid in self.ass:
299 "lb as 2001::/16 2002::%u del"
302 "lb vip 2001::/16 encap gre6 del")
303 self.vapi.cli("test lb flowtable flush")
305 def test_lb_ip4_gre4_port(self):
306 """ Load Balancer IP4 GRE4 on per-port-vip case """
309 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4")
310 for asid in self.ass:
312 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
315 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
316 self.pg_enable_capture(self.pg_interfaces)
318 self.checkCapture(encap='gre4', isv4=True)
321 for asid in self.ass:
323 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
326 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4 del")
327 self.vapi.cli("test lb flowtable flush")
329 def test_lb_ip6_gre4_port(self):
330 """ Load Balancer IP6 GRE4 on per-port-vip case """
334 "lb vip 2001::/16 protocol udp port 20000 encap gre4")
335 for asid in self.ass:
337 "lb as 2001::/16 protocol udp port 20000 10.0.0.%u"
340 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
341 self.pg_enable_capture(self.pg_interfaces)
344 self.checkCapture(encap='gre4', isv4=False)
346 for asid in self.ass:
348 "lb as 2001::/16 protocol udp port 20000 10.0.0.%u del"
351 "lb vip 2001::/16 protocol udp port 20000 encap gre4 del")
352 self.vapi.cli("test lb flowtable flush")
354 def test_lb_ip4_gre6_port(self):
355 """ Load Balancer IP4 GRE6 on per-port-vip case """
358 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6")
359 for asid in self.ass:
361 "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u"
364 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
365 self.pg_enable_capture(self.pg_interfaces)
368 self.checkCapture(encap='gre6', isv4=True)
370 for asid in self.ass:
372 "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u del"
375 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6 del")
376 self.vapi.cli("test lb flowtable flush")
378 def test_lb_ip6_gre6_port(self):
379 """ Load Balancer IP6 GRE6 on per-port-vip case """
382 "lb vip 2001::/16 protocol udp port 20000 encap gre6")
383 for asid in self.ass:
385 "lb as 2001::/16 protocol udp port 20000 2002::%u"
388 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
389 self.pg_enable_capture(self.pg_interfaces)
392 self.checkCapture(encap='gre6', isv4=False)
394 for asid in self.ass:
396 "lb as 2001::/16 protocol udp port 20000 2002::%u del"
399 "lb vip 2001::/16 protocol udp port 20000 encap gre6 del")
400 self.vapi.cli("test lb flowtable flush")
402 def test_lb_ip4_l3dsr(self):
403 """ Load Balancer IP4 L3DSR on vip case """
406 "lb vip 90.0.0.0/8 encap l3dsr dscp 7")
407 for asid in self.ass:
409 "lb as 90.0.0.0/8 10.0.0.%u"
412 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
413 self.pg_enable_capture(self.pg_interfaces)
415 self.checkCapture(encap='l3dsr', isv4=True)
418 for asid in self.ass:
420 "lb as 90.0.0.0/8 10.0.0.%u del"
423 "lb vip 90.0.0.0/8 encap l3dsr"
425 self.vapi.cli("test lb flowtable flush")
427 def test_lb_ip4_l3dsr_port(self):
428 """ Load Balancer IP4 L3DSR on per-port-vip case """
431 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr dscp 7")
432 for asid in self.ass:
434 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
437 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
438 self.pg_enable_capture(self.pg_interfaces)
440 self.checkCapture(encap='l3dsr', isv4=True)
443 for asid in self.ass:
445 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
448 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr"
450 self.vapi.cli("test lb flowtable flush")
452 def test_lb_ip4_nat4_port(self):
453 """ Load Balancer IP4 NAT4 on per-port-vip case """
456 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
457 " type clusterip target_port 3307")
458 for asid in self.ass:
460 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
463 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
464 self.pg_enable_capture(self.pg_interfaces)
466 self.checkCapture(encap='nat4', isv4=True)
469 for asid in self.ass:
471 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
474 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
475 " type clusterip target_port 3307 del")
476 self.vapi.cli("test lb flowtable flush")
478 def test_lb_ip6_nat6_port(self):
479 """ Load Balancer IP6 NAT6 on per-port-vip case """
482 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
483 " type clusterip target_port 3307")
484 for asid in self.ass:
486 "lb as 2001::/16 protocol udp port 20000 2002::%u"
489 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
490 self.pg_enable_capture(self.pg_interfaces)
492 self.checkCapture(encap='nat6', isv4=False)
495 for asid in self.ass:
497 "lb as 2001::/16 protocol udp port 20000 2002::%u del"
500 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
501 " type clusterip target_port 3307 del")
502 self.vapi.cli("test lb flowtable flush")