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 self.logger.info(self.vapi.cli("show lb vip verbose"))
75 def getIPv4Flow(self, id):
76 return (IP(dst="90.0.%u.%u" % (id / 255, id % 255),
77 src="40.0.%u.%u" % (id / 255, id % 255)) /
78 UDP(sport=10000 + id, dport=20000))
80 def getIPv6Flow(self, id):
81 return (IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) /
82 UDP(sport=10000 + id, dport=20000))
84 def generatePackets(self, src_if, isv4):
85 self.reset_packet_infos()
87 for pktid in self.packets:
88 info = self.create_packet_info(src_if, self.pg1)
89 payload = self.info_to_payload(info)
90 ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
91 packet = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
94 self.extend_packet(packet, 128)
95 info.data = packet.copy()
99 def checkInner(self, gre, isv4):
100 IPver = IP if isv4 else IPv6
101 self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
102 self.assertEqual(gre.flags, 0)
103 self.assertEqual(gre.version, 0)
104 inner = IPver(scapy.compat.raw(gre.payload))
105 payload_info = self.payload_to_info(inner[Raw])
106 self.info = self.packet_infos[payload_info.index]
107 self.assertEqual(payload_info.src, self.pg0.sw_if_index)
108 self.assertEqual(scapy.compat.raw(inner),
109 scapy.compat.raw(self.info.data[IPver]))
111 def checkCapture(self, encap, isv4):
112 self.pg0.assert_nothing_captured()
113 out = self.pg1.get_capture(len(self.packets))
115 load = [0] * len(self.ass)
121 if (encap == 'gre4'):
123 asid = int(ip.dst.split(".")[3])
124 self.assertEqual(ip.version, 4)
125 self.assertEqual(ip.flags, 0)
126 self.assertEqual(ip.src, "39.40.41.42")
127 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
128 self.assertEqual(ip.proto, 47)
129 self.assertEqual(len(ip.options), 0)
131 self.checkInner(gre, isv4)
132 elif (encap == 'gre6'):
134 asid = ip.dst.split(":")
135 asid = asid[len(asid) - 1]
136 asid = 0 if asid == "" else int(asid)
137 self.assertEqual(ip.version, 6)
138 self.assertEqual(ip.tc, 0)
139 self.assertEqual(ip.fl, 0)
140 self.assertEqual(ip.src, "2004::1")
142 socket.inet_pton(socket.AF_INET6, ip.dst),
143 socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
145 self.assertEqual(ip.nh, 47)
146 # self.assertEqual(len(ip.options), 0)
147 gre = GRE(scapy.compat.raw(p[IPv6].payload))
148 self.checkInner(gre, isv4)
149 elif (encap == 'l3dsr'):
151 asid = int(ip.dst.split(".")[3])
152 self.assertEqual(ip.version, 4)
153 self.assertEqual(ip.flags, 0)
154 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
155 self.assertEqual(ip.tos, 0x1c)
156 self.assertEqual(len(ip.options), 0)
157 self.assert_ip_checksum_valid(p)
158 if ip.proto == IP_PROTOS.tcp:
159 self.assert_tcp_checksum_valid(p)
160 elif ip.proto == IP_PROTOS.udp:
161 self.assert_udp_checksum_valid(p)
162 elif (encap == 'nat4'):
164 asid = int(ip.dst.split(".")[3])
165 self.assertEqual(ip.version, 4)
166 self.assertEqual(ip.flags, 0)
167 self.assertEqual(ip.dst, "10.0.0.%u" % asid)
168 self.assertEqual(ip.proto, 17)
169 self.assertEqual(len(ip.options), 0)
171 self.assertEqual(udp.dport, 3307)
172 elif (encap == 'nat6'):
174 asid = ip.dst.split(":")
175 asid = asid[len(asid) - 1]
176 asid = 0 if asid == "" else int(asid)
177 self.assertEqual(ip.version, 6)
178 self.assertEqual(ip.tc, 0)
179 self.assertEqual(ip.fl, 0)
181 socket.inet_pton(socket.AF_INET6, ip.dst),
182 socket.inet_pton(socket.AF_INET6, "2002::%u" % asid)
184 self.assertEqual(ip.nh, 17)
185 self.assertGreaterEqual(ip.hlim, 63)
186 udp = UDP(scapy.compat.raw(p[IPv6].payload))
187 self.assertEqual(udp.dport, 3307)
190 self.logger.error(ppp("Unexpected or invalid packet:", p))
193 # This is just to roughly check that the balancing algorithm
194 # is not completely biased.
195 for asid in self.ass:
196 if load[asid] < len(self.packets) / (len(self.ass) * 2):
198 "ASS is not balanced: load[%d] = %d" % (asid, load[asid]))
199 raise Exception("Load Balancer algorithm is biased")
201 def test_lb_ip4_gre4(self):
202 """ Load Balancer IP4 GRE4 on vip case """
205 "lb vip 90.0.0.0/8 encap gre4")
206 for asid in self.ass:
208 "lb as 90.0.0.0/8 10.0.0.%u"
211 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
212 self.pg_enable_capture(self.pg_interfaces)
214 self.checkCapture(encap='gre4', isv4=True)
217 for asid in self.ass:
219 "lb as 90.0.0.0/8 10.0.0.%u del"
222 "lb vip 90.0.0.0/8 encap gre4 del")
223 self.vapi.cli("test lb flowtable flush")
225 def test_lb_ip6_gre4(self):
226 """ Load Balancer IP6 GRE4 on vip case """
230 "lb vip 2001::/16 encap gre4")
231 for asid in self.ass:
233 "lb as 2001::/16 10.0.0.%u"
236 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
237 self.pg_enable_capture(self.pg_interfaces)
240 self.checkCapture(encap='gre4', isv4=False)
242 for asid in self.ass:
244 "lb as 2001::/16 10.0.0.%u del"
247 "lb vip 2001::/16 encap gre4 del")
248 self.vapi.cli("test lb flowtable flush")
250 def test_lb_ip4_gre6(self):
251 """ Load Balancer IP4 GRE6 on vip case """
254 "lb vip 90.0.0.0/8 encap gre6")
255 for asid in self.ass:
257 "lb as 90.0.0.0/8 2002::%u"
260 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
261 self.pg_enable_capture(self.pg_interfaces)
264 self.checkCapture(encap='gre6', isv4=True)
266 for asid in self.ass:
268 "lb as 90.0.0.0/8 2002::%u del"
271 "lb vip 90.0.0.0/8 encap gre6 del")
272 self.vapi.cli("test lb flowtable flush")
274 def test_lb_ip6_gre6(self):
275 """ Load Balancer IP6 GRE6 on vip case """
278 "lb vip 2001::/16 encap gre6")
279 for asid in self.ass:
281 "lb as 2001::/16 2002::%u"
284 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
285 self.pg_enable_capture(self.pg_interfaces)
288 self.checkCapture(encap='gre6', isv4=False)
290 for asid in self.ass:
292 "lb as 2001::/16 2002::%u del"
295 "lb vip 2001::/16 encap gre6 del")
296 self.vapi.cli("test lb flowtable flush")
298 def test_lb_ip4_gre4_port(self):
299 """ Load Balancer IP4 GRE4 on per-port-vip case """
302 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4")
303 for asid in self.ass:
305 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
308 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
309 self.pg_enable_capture(self.pg_interfaces)
311 self.checkCapture(encap='gre4', isv4=True)
314 for asid in self.ass:
316 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
319 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre4 del")
320 self.vapi.cli("test lb flowtable flush")
322 def test_lb_ip6_gre4_port(self):
323 """ Load Balancer IP6 GRE4 on per-port-vip case """
327 "lb vip 2001::/16 protocol udp port 20000 encap gre4")
328 for asid in self.ass:
330 "lb as 2001::/16 protocol udp port 20000 10.0.0.%u"
333 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
334 self.pg_enable_capture(self.pg_interfaces)
337 self.checkCapture(encap='gre4', isv4=False)
339 for asid in self.ass:
341 "lb as 2001::/16 protocol udp port 20000 10.0.0.%u del"
344 "lb vip 2001::/16 protocol udp port 20000 encap gre4 del")
345 self.vapi.cli("test lb flowtable flush")
347 def test_lb_ip4_gre6_port(self):
348 """ Load Balancer IP4 GRE6 on per-port-vip case """
351 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6")
352 for asid in self.ass:
354 "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u"
357 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
358 self.pg_enable_capture(self.pg_interfaces)
361 self.checkCapture(encap='gre6', isv4=True)
363 for asid in self.ass:
365 "lb as 90.0.0.0/8 protocol udp port 20000 2002::%u del"
368 "lb vip 90.0.0.0/8 protocol udp port 20000 encap gre6 del")
369 self.vapi.cli("test lb flowtable flush")
371 def test_lb_ip6_gre6_port(self):
372 """ Load Balancer IP6 GRE6 on per-port-vip case """
375 "lb vip 2001::/16 protocol udp port 20000 encap gre6")
376 for asid in self.ass:
378 "lb as 2001::/16 protocol udp port 20000 2002::%u"
381 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
382 self.pg_enable_capture(self.pg_interfaces)
385 self.checkCapture(encap='gre6', isv4=False)
387 for asid in self.ass:
389 "lb as 2001::/16 protocol udp port 20000 2002::%u del"
392 "lb vip 2001::/16 protocol udp port 20000 encap gre6 del")
393 self.vapi.cli("test lb flowtable flush")
395 def test_lb_ip4_l3dsr(self):
396 """ Load Balancer IP4 L3DSR on vip case """
399 "lb vip 90.0.0.0/8 encap l3dsr dscp 7")
400 for asid in self.ass:
402 "lb as 90.0.0.0/8 10.0.0.%u"
405 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
406 self.pg_enable_capture(self.pg_interfaces)
408 self.checkCapture(encap='l3dsr', isv4=True)
411 for asid in self.ass:
413 "lb as 90.0.0.0/8 10.0.0.%u del"
416 "lb vip 90.0.0.0/8 encap l3dsr"
418 self.vapi.cli("test lb flowtable flush")
420 def test_lb_ip4_l3dsr_port(self):
421 """ Load Balancer IP4 L3DSR on per-port-vip case """
424 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr dscp 7")
425 for asid in self.ass:
427 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
430 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
431 self.pg_enable_capture(self.pg_interfaces)
433 self.checkCapture(encap='l3dsr', isv4=True)
436 for asid in self.ass:
438 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
441 "lb vip 90.0.0.0/8 protocol udp port 20000 encap l3dsr"
443 self.vapi.cli("test lb flowtable flush")
445 def test_lb_ip4_nat4_port(self):
446 """ Load Balancer IP4 NAT4 on per-port-vip case """
449 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
450 " type clusterip target_port 3307")
451 for asid in self.ass:
453 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u"
456 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=True))
457 self.pg_enable_capture(self.pg_interfaces)
459 self.checkCapture(encap='nat4', isv4=True)
462 for asid in self.ass:
464 "lb as 90.0.0.0/8 protocol udp port 20000 10.0.0.%u del"
467 "lb vip 90.0.0.0/8 protocol udp port 20000 encap nat4"
468 " type clusterip target_port 3307 del")
469 self.vapi.cli("test lb flowtable flush")
471 def test_lb_ip6_nat6_port(self):
472 """ Load Balancer IP6 NAT6 on per-port-vip case """
475 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
476 " type clusterip target_port 3307")
477 for asid in self.ass:
479 "lb as 2001::/16 protocol udp port 20000 2002::%u"
482 self.pg0.add_stream(self.generatePackets(self.pg0, isv4=False))
483 self.pg_enable_capture(self.pg_interfaces)
485 self.checkCapture(encap='nat6', isv4=False)
488 for asid in self.ass:
490 "lb as 2001::/16 protocol udp port 20000 2002::%u del"
493 "lb vip 2001::/16 protocol udp port 20000 encap nat6"
494 " type clusterip target_port 3307 del")
495 self.vapi.cli("test lb flowtable flush")