summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--plugins/lb-plugin/lb/lb.c4
-rw-r--r--plugins/lb-plugin/lb/node.c12
-rw-r--r--test/test_lb.py210
3 files changed, 223 insertions, 3 deletions
diff --git a/plugins/lb-plugin/lb/lb.c b/plugins/lb-plugin/lb/lb.c
index 6af4697e37c..69bd660aba3 100644
--- a/plugins/lb-plugin/lb/lb.c
+++ b/plugins/lb-plugin/lb/lb.c
@@ -118,7 +118,9 @@ u8 *format_lb_vip (u8 * s, va_list * args)
u8 *format_lb_as (u8 * s, va_list * args)
{
lb_as_t *as = va_arg (*args, lb_as_t *);
- return format(s, "%U %s", format_ip46_address, &as->address, (as->flags & LB_AS_FLAGS_USED)?"used":"removed");
+ return format(s, "%U %s", format_ip46_address,
+ &as->address, IP46_TYPE_ANY,
+ (as->flags & LB_AS_FLAGS_USED)?"used":"removed");
}
u8 *format_lb_vip_detailed (u8 * s, va_list * args)
diff --git a/plugins/lb-plugin/lb/node.c b/plugins/lb-plugin/lb/node.c
index 77beaac9bb2..82f0cb529aa 100644
--- a/plugins/lb-plugin/lb/node.c
+++ b/plugins/lb-plugin/lb/node.c
@@ -48,8 +48,16 @@ format_lb_trace (u8 * s, va_list * args)
CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
lb_trace_t *t = va_arg (*args, lb_trace_t *);
- s = format(s, "lb vip[%d]: %U\n", t->vip_index, format_lb_vip, &lbm->vips[t->vip_index]);
- s = format(s, "lb as[%d]: %U\n", t->as_index, format_lb_as, &lbm->ass[t->as_index]);
+ if (pool_is_free_index(lbm->vips, t->vip_index)) {
+ s = format(s, "lb vip[%d]: This VIP was freed since capture\n");
+ } else {
+ s = format(s, "lb vip[%d]: %U\n", t->vip_index, format_lb_vip, &lbm->vips[t->vip_index]);
+ }
+ if (pool_is_free_index(lbm->ass, t->as_index)) {
+ s = format(s, "lb as[%d]: This AS was freed since capture\n");
+ } else {
+ s = format(s, "lb as[%d]: %U\n", t->as_index, format_lb_as, &lbm->ass[t->as_index]);
+ }
return s;
}
diff --git a/test/test_lb.py b/test/test_lb.py
new file mode 100644
index 00000000000..eb308195766
--- /dev/null
+++ b/test/test_lb.py
@@ -0,0 +1,210 @@
+import unittest
+import time
+import socket
+from framework import VppTestCase, VppTestRunner
+from util import Util
+
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether, GRE
+from scapy.layers.inet import IP, UDP
+from scapy.layers.inet6 import IPv6
+
+## TestLB is a subclass of Util and VPPTestCase classes.
+#
+# TestLB class defines Load Balancer test cases for:
+# - IP4 to GRE4 encap
+# - IP4 to GRE6 encap
+# - IP6 to GRE4 encap
+# - IP6 to GRE6 encap
+#
+# As stated in comments below, GRE has issues with IPv6.
+# All test cases involving IPv6 are executed, but
+# received packets are not parsed and checked.
+#
+class TestLB(Util, VppTestCase):
+ """ Load Balancer Test Case """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestLB, cls).setUpClass()
+
+ cls.ass = range(5)
+ cls.packets = range(100)
+
+ try:
+ cls.create_interfaces([0,1])
+ cls.api("sw_interface_dump")
+ cls.config_ip4([0,1])
+ cls.config_ip6([0,1])
+ cls.resolve_arp([0,1])
+ cls.resolve_icmpv6_nd([0,1])
+ cls.cli(0, "ip route add 10.0.0.0/24 via %s pg1" % (cls.MY_IP4S[1]))
+ cls.cli(0, "ip route add 2002::/16 via %s pg1" % (cls.MY_IP6S[1]))
+ cls.cli(0, "lb conf buckets-log2 20 ip4-src-address 39.40.41.42 ip6-src-address fd00:f00d::1")
+
+ except Exception as e:
+ super(TestLB, cls).tearDownClass()
+ raise
+
+ def tearDown(self):
+ self.cli(2, "show int")
+ self.cli(2, "show trace")
+ self.cli(2, "show lb vip verbose")
+
+ def getIPv4Flow(self, id):
+ return (IP(dst="90.0.%u.%u" % (id / 255, id % 255),
+ src="40.0.%u.%u" % (id / 255, id % 255)) /
+ UDP(sport=10000 + id, dport=20000 + id))
+
+ def getIPv6Flow(self, id):
+ return (IPv6(dst="2001::%u" % (id), src="fd00:f00d:ffff::%u" % (id)) /
+ UDP(sport=10000 + id, dport=20000 + id))
+
+ def generatePackets(self, isv4):
+ pkts = []
+ for pktid in self.packets:
+ info = self.create_packet_info(0, pktid)
+ payload = self.info_to_payload(info)
+ ip = self.getIPv4Flow(pktid) if isv4 else self.getIPv6Flow(pktid)
+ packet=(Ether(dst=self.VPP_MACS[0], src=self.MY_MACS[0]) /
+ ip / Raw(payload))
+ self.extend_packet(packet, 128)
+ info.data = packet.copy()
+ pkts.append(packet)
+ return pkts
+
+ def checkInner(self, gre, isv4):
+ self.assertEqual(gre.proto, 0x0800 if isv4 else 0x86DD)
+ self.assertEqual(gre.flags, 0)
+ self.assertEqual(gre.version, 0)
+ inner = gre[IP] if isv4 else gre[IPv6]
+ payload_info = self.payload_to_info(str(gre[Raw]))
+ packet_index = payload_info.index
+ self.info = self.get_next_packet_info_for_interface2(0, payload_info.dst, self.info)
+ self.assertEqual(str(inner), str(self.info.data[IP]))
+
+ def checkCapture(self, gre4, isv4):
+ out = self.pg_get_capture(0)
+ self.assertEqual(len(out), 0)
+ out = self.pg_get_capture(1)
+ self.assertEqual(len(out), len(self.packets))
+
+ load = [0] * len(self.ass)
+ self.info = None
+ for p in out:
+ try:
+ asid = 0
+ gre = None
+ if gre4:
+ ip = p[IP]
+ gre = p[GRE]
+ inner = gre[IP] if isv4 else gre[IPv6]
+ asid = int(ip.dst.split(".")[3])
+ self.assertEqual(ip.version, 4)
+ self.assertEqual(ip.flags, 0)
+ self.assertEqual(ip.src, "39.40.41.42")
+ self.assertEqual(ip.dst, "10.0.0.%u" % asid)
+ self.assertEqual(ip.proto, 47)
+ self.assertEqual(len(ip.options), 0)
+ self.assertTrue(ip.ttl >= 64)
+ else:
+ ip = p[IPv6]
+ gre = p[GRE]
+ inner = gre[IP] if isv4 else gre[IPv6]
+ asid = ip.dst.split(":")
+ asid = asid[len(asid) - 1]
+ asid = 0 if asid=="" else int(asid)
+ self.assertEqual(ip.version, 6)
+ # Todo: Given scapy... I will do that when it works.
+ self.checkInner(gre, isv4)
+ load[asid] += 1
+ except:
+ self.log("Unexpected or invalid packet:")
+ p.show()
+ raise
+
+ # This is just to roughly check that the balancing algorithm
+ # is not completly biased.
+ for asid in self.ass:
+ if load[asid] < len(self.packets)/(len(self.ass)*2):
+ self.log("ASS is not balanced: load[%d] = %d" % (asid, load[asid]))
+ raise Exception("Load Balancer algorithm is biased")
+
+
+ def test_lb_ip4_gre4(self):
+ """ Load Balancer IP4 GRE4 """
+
+ return
+ self.cli(0, "lb vip 90.0.0.0/8 encap gre4")
+ for asid in self.ass:
+ self.cli(0, "lb as 90.0.0.0/8 10.0.0.%u" % (asid))
+
+ self.pg_add_stream(0, self.generatePackets(1))
+ self.pg_enable_capture([0,1])
+ self.pg_start()
+ self.checkCapture(1, 1)
+
+ for asid in self.ass:
+ self.cli(0, "lb as 90.0.0.0/8 10.0.0.%u del" % (asid))
+ self.cli(0, "lb vip 90.0.0.0/8 encap gre4 del")
+
+
+ def test_lb_ip6_gre4(self):
+ """ Load Balancer IP6 GRE4 """
+
+ self.cli(0, "lb vip 2001::/16 encap gre4")
+ for asid in self.ass:
+ self.cli(0, "lb as 2001::/16 10.0.0.%u" % (asid))
+
+ self.pg_add_stream(0, self.generatePackets(0))
+ self.pg_enable_capture([0,1])
+ self.pg_start()
+
+ # Scapy fails parsing IPv6 over GRE.
+ # This check is therefore disabled for now.
+ #self.checkCapture(1, 0)
+
+ for asid in self.ass:
+ self.cli(0, "lb as 2001::/16 10.0.0.%u del" % (asid))
+ self.cli(0, "lb vip 2001::/16 encap gre4 del")
+
+
+ def test_lb_ip4_gre6(self):
+ """ Load Balancer IP4 GRE6 """
+
+ self.cli(0, "lb vip 90.0.0.0/8 encap gre6")
+ for asid in self.ass:
+ self.cli(0, "lb as 90.0.0.0/8 2002::%u" % (asid))
+
+ self.pg_add_stream(0, self.generatePackets(1))
+ self.pg_enable_capture([0,1])
+ self.pg_start()
+
+ # Scapy fails parsing GRE over IPv6.
+ # This check is therefore disabled for now.
+ # One can easily patch layers/inet6.py to fix the issue.
+ #self.checkCapture(0, 1)
+
+ for asid in self.ass:
+ self.cli(0, "lb as 90.0.0.0/8 2002::%u" % (asid))
+ self.cli(0, "lb vip 90.0.0.0/8 encap gre6 del")
+
+ def test_lb_ip6_gre6(self):
+ """ Load Balancer IP6 GRE6 """
+
+ self.cli(0, "lb vip 2001::/16 encap gre6")
+ for asid in self.ass:
+ self.cli(0, "lb as 2001::/16 2002::%u" % (asid))
+
+ self.pg_add_stream(0, self.generatePackets(0))
+ self.pg_enable_capture([0,1])
+ self.pg_start()
+
+ # Scapy fails parsing IPv6 over GRE and IPv6 over GRE.
+ # This check is therefore disabled for now.
+ #self.checkCapture(0, 0)
+
+ for asid in self.ass:
+ self.cli(0, "lb as 2001::/16 2002::%u del" % (asid))
+ self.cli(0, "lb vip 2001::/16 encap gre6 del")
+