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
13 """ TestLB is a subclass of VPPTestCase classes.
15 TestLB class defines Load Balancer test cases for:
16 - IP4 to GRE4 encap on per-port vip case
17 - IP4 to GRE6 encap on per-port vip case
18 - IP6 to GRE4 encap on per-port vip case
19 - IP6 to GRE6 encap on per-port vip case
20 - IP4 to L3DSR encap on vip case
21 - IP4 to L3DSR encap on per-port vip case
22 - IP4 to NAT4 encap on per-port vip case
23 - IP6 to NAT6 encap on per-port vip case
25 As stated in comments below, GRE has issues with IPv6.
26 All test cases involving IPv6 are executed, but
27 received packets are not parsed and checked.
32 class TestLB(VppTestCase):
33 """ Load Balancer Test Case """
37 super(TestLB, cls).setUpClass()
40 cls.packets = range(1)
43 cls.create_pg_interfaces(range(2))
44 cls.interfaces = list(cls.pg_interfaces)
46 for i in cls.interfaces:
53 dst4 = socket.inet_pton(socket.AF_INET, "10.0.0.0")
54 dst6 = socket.inet_pton(socket.AF_INET6, "2002::")
55 cls.vapi.ip_add_del_route(dst_address=dst4, dst_address_length=24,
56 next_hop_address=cls.pg1.remote_ip4n)
57 cls.vapi.ip_add_del_route(dst_address=dst6, dst_address_length=16,
58 next_hop_address=cls.pg1.remote_ip6n,
60 cls.vapi.lb_conf(ip4_src_address="39.40.41.42",
61 ip6_src_address="2004::1")
63 super(TestLB, cls).tearDownClass()
67 def tearDownClass(cls):
68 super(TestLB, cls).tearDownClass()
71 super(TestLB, self).tearDown()
73 def show_commands_at_teardown(self):
74 self.logger.info(self.vapi.cli("show lb vip verbose"))
76 def getIPv4Flow(self, id):
77 return (IP(dst="90.0.%u.%u" % (id / 255, id % 255),
78 src="40.0.%u.%u" % (id / 255, id % 255)) /
79 UDP(sport=10000 + id, dport=20000))
81 def getIPv6Flow(self, id):
82 return (IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) /
83 UDP(sport=10000 + id, dport=20000))
85 def generatePackets(self, src_if, isv4):
86 self.reset_packet_infos()
88 for pktid in self.packets:
89 info = self.create_packet_info(src_if, self.pg1)
90 payload = self.info_to_payload(info)
91 ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
92 packet = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
95 self.extend_packet(packet, 128)
96 info.data = packet.copy()
100 def checkInner(self, gre, isv4):
101 IPver = IP if isv4 else IPv6
102 self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
103 self.assertEqual(gre.flags, 0)
104 self.assertEqual(gre.version, 0)
105 inner = IPver(scapy.compat.raw(gre.payload))
106 payload_info = self.payload_to_info(inner[Raw])
107 self.info = self.packet_infos[payload_info.index]
108 self.assertEqual(payload_info.src, self.pg0.sw_if_index)
109 self.assertEqual(scapy.compat.raw(inner),
110 scapy.compat.raw(self.info.data[IPver]))
112 def checkCapture(self, encap, isv4):
113 self.pg0.assert_nothing_captured()
114 out = self.pg1.get_capture(len(self.packets))
116 load = [0] * len(self.ass)
122 if (encap == 'gre4'):
124 asid = int(ip.dst.split(".")[3])
125 self.assertEqual(ip.version, 4)
126 self.assertEqual(ip.flags, 0)
127 self.assertEqual(ip.src, "39.40.41.42")
128 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
129 self.assertEqual(ip.proto, 47)
130 self.assertEqual(len(ip.options), 0)
132 self.checkInner(gre, isv4)
133 elif (encap == 'gre6'):
135 asid = ip.dst.split(":")
136 asid = asid[len(asid) - 1]
137 asid = 0 if asid == "" else int(asid)
138 self.assertEqual(ip.version, 6)
139 self.assertEqual(ip.tc, 0)
140 self.assertEqual(ip.fl, 0)
141 self.assertEqual(ip.src, "2004::1")
143 socket.inet_pton(socket.AF_INET6, ip.dst),
144 socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
146 self.assertEqual(ip.nh, 47)
147 # self.assertEqual(len(ip.options), 0)
148 gre = GRE(scapy.compat.raw(p[IPv6].payload))
149 self.checkInner(gre, isv4)
150 elif (encap == 'l3dsr'):
152 asid = int(ip.dst.split(".")[3])
153 self.assertEqual(ip.version, 4)
154 self.assertEqual(ip.flags, 0)
155 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
156 self.assertEqual(ip.tos, 0x1c)
157 self.assertEqual(len(ip.options), 0)
158 self.assert_ip_checksum_valid(p)
159 if ip.proto == IP_PROTOS.tcp:
160 self.assert_tcp_checksum_valid(p)
161 elif ip.proto == IP_PROTOS.udp:
162 self.assert_udp_checksum_valid(p)
163 elif (encap == 'nat4'):
165 asid = int(ip.dst.split(".")[3])
166 self.assertEqual(ip.version, 4)
167 self.assertEqual(ip.flags, 0)
168 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
169 self.assertEqual(ip.proto, 17)
170 self.assertEqual(len(ip.options), 0)
172 self.assertEqual(udp.dport, 3307)
173 elif (encap == 'nat6'):
175 asid = ip.dst.split(":")
176 asid = asid[len(asid) - 1]
177 asid = 0 if asid == "" else int(asid)
178 self.assertEqual(ip.version, 6)
179 self.assertEqual(ip.tc, 0)
180 self.assertEqual(ip.fl, 0)
182 socket.inet_pton(socket.AF_INET6, ip.dst),
183 socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
185 self.assertEqual(ip.nh, 17)
186 self.assertGreaterEqual(ip.hlim, 63)
187 udp = UDP(scapy.compat.raw(p[IPv6].payload))
188 self.assertEqual(udp.dport, 3307)
191 self.logger.error(ppp("Unexpected or invalid packet:", p))
194 # This is just to roughly check that the balancing algorithm
195 # is not completely biased.
196 for asid in self.ass:
197 if load[asid] < len(self.packets) / (len(self.ass) * 2):
199 "ASS is not balanced: load[%d] = %d" % (asid, load[asid]))
200 raise Exception("Load Balancer algorithm is biased")
202 def test_lb_ip4_gre4(self):
203 """ Load Balancer IP4 GRE4 on vip case """
206 "lb vip 90.0.0.0/8 encap gre4")
207 for asid in self.ass:
209 "lb as 90.0.0.0/8 10.0.0.%u"
212 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
213 self.pg_enable_capture(self.pg_interfaces)
215 self.checkCapture(encap='gre4', isv4=True)
218 for asid in self.ass:
220 "lb as 90.0.0.0/8 10.0.0.%u del"
223 "lb vip 90.0.0.0/8 encap gre4 del")
224 self.vapi.cli("test lb flowtable flush")
226 def test_lb_ip6_gre4(self):
227 """ Load Balancer IP6 GRE4 on vip case """
231 "lb vip 2001::/16 encap gre4")
232 for asid in self.ass:
234 "lb as 2001::/16 10.0.0.%u"
237 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
238 self.pg_enable_capture(self.pg_interfaces)
241 self.checkCapture(encap='gre4', isv4=False)
243 for asid in self.ass:
245 "lb as 2001::/16 10.0.0.%u del"
248 "lb vip 2001::/16 encap gre4 del")
249 self.vapi.cli("test lb flowtable flush")
251 def test_lb_ip4_gre6(self):
252 """ Load Balancer IP4 GRE6 on vip case """
255 "lb vip 90.0.0.0/8 encap gre6")
256 for asid in self.ass:
258 "lb as 90.0.0.0/8 2002::%u"
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:
269 "lb as 90.0.0.0/8 2002::%u del"
272 "lb vip 90.0.0.0/8 encap gre6 del")
273 self.vapi.cli("test lb flowtable flush")
275 def test_lb_ip6_gre6(self):
276 """ Load Balancer IP6 GRE6 on vip case """
279 "lb vip 2001::/16 encap gre6")
280 for asid in self.ass:
282 "lb as 2001::/16 2002::%u"
285 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
286 self.pg_enable_capture(self.pg_interfaces)
289 self.checkCapture(encap='gre6', isv4=False)
291 for asid in self.ass:
293 "lb as 2001::/16 2002::%u del"
296 "lb vip 2001::/16 encap gre6 del")
297 self.vapi.cli("test lb flowtable flush")
299 def test_lb_ip4_gre4_port(self):
300 """ Load Balancer IP4 GRE4 on per-port-vip case """
303 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4")
304 for asid in self.ass:
306 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
309 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
310 self.pg_enable_capture(self.pg_interfaces)
312 self.checkCapture(encap='gre4', isv4=True)
315 for asid in self.ass:
317 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
320 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4 del")
321 self.vapi.cli("test lb flowtable flush")
323 def test_lb_ip6_gre4_port(self):
324 """ Load Balancer IP6 GRE4 on per-port-vip case """
328 "lb vip 2001::/16 protocol udp port 20000 encap gre4")
329 for asid in self.ass:
331 "lb as 2001::/16 protocol udp port 20000 10.0.0.%u"
334 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
335 self.pg_enable_capture(self.pg_interfaces)
338 self.checkCapture(encap='gre4', isv4=False)
340 for asid in self.ass:
342 "lb as 2001::/16 protocol udp port 20000 10.0.0.%u del"
345 "lb vip 2001::/16 protocol udp port 20000 encap gre4 del")
346 self.vapi.cli("test lb flowtable flush")
348 def test_lb_ip4_gre6_port(self):
349 """ Load Balancer IP4 GRE6 on per-port-vip case """
352 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6")
353 for asid in self.ass:
355 "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u"
358 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
359 self.pg_enable_capture(self.pg_interfaces)
362 self.checkCapture(encap='gre6', isv4=True)
364 for asid in self.ass:
366 "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u del"
369 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6 del")
370 self.vapi.cli("test lb flowtable flush")
372 def test_lb_ip6_gre6_port(self):
373 """ Load Balancer IP6 GRE6 on per-port-vip case """
376 "lb vip 2001::/16 protocol udp port 20000 encap gre6")
377 for asid in self.ass:
379 "lb as 2001::/16 protocol udp port 20000 2002::%u"
382 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
383 self.pg_enable_capture(self.pg_interfaces)
386 self.checkCapture(encap='gre6', isv4=False)
388 for asid in self.ass:
390 "lb as 2001::/16 protocol udp port 20000 2002::%u del"
393 "lb vip 2001::/16 protocol udp port 20000 encap gre6 del")
394 self.vapi.cli("test lb flowtable flush")
396 def test_lb_ip4_l3dsr(self):
397 """ Load Balancer IP4 L3DSR on vip case """
400 "lb vip 90.0.0.0/8 encap l3dsr dscp 7")
401 for asid in self.ass:
403 "lb as 90.0.0.0/8 10.0.0.%u"
406 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
407 self.pg_enable_capture(self.pg_interfaces)
409 self.checkCapture(encap='l3dsr', isv4=True)
412 for asid in self.ass:
414 "lb as 90.0.0.0/8 10.0.0.%u del"
417 "lb vip 90.0.0.0/8 encap l3dsr"
419 self.vapi.cli("test lb flowtable flush")
421 def test_lb_ip4_l3dsr_port(self):
422 """ Load Balancer IP4 L3DSR on per-port-vip case """
425 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr dscp 7")
426 for asid in self.ass:
428 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
431 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
432 self.pg_enable_capture(self.pg_interfaces)
434 self.checkCapture(encap='l3dsr', isv4=True)
437 for asid in self.ass:
439 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
442 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr"
444 self.vapi.cli("test lb flowtable flush")
446 def test_lb_ip4_nat4_port(self):
447 """ Load Balancer IP4 NAT4 on per-port-vip case """
450 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
451 " type clusterip target_port 3307")
452 for asid in self.ass:
454 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
457 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
458 self.pg_enable_capture(self.pg_interfaces)
460 self.checkCapture(encap='nat4', isv4=True)
463 for asid in self.ass:
465 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
468 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
469 " type clusterip target_port 3307 del")
470 self.vapi.cli("test lb flowtable flush")
472 def test_lb_ip6_nat6_port(self):
473 """ Load Balancer IP6 NAT6 on per-port-vip case """
476 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
477 " type clusterip target_port 3307")
478 for asid in self.ass:
480 "lb as 2001::/16 protocol udp port 20000 2002::%u"
483 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
484 self.pg_enable_capture(self.pg_interfaces)
486 self.checkCapture(encap='nat6', isv4=False)
489 for asid in self.ass:
491 "lb as 2001::/16 protocol udp port 20000 2002::%u del"
494 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
495 " type clusterip target_port 3307 del")
496 self.vapi.cli("test lb flowtable flush")