Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

T6796: QoS: match filter by interface(iif) #4188

Open
wants to merge 1 commit into
base: current
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -251,7 +252,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 @@ -261,9 +262,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.utils.process import cmd

base_path = ['qos']
Expand Down Expand Up @@ -922,6 +922,30 @@ def test_20_round_robin_policy_default(self):
tmp[2]['options'],
)

def test_22_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
Loading