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 super(TestLB, self).tearDown()
69 self.logger.info(self.vapi.cli("show lb vip verbose"))
71 def getIPv4Flow(self, id):
72 return (IP(dst="90.0.%u.%u" % (id / 255, id % 255),
73 src="40.0.%u.%u" % (id / 255, id % 255)) /
74 UDP(sport=10000 + id, dport=20000))
76 def getIPv6Flow(self, id):
77 return (IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) /
78 UDP(sport=10000 + id, dport=20000))
80 def generatePackets(self, src_if, isv4):
81 self.reset_packet_infos()
83 for pktid in self.packets:
84 info = self.create_packet_info(src_if, self.pg1)
85 payload = self.info_to_payload(info)
86 ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
87 packet = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
90 self.extend_packet(packet, 128)
91 info.data = packet.copy()
95 def checkInner(self, gre, isv4):
96 IPver = IP if isv4 else IPv6
97 self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
98 self.assertEqual(gre.flags, 0)
99 self.assertEqual(gre.version, 0)
100 inner = IPver(scapy.compat.raw(gre.payload))
101 payload_info = self.payload_to_info(inner[Raw])
102 self.info = self.packet_infos[payload_info.index]
103 self.assertEqual(payload_info.src, self.pg0.sw_if_index)
104 self.assertEqual(scapy.compat.raw(inner),
105 scapy.compat.raw(self.info.data[IPver]))
107 def checkCapture(self, encap, isv4):
108 self.pg0.assert_nothing_captured()
109 out = self.pg1.get_capture(len(self.packets))
111 load = [0] * len(self.ass)
117 if (encap == 'gre4'):
119 asid = int(ip.dst.split(".")[3])
120 self.assertEqual(ip.version, 4)
121 self.assertEqual(ip.flags, 0)
122 self.assertEqual(ip.src, "39.40.41.42")
123 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
124 self.assertEqual(ip.proto, 47)
125 self.assertEqual(len(ip.options), 0)
127 self.checkInner(gre, isv4)
128 elif (encap == 'gre6'):
130 asid = ip.dst.split(":")
131 asid = asid[len(asid) - 1]
132 asid = 0 if asid == "" else int(asid)
133 self.assertEqual(ip.version, 6)
134 self.assertEqual(ip.tc, 0)
135 self.assertEqual(ip.fl, 0)
136 self.assertEqual(ip.src, "2004::1")
138 socket.inet_pton(socket.AF_INET6, ip.dst),
139 socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
141 self.assertEqual(ip.nh, 47)
142 # self.assertEqual(len(ip.options), 0)
143 gre = GRE(scapy.compat.raw(p[IPv6].payload))
144 self.checkInner(gre, isv4)
145 elif (encap == 'l3dsr'):
147 asid = int(ip.dst.split(".")[3])
148 self.assertEqual(ip.version, 4)
149 self.assertEqual(ip.flags, 0)
150 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
151 self.assertEqual(ip.tos, 0x1c)
152 self.assertEqual(len(ip.options), 0)
153 self.assert_ip_checksum_valid(p)
154 if ip.proto == IP_PROTOS.tcp:
155 self.assert_tcp_checksum_valid(p)
156 elif ip.proto == IP_PROTOS.udp:
157 self.assert_udp_checksum_valid(p)
158 elif (encap == 'nat4'):
160 asid = int(ip.dst.split(".")[3])
161 self.assertEqual(ip.version, 4)
162 self.assertEqual(ip.flags, 0)
163 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
164 self.assertEqual(ip.proto, 17)
165 self.assertEqual(len(ip.options), 0)
167 self.assertEqual(udp.dport, 3307)
168 elif (encap == 'nat6'):
170 asid = ip.dst.split(":")
171 asid = asid[len(asid) - 1]
172 asid = 0 if asid == "" else int(asid)
173 self.assertEqual(ip.version, 6)
174 self.assertEqual(ip.tc, 0)
175 self.assertEqual(ip.fl, 0)
177 socket.inet_pton(socket.AF_INET6, ip.dst),
178 socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
180 self.assertEqual(ip.nh, 17)
181 self.assertGreaterEqual(ip.hlim, 63)
182 udp = UDP(scapy.compat.raw(p[IPv6].payload))
183 self.assertEqual(udp.dport, 3307)
186 self.logger.error(ppp("Unexpected or invalid packet:", p))
189 # This is just to roughly check that the balancing algorithm
190 # is not completely biased.
191 for asid in self.ass:
192 if load[asid] < len(self.packets) / (len(self.ass) * 2):
194 "ASS is not balanced: load[%d] = %d" % (asid, load[asid]))
195 raise Exception("Load Balancer algorithm is biased")
197 def test_lb_ip4_gre4(self):
198 """ Load Balancer IP4 GRE4 on vip case """
201 "lb vip 90.0.0.0/8 encap gre4")
202 for asid in self.ass:
204 "lb as 90.0.0.0/8 10.0.0.%u"
207 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
208 self.pg_enable_capture(self.pg_interfaces)
210 self.checkCapture(encap='gre4', isv4=True)
213 for asid in self.ass:
215 "lb as 90.0.0.0/8 10.0.0.%u del"
218 "lb vip 90.0.0.0/8 encap gre4 del")
219 self.vapi.cli("test lb flowtable flush")
221 def test_lb_ip6_gre4(self):
222 """ Load Balancer IP6 GRE4 on vip case """
226 "lb vip 2001::/16 encap gre4")
227 for asid in self.ass:
229 "lb as 2001::/16 10.0.0.%u"
232 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
233 self.pg_enable_capture(self.pg_interfaces)
236 self.checkCapture(encap='gre4', isv4=False)
238 for asid in self.ass:
240 "lb as 2001::/16 10.0.0.%u del"
243 "lb vip 2001::/16 encap gre4 del")
244 self.vapi.cli("test lb flowtable flush")
246 def test_lb_ip4_gre6(self):
247 """ Load Balancer IP4 GRE6 on vip case """
250 "lb vip 90.0.0.0/8 encap gre6")
251 for asid in self.ass:
253 "lb as 90.0.0.0/8 2002::%u"
256 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
257 self.pg_enable_capture(self.pg_interfaces)
260 self.checkCapture(encap='gre6', isv4=True)
262 for asid in self.ass:
264 "lb as 90.0.0.0/8 2002::%u del"
267 "lb vip 90.0.0.0/8 encap gre6 del")
268 self.vapi.cli("test lb flowtable flush")
270 def test_lb_ip6_gre6(self):
271 """ Load Balancer IP6 GRE6 on vip case """
274 "lb vip 2001::/16 encap gre6")
275 for asid in self.ass:
277 "lb as 2001::/16 2002::%u"
280 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
281 self.pg_enable_capture(self.pg_interfaces)
284 self.checkCapture(encap='gre6', isv4=False)
286 for asid in self.ass:
288 "lb as 2001::/16 2002::%u del"
291 "lb vip 2001::/16 encap gre6 del")
292 self.vapi.cli("test lb flowtable flush")
294 def test_lb_ip4_gre4_port(self):
295 """ Load Balancer IP4 GRE4 on per-port-vip case """
298 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4")
299 for asid in self.ass:
301 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
304 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
305 self.pg_enable_capture(self.pg_interfaces)
307 self.checkCapture(encap='gre4', isv4=True)
310 for asid in self.ass:
312 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
315 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4 del")
316 self.vapi.cli("test lb flowtable flush")
318 def test_lb_ip6_gre4_port(self):
319 """ Load Balancer IP6 GRE4 on per-port-vip case """
323 "lb vip 2001::/16 protocol udp port 20000 encap gre4")
324 for asid in self.ass:
326 "lb as 2001::/16 protocol udp port 20000 10.0.0.%u"
329 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
330 self.pg_enable_capture(self.pg_interfaces)
333 self.checkCapture(encap='gre4', isv4=False)
335 for asid in self.ass:
337 "lb as 2001::/16 protocol udp port 20000 10.0.0.%u del"
340 "lb vip 2001::/16 protocol udp port 20000 encap gre4 del")
341 self.vapi.cli("test lb flowtable flush")
343 def test_lb_ip4_gre6_port(self):
344 """ Load Balancer IP4 GRE6 on per-port-vip case """
347 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6")
348 for asid in self.ass:
350 "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u"
353 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
354 self.pg_enable_capture(self.pg_interfaces)
357 self.checkCapture(encap='gre6', isv4=True)
359 for asid in self.ass:
361 "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u del"
364 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6 del")
365 self.vapi.cli("test lb flowtable flush")
367 def test_lb_ip6_gre6_port(self):
368 """ Load Balancer IP6 GRE6 on per-port-vip case """
371 "lb vip 2001::/16 protocol udp port 20000 encap gre6")
372 for asid in self.ass:
374 "lb as 2001::/16 protocol udp port 20000 2002::%u"
377 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
378 self.pg_enable_capture(self.pg_interfaces)
381 self.checkCapture(encap='gre6', isv4=False)
383 for asid in self.ass:
385 "lb as 2001::/16 protocol udp port 20000 2002::%u del"
388 "lb vip 2001::/16 protocol udp port 20000 encap gre6 del")
389 self.vapi.cli("test lb flowtable flush")
391 def test_lb_ip4_l3dsr(self):
392 """ Load Balancer IP4 L3DSR on vip case """
395 "lb vip 90.0.0.0/8 encap l3dsr dscp 7")
396 for asid in self.ass:
398 "lb as 90.0.0.0/8 10.0.0.%u"
401 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
402 self.pg_enable_capture(self.pg_interfaces)
404 self.checkCapture(encap='l3dsr', isv4=True)
407 for asid in self.ass:
409 "lb as 90.0.0.0/8 10.0.0.%u del"
412 "lb vip 90.0.0.0/8 encap l3dsr"
414 self.vapi.cli("test lb flowtable flush")
416 def test_lb_ip4_l3dsr_port(self):
417 """ Load Balancer IP4 L3DSR on per-port-vip case """
420 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr dscp 7")
421 for asid in self.ass:
423 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
426 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
427 self.pg_enable_capture(self.pg_interfaces)
429 self.checkCapture(encap='l3dsr', isv4=True)
432 for asid in self.ass:
434 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
437 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr"
439 self.vapi.cli("test lb flowtable flush")
441 def test_lb_ip4_nat4_port(self):
442 """ Load Balancer IP4 NAT4 on per-port-vip case """
445 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
446 " type clusterip target_port 3307")
447 for asid in self.ass:
449 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
452 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
453 self.pg_enable_capture(self.pg_interfaces)
455 self.checkCapture(encap='nat4', isv4=True)
458 for asid in self.ass:
460 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
463 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
464 " type clusterip target_port 3307 del")
465 self.vapi.cli("test lb flowtable flush")
467 def test_lb_ip6_nat6_port(self):
468 """ Load Balancer IP6 NAT6 on per-port-vip case """
471 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
472 " type clusterip target_port 3307")
473 for asid in self.ass:
475 "lb as 2001::/16 protocol udp port 20000 2002::%u"
478 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
479 self.pg_enable_capture(self.pg_interfaces)
481 self.checkCapture(encap='nat6', isv4=False)
484 for asid in self.ass:
486 "lb as 2001::/16 protocol udp port 20000 2002::%u del"
489 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
490 " type clusterip target_port 3307 del")
491 self.vapi.cli("test lb flowtable flush")