diff --git a/interface-definitions/include/version/qos-version.xml.i b/interface-definitions/include/version/qos-version.xml.i
index c67e61e915..127f771a9f 100644
--- a/interface-definitions/include/version/qos-version.xml.i
+++ b/interface-definitions/include/version/qos-version.xml.i
@@ -1,3 +1,3 @@
-
+
diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in
index 907fd5e4ce..c6ecb742e3 100644
--- a/interface-definitions/qos.xml.in
+++ b/interface-definitions/qos.xml.in
@@ -85,78 +85,67 @@
#include
#include
-
+
Flow isolation settings
+
+ blind src-host dst-host host flow dual-src-host dual-dst-host triple-isolate
+
+
+ blind
+ Disables flow isolation, all traffic passes through a single queue
+
+
+ src-host
+ Flows are defined only by source address
+
+
+ dst-host
+ Flows are defined only by destination address
+
+
+ host
+ Flows are defined by source-destination host pairs
+
+
+ flow
+ Flows are defined by the entire 5-tuple
+
+
+ dual-src-host
+ Flows are defined by the 5-tuple, fairness is applied first over source addresses, then over individual flows
+
+
+ dual-dst-host
+ Flows are defined by the 5-tuple, fairness is applied first over destination addresses, then over individual flows
+
+
+ triple-isolate
+ Flows are defined by the 5-tuple, fairness is applied over source and destination addresses and also over individual flows (default)
+
+
+ (blind|src-host|dst-host|host|flow|dual-src-host|dual-dst-host|triple-isolate)
+
-
-
-
- Disables flow isolation, all traffic passes through a single queue
-
-
-
-
-
- Flows are defined only by source address
-
-
-
-
-
- Flows are defined only by destination address
-
-
-
-
-
- Flows are defined by source-destination host pairs
-
-
-
-
-
- Flows are defined by the entire 5-tuple
-
-
-
-
-
- Flows are defined by the 5-tuple, fairness is applied first over source addresses, then over individual flows
-
-
-
-
-
- Flows are defined by the 5-tuple, fairness is applied first over destination addresses, then over individual flows
-
-
-
-
-
- Flows are defined by the 5-tuple, fairness is applied over source and destination addresses and also over individual flows (default)
-
-
-
-
-
- Perform NAT lookup before applying flow-isolation rules
-
-
-
-
-
+ triple-isolate
+
+
+
+ Perform NAT lookup before applying flow-isolation rules
+
+
+
Round-Trip-Time for Active Queue Management (AQM)
- u32:1-3600000
+ u32:1-1000000000
RTT in ms
-
+
- RTT must be in range 1 to 3600000 milli-seconds
+ RTT must be in range 1 to 1000000000 milli-seconds
100
diff --git a/python/vyos/qos/cake.py b/python/vyos/qos/cake.py
index 1ee7d0fc3c..ca5a269174 100644
--- a/python/vyos/qos/cake.py
+++ b/python/vyos/qos/cake.py
@@ -15,10 +15,25 @@
from vyos.qos.base import QoSBase
+
class CAKE(QoSBase):
+ """
+ https://man7.org/linux/man-pages/man8/tc-cake.8.html
+ """
+
_direction = ['egress']
- # https://man7.org/linux/man-pages/man8/tc-cake.8.html
+ flow_isolation_map = {
+ 'blind': 'flowblind',
+ 'src-host': 'srchost',
+ 'dst-host': 'dsthost',
+ 'dual-dst-host': 'dual-dsthost',
+ 'dual-src-host': 'dual-srchost',
+ 'triple-isolate': 'triple-isolate',
+ 'flow': 'flows',
+ 'host': 'hosts',
+ }
+
def update(self, config, direction):
tmp = f'tc qdisc add dev {self._interface} root handle 1: cake {direction}'
if 'bandwidth' in config:
@@ -30,26 +45,16 @@ def update(self, config, direction):
tmp += f' rtt {rtt}ms'
if 'flow_isolation' in config:
- if 'blind' in config['flow_isolation']:
- tmp += f' flowblind'
- if 'dst_host' in config['flow_isolation']:
- tmp += f' dsthost'
- if 'dual_dst_host' in config['flow_isolation']:
- tmp += f' dual-dsthost'
- if 'dual_src_host' in config['flow_isolation']:
- tmp += f' dual-srchost'
- if 'triple_isolate' in config['flow_isolation']:
- tmp += f' triple-isolate'
- if 'flow' in config['flow_isolation']:
- tmp += f' flows'
- if 'host' in config['flow_isolation']:
- tmp += f' hosts'
- if 'nat' in config['flow_isolation']:
- tmp += f' nat'
- if 'src_host' in config['flow_isolation']:
- tmp += f' srchost '
- else:
- tmp += f' nonat'
+ isolation_value = self.flow_isolation_map.get(config['flow_isolation'])
+
+ if isolation_value is not None:
+ tmp += f' {isolation_value}'
+ else:
+ raise ValueError(
+ f'Invalid flow isolation parameter: {config["flow_isolation"]}'
+ )
+
+ tmp += ' nat' if 'flow_isolation_nat' in config else ' nonat'
self._cmd(tmp)
diff --git a/smoketest/scripts/cli/test_qos.py b/smoketest/scripts/cli/test_qos.py
index aaeebcdae2..9c3e848cdb 100755
--- a/smoketest/scripts/cli/test_qos.py
+++ b/smoketest/scripts/cli/test_qos.py
@@ -22,6 +22,7 @@
from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
+from vyos.qos import CAKE
from vyos.utils.process import cmd
base_path = ['qos']
@@ -871,6 +872,68 @@ def test_16_wrong_traffic_match_group(self):
self.cli_set(['qos', 'traffic-match-group', '3', 'match-group', 'unexpected'])
self.cli_commit()
+ def test_17_cake_updates(self):
+ bandwidth = 1000000
+ rtt = 200
+ interface = self._interfaces[0]
+ policy_name = f'qos-policy-{interface}'
+
+ self.cli_set(base_path + ['interface', interface, 'egress', policy_name])
+ self.cli_set(
+ base_path + ['policy', 'cake', policy_name, 'bandwidth', str(bandwidth)]
+ )
+ self.cli_set(base_path + ['policy', 'cake', policy_name, 'rtt', str(rtt)])
+
+ # commit changes
+ self.cli_commit()
+
+ tmp = get_tc_qdisc_json(interface)
+
+ self.assertEqual('cake', tmp['kind'])
+ # TC store rates as a 32-bit unsigned integer in bps (Bytes per second)
+ self.assertEqual(int(bandwidth * 125), tmp['options']['bandwidth'])
+ # RTT internally is in us
+ self.assertEqual(int(rtt * 1000), tmp['options']['rtt'])
+ self.assertEqual('triple-isolate', tmp['options']['flowmode'])
+ self.assertFalse(tmp['options']['ingress'])
+ self.assertFalse(tmp['options']['nat'])
+ self.assertTrue(tmp['options']['raw'])
+
+ nat = True
+ for flow_isolation in [
+ 'blind',
+ 'src-host',
+ 'dst-host',
+ 'dual-dst-host',
+ 'dual-src-host',
+ 'triple-isolate',
+ 'flow',
+ 'host',
+ ]:
+ self.cli_set(
+ base_path
+ + ['policy', 'cake', policy_name, 'flow-isolation', flow_isolation]
+ )
+
+ if nat:
+ self.cli_set(
+ base_path + ['policy', 'cake', policy_name, 'flow-isolation-nat']
+ )
+ else:
+ self.cli_delete(
+ base_path + ['policy', 'cake', policy_name, 'flow-isolation-nat']
+ )
+
+ self.cli_commit()
+
+ tmp = get_tc_qdisc_json(interface)
+ self.assertEqual(
+ CAKE.flow_isolation_map.get(flow_isolation), tmp['options']['flowmode']
+ )
+
+ self.assertEqual(nat, tmp['options']['nat'])
+ nat = not nat
+
def test_20_round_robin_policy_default(self):
interface = self._interfaces[0]
policy_name = f'qos-policy-{interface}'
diff --git a/src/migration-scripts/qos/2-to-3 b/src/migration-scripts/qos/2-to-3
new file mode 100644
index 0000000000..284fe828e6
--- /dev/null
+++ b/src/migration-scripts/qos/2-to-3
@@ -0,0 +1,34 @@
+# Copyright 2024 VyOS maintainers and contributors
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see .
+
+from vyos.configtree import ConfigTree
+
+
+def migrate(config: ConfigTree) -> None:
+ base = ['qos', 'policy', 'cake']
+ if config.exists(base):
+ for policy in config.list_nodes(base):
+ if config.exists(base + [policy, 'flow-isolation']):
+ isolation = None
+ for isol in config.list_nodes(base + [policy, 'flow-isolation']):
+ if isol == 'nat':
+ config.set(base + [policy, 'flow-isolation-nat'])
+ else:
+ isolation = isol
+
+ config.delete(base + [policy, 'flow-isolation'])
+
+ if isolation:
+ config.set(base + [policy, 'flow-isolation'], value=isolation)