mirror of
https://github.com/torvalds/linux.git
synced 2026-09-28 12:02:03 +02:00
selftests: net: nl_nlctrl: check the op ids in the policy map
Validate that the op map in the policy dump is correct. Signed-off-by: Jakub Kicinski <kuba@kernel.org> Link: https://patch.msgid.link/20260918222949.4190284-2-kuba@kernel.org Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
261e8a37ec
commit
a87529034b
|
|
@ -9,40 +9,86 @@ from lib.py import ksft_run, ksft_exit
|
|||
from lib.py import ksft_eq, ksft_ge, ksft_true, ksft_in, ksft_not_in
|
||||
from lib.py import NetdevFamily, EthtoolFamily, NlctrlFamily
|
||||
|
||||
# Families we can expect to always be around, and which between them
|
||||
# cover ops with a do, with a dump, and with both.
|
||||
FAMILIES = ('nlctrl', 'netdev')
|
||||
|
||||
def getfamily_do(ctrl) -> None:
|
||||
"""Query a single family by name and validate its ops."""
|
||||
fam = ctrl.getfamily({'family-name': 'netdev'})
|
||||
ksft_eq(fam['family-name'], 'netdev')
|
||||
|
||||
def _get_ops(ctrl, name):
|
||||
"""Get the ops of a family, keyed by command id."""
|
||||
fam = ctrl.getfamily({'family-name': name})
|
||||
ksft_eq(fam['family-name'], name)
|
||||
ksft_true(fam['family-id'] > 0)
|
||||
|
||||
# The format of ops is quite odd, [{$idx: {"id"...}}, {$idx: {"id"...}}]
|
||||
# Discard the indices and re-key by command id.
|
||||
ops_by_id = {v['id']: v for op in fam['ops'] for v in op.values()}
|
||||
ksft_eq(len(ops_by_id), len(fam['ops']))
|
||||
ksft_eq(len(ops_by_id), len(fam['ops']),
|
||||
comment=f"{name} lists a command twice")
|
||||
return ops_by_id
|
||||
|
||||
# All ops should have a policy (either do or dump has one)
|
||||
for op in ops_by_id.values():
|
||||
ksft_in('cmd-cap-haspol', op['flags'],
|
||||
comment=f"op {op['id']} missing haspol")
|
||||
|
||||
def _get_policy_map(ctrl, req):
|
||||
"""
|
||||
The policy map in the Netlink replies looks like this:
|
||||
|
||||
[{'family-id': 16, 'op-policy': {'do': 0, 'dump': 0, 'op-id': 3}},
|
||||
{'family-id': 16, 'op-policy': {'dump': 1, 'op-id': 4}}, ...]
|
||||
|
||||
Return the mapping:
|
||||
|
||||
{3:{'do','dump'}, 4:{'dump'}}
|
||||
|
||||
The policy itself is discarded here, only return which command has policy.
|
||||
"""
|
||||
pol_map = {}
|
||||
for msg in ctrl.getpolicy(req, dump=True):
|
||||
if 'op-policy' not in msg:
|
||||
continue
|
||||
modes = dict(msg['op-policy'])
|
||||
cmd = modes.pop('op-id')
|
||||
ksft_not_in(cmd, pol_map, comment=f"command {cmd} reported twice")
|
||||
pol_map[cmd] = set(modes.keys())
|
||||
return pol_map
|
||||
|
||||
|
||||
def getfamily_do(ctrl) -> None:
|
||||
"""Query single families by name and validate their ops."""
|
||||
ops = {name: _get_ops(ctrl, name) for name in FAMILIES}
|
||||
|
||||
for name, ops_by_id in ops.items():
|
||||
for op in ops_by_id.values():
|
||||
# All ops in nlctrl and netdev have a policy
|
||||
ksft_in('cmd-cap-haspol', op['flags'],
|
||||
comment=f"{name} op {op['id']} missing haspol")
|
||||
ksft_true(op['flags'] & {'cmd-cap-do', 'cmd-cap-dump'},
|
||||
comment=f"{name} op {op['id']} has no handler")
|
||||
|
||||
# nlctrl getfamily (id 3) does both, getpolicy (id 10) is dump-only
|
||||
ksft_in('cmd-cap-do', ops['nlctrl'][3]['flags'])
|
||||
ksft_in('cmd-cap-dump', ops['nlctrl'][3]['flags'])
|
||||
ksft_not_in('cmd-cap-do', ops['nlctrl'][10]['flags'])
|
||||
ksft_in('cmd-cap-dump', ops['nlctrl'][10]['flags'])
|
||||
|
||||
netdev = ops['netdev']
|
||||
|
||||
# dev-get (id 1) should support both do and dump
|
||||
ksft_in('cmd-cap-do', ops_by_id[1]['flags'])
|
||||
ksft_in('cmd-cap-dump', ops_by_id[1]['flags'])
|
||||
ksft_in('cmd-cap-do', netdev[1]['flags'])
|
||||
ksft_in('cmd-cap-dump', netdev[1]['flags'])
|
||||
|
||||
# qstats-get (id 12) is dump-only
|
||||
ksft_not_in('cmd-cap-do', ops_by_id[12]['flags'])
|
||||
ksft_in('cmd-cap-dump', ops_by_id[12]['flags'])
|
||||
ksft_not_in('cmd-cap-do', netdev[12]['flags'])
|
||||
ksft_in('cmd-cap-dump', netdev[12]['flags'])
|
||||
|
||||
# napi-set (id 14) is do-only and requires admin
|
||||
ksft_in('cmd-cap-do', ops_by_id[14]['flags'])
|
||||
ksft_not_in('cmd-cap-dump', ops_by_id[14]['flags'])
|
||||
ksft_in('admin-perm', ops_by_id[14]['flags'])
|
||||
ksft_in('cmd-cap-do', netdev[14]['flags'])
|
||||
ksft_not_in('cmd-cap-dump', netdev[14]['flags'])
|
||||
ksft_in('admin-perm', netdev[14]['flags'])
|
||||
|
||||
# Notification-only commands (dev-add/del/change-ntf etc.) must
|
||||
# not appear in the ops list since they have no do/dump handlers.
|
||||
for ntf_id in [2, 3, 4, 6, 7, 8]:
|
||||
ksft_not_in(ntf_id, ops_by_id,
|
||||
ksft_not_in(ntf_id, netdev,
|
||||
comment=f"ntf-only cmd {ntf_id} should not be in ops")
|
||||
|
||||
|
||||
|
|
@ -103,6 +149,41 @@ def getpolicy_dump(_ctrl) -> None:
|
|||
comment="linkinfo-set should not have a dump policy")
|
||||
|
||||
|
||||
def getpolicy_op_map(ctrl) -> None:
|
||||
"""Check the op-to-policy map consistency. Each op with 'haspol' flag
|
||||
has to have a policy. The policy back-references must name only
|
||||
real ops that exist, have given modes (do vs dump) and have 'haspol'.
|
||||
"""
|
||||
for name in FAMILIES:
|
||||
ops_by_id = _get_ops(ctrl, name)
|
||||
haspol = {cmd for cmd, op in ops_by_id.items()
|
||||
if 'cmd-cap-haspol' in op['flags']}
|
||||
|
||||
pol_map = _get_policy_map(ctrl, {'family-name': name})
|
||||
ksft_eq(set(pol_map), haspol,
|
||||
comment=f"{name} policy map does not match the op list")
|
||||
|
||||
# Walk the op list rather than the map, the map may be missing
|
||||
# the very op we are after. Asking for a command the family does
|
||||
# not have is an error, so it must not come from the map either.
|
||||
for cmd in sorted(haspol):
|
||||
modes = pol_map.get(cmd, set())
|
||||
|
||||
# The kernel only reports a mode the op actually has.
|
||||
if 'do' in modes:
|
||||
ksft_in('cmd-cap-do', ops_by_id[cmd]['flags'],
|
||||
comment=f"{name} cmd {cmd} has no do")
|
||||
if 'dump' in modes:
|
||||
ksft_in('cmd-cap-dump', ops_by_id[cmd]['flags'],
|
||||
comment=f"{name} cmd {cmd} has no dump")
|
||||
|
||||
# Asking for one op builds the map in a different place in
|
||||
# the kernel, it has to report what the full dump did.
|
||||
single = _get_policy_map(ctrl, {'family-name': name, 'op': cmd})
|
||||
ksft_eq(single, {cmd: modes},
|
||||
comment=f"{name} cmd {cmd} policy differs from the dump")
|
||||
|
||||
|
||||
def getpolicy_by_op(_ctrl) -> None:
|
||||
"""Query policy for specific ops, check attr names are resolved."""
|
||||
ndev = NetdevFamily()
|
||||
|
|
@ -122,6 +203,7 @@ def main() -> None:
|
|||
ksft_run([getfamily_do,
|
||||
getfamily_dump,
|
||||
getpolicy_dump,
|
||||
getpolicy_op_map,
|
||||
getpolicy_by_op],
|
||||
args=(ctrl, ))
|
||||
ksft_exit()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user