diff --git a/autotest/units/001_one_port/080_balancer_pure_round_robin/001-expect.pcap b/autotest/units/001_one_port/080_balancer_pure_round_robin/001-expect.pcap new file mode 100644 index 00000000..61f77c51 Binary files /dev/null and b/autotest/units/001_one_port/080_balancer_pure_round_robin/001-expect.pcap differ diff --git a/autotest/units/001_one_port/080_balancer_pure_round_robin/001-send.pcap b/autotest/units/001_one_port/080_balancer_pure_round_robin/001-send.pcap new file mode 100644 index 00000000..893fb53e Binary files /dev/null and b/autotest/units/001_one_port/080_balancer_pure_round_robin/001-send.pcap differ diff --git a/autotest/units/001_one_port/080_balancer_pure_round_robin/autotest.yaml b/autotest/units/001_one_port/080_balancer_pure_round_robin/autotest.yaml new file mode 100644 index 00000000..429015cb --- /dev/null +++ b/autotest/units/001_one_port/080_balancer_pure_round_robin/autotest.yaml @@ -0,0 +1,14 @@ +steps: +- ipv4Update: "0.0.0.0/0 -> 200.0.0.1" +- ipv6Update: "::/0 -> fe80::1" +- cli: + - balancer real enable balancer0 10.0.0.3 udp 80 2000::1 80 + - balancer real enable balancer0 10.0.0.3 udp 80 2000::2 80 + - balancer real enable balancer0 10.0.0.3 udp 80 2000::3 80 + - balancer real enable balancer0 10.0.0.3 udp 81 2000::1 81 + - balancer real enable balancer0 10.0.0.3 udp 81 2000::2 81 + - balancer real flush +- sendPackets: + - port: kni0 + send: 001-send.pcap + expect: 001-expect.pcap diff --git a/autotest/units/001_one_port/080_balancer_pure_round_robin/controlplane.conf b/autotest/units/001_one_port/080_balancer_pure_round_robin/controlplane.conf new file mode 100644 index 00000000..40f772fa --- /dev/null +++ b/autotest/units/001_one_port/080_balancer_pure_round_robin/controlplane.conf @@ -0,0 +1,46 @@ +{ + "modules": { + "lp0.100": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "100", + "macAddress": "00:11:22:33:44:55", + "nextModule": "acl0" + }, + "lp0.200": { + "type": "logicalPort", + "physicalPort": "kni0", + "vlanId": "200", + "macAddress": "00:11:22:33:44:55", + "nextModule": "acl0" + }, + "acl0": { + "type": "acl", + "nextModules": [ + "balancer0", + "route0" + ] + }, + "balancer0": { + "type": "balancer", + "source": "2000:51b::1", + "services": "services.conf", + "nextModule": "route0" + }, + "route0": { + "type": "route", + "interfaces": { + "kni0.100": { + "neighborIPv6Address": "fe80::1", + "neighborMacAddress": "00:00:00:00:00:01", + "nextModule": "lp0.100" + }, + "kni0.200": { + "neighborIPv4Address": "200.0.0.1", + "neighborMacAddress": "00:00:00:00:00:02", + "nextModule": "lp0.200" + } + } + } + } +} diff --git a/autotest/units/001_one_port/080_balancer_pure_round_robin/gen.py b/autotest/units/001_one_port/080_balancer_pure_round_robin/gen.py new file mode 100755 index 00000000..f517b6d5 --- /dev/null +++ b/autotest/units/001_one_port/080_balancer_pure_round_robin/gen.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from scapy.all import * + + +def write_pcap(filename, *packetsList): + if len(packetsList) == 0: + PcapWriter(filename)._write_header(Ether()) + return + + PcapWriter(filename) + + for packets in packetsList: + if type(packets) == list: + for packet in packets: + packet.time = 0 + wrpcap(filename, [p for p in packet], append=True) + else: + packets.time = 0 + wrpcap(filename, [p for p in packets], append=True) + + +write_pcap("001-send.pcap", + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=81, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=81, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=81, sport=12380), + Ether(dst="00:11:22:33:44:55", src="00:00:00:00:00:02")/Dot1Q(vlan=200)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=81, sport=12380)) + +write_pcap("001-expect.pcap", + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::2", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::3", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::1", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::2", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::3", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::1", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::2", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::3", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=80, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::1", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=81, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::1", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=81, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::1", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=81, sport=12380), + Ether(dst="00:00:00:00:00:01", src="00:11:22:33:44:55")/Dot1Q(vlan=100)/IPv6(dst="2000::1", src="2000:51b::0101:0001:0:1", hlim=63, fl=0)/IP(dst="10.0.0.3", src="1.1.0.1", ttl=64)/UDP(dport=81, sport=12380)) diff --git a/autotest/units/001_one_port/080_balancer_pure_round_robin/services.conf b/autotest/units/001_one_port/080_balancer_pure_round_robin/services.conf new file mode 100644 index 00000000..f021d84a --- /dev/null +++ b/autotest/units/001_one_port/080_balancer_pure_round_robin/services.conf @@ -0,0 +1,45 @@ +[ + { + "vip": "10.0.0.3", + "proto": "udp", + "vport": "80", + "scheduler": "purr", + "ops": true, + "reals": [ + { + "ip": "2000::1", + "port": "80", + "weight": "1" + }, + { + "ip": "2000::2", + "port": "80", + "weight": "1" + }, + { + "ip": "2000::3", + "port": "80", + "weight": "1" + } + ] + }, + { + "vip": "10.0.0.3", + "proto": "udp", + "vport": "81", + "scheduler": "wrr", + "ops": true, + "reals": [ + { + "ip": "2000::1", + "port": "81", + "weight": "1" + }, + { + "ip": "2000::2", + "port": "81", + "weight": "1" + } + ] + } +] diff --git a/common/define.h b/common/define.h index 47cc5fd0..b090da18 100644 --- a/common/define.h +++ b/common/define.h @@ -101,6 +101,7 @@ extern LogPriority logPriority; #define YANET_BALANCER_OPS_FLAG ((uint8_t)(1u << 1)) #define YANET_BALANCER_PURE_L3 ((uint8_t)(1u << 2)) +#define YANET_BALANCER_PURE_ROUND_ROBIN ((uint8_t)(1u << 3)) #define CALCULATE_LOGICALPORT_ID(portId, vlanId) ((portId << 13) | ((vlanId & 0xFFF) << 1) | 1) diff --git a/controlplane/configparser.cpp b/controlplane/configparser.cpp index 92054e4c..3b27e14f 100644 --- a/controlplane/configparser.cpp +++ b/controlplane/configparser.cpp @@ -1651,6 +1651,7 @@ void config_parser_t::loadConfig_balancer_services(controlplane::base_t& baseNex balancer::scheduler scheduler{}; balancer::scheduler_params scheduler_params{}; + uint8_t flags = 0; if (scheduler_string == "rr") { scheduler = balancer::scheduler::rr; @@ -1667,6 +1668,11 @@ void config_parser_t::loadConfig_balancer_services(controlplane::base_t& baseNex scheduler_params.wlc_power = std::stoll(service_json["scheduler_params"]["wlc_power"].get(), nullptr, 10); } } + else if (scheduler_string == "purr") + { + scheduler = balancer::scheduler::wrr; + flags |= YANET_BALANCER_PURE_ROUND_ROBIN; + } else { throw error_result_t(eResult::invalidConfigurationFile, "unknown scheduler: " + scheduler_string); @@ -1725,7 +1731,6 @@ void config_parser_t::loadConfig_balancer_services(controlplane::base_t& baseNex throw error_result_t(eResult::invalidConfigurationFile, "unknown proto"); } - uint8_t flags = 0; if (service_json.value("mss_fix", false) == true) { flags |= YANET_BALANCER_FIX_MSS_FLAG; diff --git a/dataplane/worker.cpp b/dataplane/worker.cpp index 7a1ad8e5..bb64c1c2 100644 --- a/dataplane/worker.cpp +++ b/dataplane/worker.cpp @@ -47,6 +47,7 @@ cWorker::cWorker(cDataPlane* dataPlane) : ring_lowPriority(nullptr), ring_toFreePackets(nullptr), ring_log(nullptr), + roundRobinCounter(0), packetsToSWNPRemainder(dataPlane->config.SWNormalPriorityRateLimitPerWorker) { } @@ -4117,7 +4118,8 @@ inline void cWorker::balancer_handle() continue; } - const auto& real_id = ring->reals[range->start + (metadata->hash % range->size)]; + const auto& shift = service.flags & YANET_BALANCER_PURE_ROUND_ROBIN ? ++roundRobinCounter : metadata->hash; + const auto& real_id = ring->reals[range->start + (shift % range->size)]; const auto& real_unordered = base.globalBase->balancer_reals[real_id]; if (!value) { diff --git a/dataplane/worker.h b/dataplane/worker.h index 1ed5774f..9d42377a 100644 --- a/dataplane/worker.h +++ b/dataplane/worker.h @@ -354,6 +354,7 @@ class cWorker uint64_t* bursts; // CONFIG_YADECAP_MBUFS_BURST_SIZE + 1 uint64_t* counters; // YANET_CONFIG_COUNTERS_SIZE uint64_t* aclCounters; // YANET_CONFIG_ACL_COUNTERS_SIZE + uint64_t roundRobinCounter; // will decrease with each new packet sent to slow worker, replenishes each N mseconds int32_t packetsToSWNPRemainder;