Skip to content

Commit

Permalink
T6796: QoS: match filter by interface(iif) (#4188)
Browse files Browse the repository at this point in the history
  • Loading branch information
HollyGurza authored Nov 21, 2024
1 parent 1a291b4 commit b51cf40
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 3 deletions.
15 changes: 15 additions & 0 deletions python/vyos/ifconfig/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ class Interface(Control):
'shellcmd': 'ip -json -detail link list dev {ifname}',
'format': lambda j: jmespath.search('[*].ifalias | [0]', json.loads(j)) or '',
},
'ifindex': {
'shellcmd': 'ip -json -detail link list dev {ifname}',
'format': lambda j: jmespath.search('[*].ifindex | [0]', json.loads(j)) or '',
},
'mac': {
'shellcmd': 'ip -json -detail link list dev {ifname}',
'format': lambda j: jmespath.search('[*].address | [0]', json.loads(j)),
Expand Down Expand Up @@ -428,6 +432,17 @@ def _add_interface_to_ct_iface_map(self, vrf_table_id: int):
nft_command = f'add element inet vrf_zones ct_iface_map {{ "{self.ifname}" : {vrf_table_id} }}'
self._nft_check_and_run(nft_command)

def get_ifindex(self):
"""
Get interface index by name
Example:
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').get_ifindex()
'2'
"""
return int(self.get_interface('ifindex'))

def get_min_mtu(self):
"""
Get hardware minimum supported MTU
Expand Down
8 changes: 7 additions & 1 deletion python/vyos/qos/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import jmespath

from vyos.base import Warning
from vyos.ifconfig import Interface
from vyos.utils.process import cmd
from vyos.utils.dict import dict_search
from vyos.utils.file import read_file
Expand Down Expand Up @@ -253,7 +254,7 @@ def update(self, config, direction, priority=None):
for index, (match, match_config) in enumerate(cls_config['match'].items(), start=1):
filter_cmd = filter_cmd_base
if not has_filter:
for key in ['mark', 'vif', 'ip', 'ipv6']:
for key in ['mark', 'vif', 'ip', 'ipv6', 'interface']:
if key in match_config:
has_filter = True
break
Expand All @@ -263,9 +264,14 @@ def update(self, config, direction, priority=None):
if 'mark' in match_config:
mark = match_config['mark']
filter_cmd += f' handle {mark} fw'

if 'vif' in match_config:
vif = match_config['vif']
filter_cmd += f' basic match "meta(vlan mask 0xfff eq {vif})"'
elif 'interface' in match_config:
iif_name = match_config['interface']
iif = Interface(iif_name).get_ifindex()
filter_cmd += f' basic match "meta(rt_iif eq {iif})"'

for af in ['ip', 'ipv6']:
tc_af = af
Expand Down
26 changes: 25 additions & 1 deletion smoketest/scripts/cli/test_qos.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from base_vyostest_shim import VyOSUnitTestSHIM

from vyos.configsession import ConfigSessionError
from vyos.ifconfig import Section
from vyos.ifconfig import Section, Interface
from vyos.qos import CAKE
from vyos.utils.process import cmd

Expand Down Expand Up @@ -1006,6 +1006,30 @@ def test_22_rate_control_default(self):
# TC store rates as a 32-bit unsigned integer in bps (Bytes per second)
self.assertEqual(int(bandwidth * 125), tmp['options']['rate'])

def test_23_policy_limiter_iif_filter(self):
policy_name = 'smoke_test'
base_policy_path = ['qos', 'policy', 'limiter', policy_name]

self.cli_set(['qos', 'interface', self._interfaces[0], 'ingress', policy_name])
self.cli_set(base_policy_path + ['class', '100', 'bandwidth', '20gbit'])
self.cli_set(base_policy_path + ['class', '100', 'burst', '3760k'])
self.cli_set(base_policy_path + ['class', '100', 'match', 'test', 'interface', self._interfaces[0]])
self.cli_set(base_policy_path + ['class', '100', 'priority', '20'])
self.cli_set(base_policy_path + ['default', 'bandwidth', '1gbit'])
self.cli_set(base_policy_path + ['default', 'burst', '125000000b'])
self.cli_commit()

iif = Interface(self._interfaces[0]).get_ifindex()
tc_filters = cmd(f'tc filter show dev {self._interfaces[0]} ingress')

# class 100
self.assertIn('filter parent ffff: protocol all pref 20 basic chain 0', tc_filters)
self.assertIn(f'meta(rt_iif eq {iif})', tc_filters)
self.assertIn('action order 1: police 0x1 rate 20Gbit burst 3847500b mtu 2Kb action drop overhead 0b', tc_filters)
# default
self.assertIn('filter parent ffff: protocol all pref 255 basic chain 0', tc_filters)
self.assertIn('action order 1: police 0x2 rate 1Gbit burst 125000000b mtu 2Kb action drop overhead 0b', tc_filters)


if __name__ == '__main__':
unittest.main(verbosity=2)
8 changes: 7 additions & 1 deletion src/conf_mode/qos.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,16 @@ def get_config(config=None):
def _verify_match(cls_config: dict) -> None:
if 'match' in cls_config:
for match, match_config in cls_config['match'].items():
if {'ip', 'ipv6'} <= set(match_config):
filters = set(match_config)
if {'ip', 'ipv6'} <= filters:
raise ConfigError(
f'Can not use both IPv6 and IPv4 in one match ({match})!')

if {'interface', 'vif'} & filters:
if {'ip', 'ipv6', 'ether'} & filters:
raise ConfigError(
f'Can not combine protocol and interface or vlan tag match ({match})!')


def _verify_match_group_exist(cls_config, qos):
if 'match_group' in cls_config:
Expand Down

0 comments on commit b51cf40

Please sign in to comment.