diff options
Diffstat (limited to 'python/vyos/qos')
-rw-r--r-- | python/vyos/qos/base.py | 169 | ||||
-rw-r--r-- | python/vyos/qos/trafficshaper.py | 12 |
2 files changed, 127 insertions, 54 deletions
diff --git a/python/vyos/qos/base.py b/python/vyos/qos/base.py index 33bb8ae28..26ec65535 100644 --- a/python/vyos/qos/base.py +++ b/python/vyos/qos/base.py @@ -1,4 +1,4 @@ -# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2022-2023 VyOS maintainers and contributors <maintainers@vyos.io> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -20,10 +20,47 @@ from vyos.util import cmd from vyos.util import dict_search from vyos.util import read_file +from vyos.utils.network import get_protocol_by_name + + class QoSBase: _debug = False _direction = ['egress'] _parent = 0xffff + _dsfields = { + "default": 0x0, + "lowdelay": 0x10, + "throughput": 0x08, + "reliability": 0x04, + "mincost": 0x02, + "priority": 0x20, + "immediate": 0x40, + "flash": 0x60, + "flash-override": 0x80, + "critical": 0x0A, + "internet": 0xC0, + "network": 0xE0, + "AF11": 0x28, + "AF12": 0x30, + "AF13": 0x38, + "AF21": 0x48, + "AF22": 0x50, + "AF23": 0x58, + "AF31": 0x68, + "AF32": 0x70, + "AF33": 0x78, + "AF41": 0x88, + "AF42": 0x90, + "AF43": 0x98, + "CS1": 0x20, + "CS2": 0x40, + "CS3": 0x60, + "CS4": 0x80, + "CS5": 0xA0, + "CS6": 0xC0, + "CS7": 0xE0, + "EF": 0xB8 + } def __init__(self, interface): if os.path.exists('/tmp/vyos.qos.debug'): @@ -45,6 +82,12 @@ class QoSBase: return tmp[-1] return None + def _get_dsfield(self, value): + if value in self._dsfields: + return self._dsfields[value] + else: + return value + def _build_base_qdisc(self, config : dict, cls_id : int): """ Add/replace qdisc for every class (also default is a class). This is @@ -197,7 +240,17 @@ class QoSBase: if tmp: filter_cmd += f' match {tc_af} dport {tmp} 0xffff' tmp = dict_search(f'{af}.protocol', match_config) - if tmp: filter_cmd += f' match {tc_af} protocol {tmp} 0xff' + if tmp: + tmp = get_protocol_by_name(tmp) + filter_cmd += f' match {tc_af} protocol {tmp} 0xff' + + tmp = dict_search(f'{af}.dscp', match_config) + if tmp: + tmp = self._get_dsfield(tmp) + if af == 'ip': + filter_cmd += f' match {tc_af} dsfield {tmp} 0xff' + elif af == 'ipv6': + filter_cmd += f' match u16 {tmp} 0x0ff0 at 0' # Will match against total length of an IPv4 packet and # payload length of an IPv6 packet. @@ -243,60 +296,70 @@ class QoSBase: # The police block allows limiting of the byte or packet rate of # traffic matched by the filter it is attached to. # https://man7.org/linux/man-pages/man8/tc-police.8.html - if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in cls_config): - filter_cmd += f' action police' - - if 'exceed' in cls_config: - action = cls_config['exceed'] - filter_cmd += f' conform-exceed {action}' - if 'not_exceed' in cls_config: - action = cls_config['not_exceed'] - filter_cmd += f'/{action}' - if 'bandwidth' in cls_config: - rate = self._rate_convert(cls_config['bandwidth']) - filter_cmd += f' rate {rate}' - - if 'burst' in cls_config: - burst = cls_config['burst'] - filter_cmd += f' burst {burst}' + # T5295: We do not handle rate via tc filter directly, + # but rather set the tc filter to direct traffic to the correct tc class flow. + # + # if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in cls_config): + # filter_cmd += f' action police' + # + # if 'exceed' in cls_config: + # action = cls_config['exceed'] + # filter_cmd += f' conform-exceed {action}' + # if 'not_exceed' in cls_config: + # action = cls_config['not_exceed'] + # filter_cmd += f'/{action}' + # + # if 'bandwidth' in cls_config: + # rate = self._rate_convert(cls_config['bandwidth']) + # filter_cmd += f' rate {rate}' + # + # if 'burst' in cls_config: + # burst = cls_config['burst'] + # filter_cmd += f' burst {burst}' cls = int(cls) filter_cmd += f' flowid {self._parent:x}:{cls:x}' self._cmd(filter_cmd) - if 'default' in config: - if 'class' in config: - class_id_max = self._get_class_max_id(config) - default_cls_id = int(class_id_max) +1 - self._build_base_qdisc(config['default'], default_cls_id) - - filter_cmd = f'tc filter replace dev {self._interface} parent {self._parent:x}: ' - filter_cmd += 'prio 255 protocol all basic' - - # The police block allows limiting of the byte or packet rate of - # traffic matched by the filter it is attached to. - # https://man7.org/linux/man-pages/man8/tc-police.8.html - if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in config['default']): - filter_cmd += f' action police' - - if 'exceed' in config['default']: - action = config['default']['exceed'] - filter_cmd += f' conform-exceed {action}' - if 'not_exceed' in config['default']: - action = config['default']['not_exceed'] - filter_cmd += f'/{action}' - - if 'bandwidth' in config['default']: - rate = self._rate_convert(config['default']['bandwidth']) - filter_cmd += f' rate {rate}' - - if 'burst' in config['default']: - burst = config['default']['burst'] - filter_cmd += f' burst {burst}' - - if 'class' in config: - filter_cmd += f' flowid {self._parent:x}:{default_cls_id:x}' - - self._cmd(filter_cmd) - + # T5295: Do not do any tc filter action for 'default' + # In VyOS 1.4, we have the following configuration: + # tc filter replace dev eth0 parent 1: prio 255 protocol all basic action police rate 300000000 burst 15k + # However, this caused unexpected random speeds. + # In VyOS 1.3, we do not use any 'tc filter' for rate limits, + # It gets rate from tc class classid 1:1 + # + # if 'default' in config: + # if 'class' in config: + # class_id_max = self._get_class_max_id(config) + # default_cls_id = int(class_id_max) +1 + # self._build_base_qdisc(config['default'], default_cls_id) + # + # filter_cmd = f'tc filter replace dev {self._interface} parent {self._parent:x}: ' + # filter_cmd += 'prio 255 protocol all basic' + # + # # The police block allows limiting of the byte or packet rate of + # # traffic matched by the filter it is attached to. + # # https://man7.org/linux/man-pages/man8/tc-police.8.html + # if any(tmp in ['exceed', 'bandwidth', 'burst'] for tmp in config['default']): + # filter_cmd += f' action police' + # + # if 'exceed' in config['default']: + # action = config['default']['exceed'] + # filter_cmd += f' conform-exceed {action}' + # if 'not_exceed' in config['default']: + # action = config['default']['not_exceed'] + # filter_cmd += f'/{action}' + # + # if 'bandwidth' in config['default']: + # rate = self._rate_convert(config['default']['bandwidth']) + # filter_cmd += f' rate {rate}' + # + # if 'burst' in config['default']: + # burst = config['default']['burst'] + # filter_cmd += f' burst {burst}' + # + # if 'class' in config: + # filter_cmd += f' flowid {self._parent:x}:{default_cls_id:x}' + # + # self._cmd(filter_cmd) diff --git a/python/vyos/qos/trafficshaper.py b/python/vyos/qos/trafficshaper.py index f42f4d022..573283833 100644 --- a/python/vyos/qos/trafficshaper.py +++ b/python/vyos/qos/trafficshaper.py @@ -70,7 +70,17 @@ class TrafficShaper(QoSBase): cls = int(cls) # bandwidth is a mandatory CLI node - rate = self._rate_convert(cls_config['bandwidth']) + # T5296 if bandwidth 'auto' or 'xx%' get value from config shaper total "bandwidth" + # i.e from set shaper test bandwidth '300mbit' + # without it, it tries to get value from qos.base /sys/class/net/{self._interface}/speed + if cls_config['bandwidth'] == 'auto': + rate = self._rate_convert(config['bandwidth']) + elif cls_config['bandwidth'].endswith('%'): + percent = cls_config['bandwidth'].rstrip('%') + rate = self._rate_convert(config['bandwidth']) * int(percent) // 100 + else: + rate = self._rate_convert(cls_config['bandwidth']) + burst = cls_config['burst'] quantum = cls_config['codel_quantum'] |